Google apps script 如何使库与调用方脚本PropertiesService一起工作?

Google apps script 如何使库与调用方脚本PropertiesService一起工作?,google-apps-script,libraries,add-on,Google Apps Script,Libraries,Add On,在Google之前,我已经将我的大部分项目转移到一个可以使用该API的库中,然后将Google文档项目制作成一个只调用库的shell 我的问题是让库访问与GoogleDoc项目相同的属性()。因为我有我的文档加载项的现有用户,所以我需要继续使用这些属性 在我的谷歌文档项目中,我尝试了 $.PropertiesService = PropertiesService; (其中,$是我的库) 它不起作用。库一直在使用它自己的属性 于是我试着: function _mock(obj) { var

在Google之前,我已经将我的大部分项目转移到一个可以使用该API的库中,然后将Google文档项目制作成一个只调用库的shell

我的问题是让库访问与GoogleDoc项目相同的属性()。因为我有我的文档加载项的现有用户,所以我需要继续使用这些属性

在我的谷歌文档项目中,我尝试了

$.PropertiesService = PropertiesService;
(其中,
$
是我的库)

它不起作用。库一直在使用它自己的属性

于是我试着:

function _mock(obj) {
  var ret = {};
  for(var key in obj) {
    if(typeof obj[key] == 'function') {
      ret[key] = obj[key].bind(obj);
    } else {
      ret[key] = obj[key];
    }
  }
  return ret;
}

$.PropertiesService = _mock(PropertiesService);
仍然不起作用。重试:

function _mock(obj) {
  var ret = {};
  for(var key in obj) {
    if(typeof obj[key] == 'function') {
      ret[key] = (function(val) {
        return function() {
          return val.apply(obj, arguments);
        };
      })(obj[key]);
    } else {
      ret[key] = obj[key];
    }
  }
  return ret;
}

$.PropertiesService = _mock(PropertiesService);
这很有效


在这一点上,我想知道:

  • 为什么前两种方法不起作用,而第三种方法起作用

  • 我能指望它继续工作吗

  • 有没有更好的方法让库访问主脚本的属性


  • 文档很少。有,但是没有提到属性服务。

    资源共享

    如您所知,您拥有共享和非共享的资源<代码>属性服务列在非共享资源下,这意味着库有自己的服务实例,当您在库代码中引用该实例时,可以访问该实例

    如果上面的函数是在库中声明的,那么它将使用库的资源(如果在调用脚本中),即它自己的实例


    V8运行时解决方案

    V8运行时,并允许您直接访问内置服务。因此,在使用运行时时,只需定义或替换全局
    this
    上的属性即可注入服务:

    //in the library;
    var getProperty = ((ctxt) => (key) => {
        var service = ctxt.injectedService;
        var store = service.getScriptProperties();
        return store.getProperty(key);
    })(this);
    
    var setProperty = ((ctxt) => (key, val) => {
        var service = ctxt.injectedService;
        var store = service.getScriptProperties();
        return store.setProperty(key, val);
    })(this);
    
    var inject = ((ctxt) => (service) => ctxt.injectedService = service)(this);
    
    var greet = ((ctxt) => () => {
        var store = ctxt.injectedService.getScriptProperties();
        return store.getProperty("greeting") || "Ola!";
    })(this);
    
    //in the calling script;
    function testSharedResources() {
      PropertiesService.getScriptProperties().setProperty("greeting", "Hello, lib!");
      $.inject(PropertiesService);
      Logger.log($.greet()); //Hello, lib!
      $.setProperty("greeting", "Hello, world!");
      Logger.log($.greet()); //Hello, world!
    }
    
    在某些上下文中,全局
    这个
    将是
    未定义的
    (我在向绑定脚本添加库时遇到了这个问题)。在这种情况下,只需定义一个私有全局命名空间(以避免泄漏到调用方脚本):


    Rhino运行时解决方案

    另一方面,较旧的Rhino运行时创建了一个特殊的隐式上下文。这意味着您无法访问内置服务或全局
    This
    。您唯一的选择是绕过调用库中的服务(您的方法#3非常适合这样做)


    问题

  • 为什么前两种方法不起作用,而第三种方法起作用
  • 您的方法的所有问题归结为:

  • 资源共享(库有自己的服务实例)
  • 特殊隐式上下文(没有对Rhino中内置lib的外部访问)
  • 但有一个陷阱:所有3种方法都能按设计工作

    首先,如果您特别引用
    $
    上的
    属性服务
    ,则方法一确实有效。这是有意义的,因为库作为命名空间包含,其成员映射到库中的全局声明。例如:

    //in the caller script
    PropertiesService.getScriptProperties().setProperty("test", "test");
    $.PropertiesService = PropertiesService;
    
    Logger.log( $.PropertiesService.getScriptProperties().getProperty("test") ); // "test"
    Logger.log( $.getProperty("test") ); // "null"
    
    //in the library
    function getProperty(key) {
      var store = PropertiesService.getScriptProperties();
      return store.getProperty(key);
    }
    
    方法二也有效。如果在库中调用调用调用方脚本中的函数并接收库上下文,则该函数的绑定不会改变这一事实,但如果在调用脚本中直接调用绑定副本,则该函数可以:

    //in the caller script
    PropertiesService.getScriptProperties().setProperty("test", "test");
    var bound = $.PropertiesService.getScriptProperties.bind(PropertiesService); 
    
    var obj = { getScriptProperties : bound };
      
    $.PropertiesService = obj;
    
    Logger.log( bound().getProperty("test") ); // "test"
    Logger.log( $.getProperty("test") ); // "null"
    
    现在,为什么第三种方法是开箱即用的?因为封装函数捕获调用脚本的
    PropertiesService
    并应用
    getScriptProperties
    方法而导致闭包。举例说明:

    //in the caller script
    var appl = { 
      getScriptProperties : (function(val) { 
        return function() { 
          return val.apply(PropertiesService);
        }; 
      })(PropertiesService.getScriptProperties) 
    };
      
    $.PropertiesService = appl;
      
    Logger.log( $.getProperty("test") ); // "test"
    
  • 我能指望它继续工作吗
  • 是和否。是的,因为您的
    \u mock
    函数行为在所有情况下都表现出预期的行为。否,因为
    apply
    依赖于
    getScriptProperties
    未作为箭头函数实现,而
    覆盖将被忽略

  • 有没有更好的方法让库访问主脚本的属性
  • 对于Rhino运行时,不要这样认为。对于V8-直接注入服务就足够了

    //in the caller script
    PropertiesService.getScriptProperties().setProperty("test", "test");
    var bound = $.PropertiesService.getScriptProperties.bind(PropertiesService); 
    
    var obj = { getScriptProperties : bound };
      
    $.PropertiesService = obj;
    
    Logger.log( bound().getProperty("test") ); // "test"
    Logger.log( $.getProperty("test") ); // "null"
    
    //in the caller script
    var appl = { 
      getScriptProperties : (function(val) { 
        return function() { 
          return val.apply(PropertiesService);
        }; 
      })(PropertiesService.getScriptProperties) 
    };
      
    $.PropertiesService = appl;
      
    Logger.log( $.getProperty("test") ); // "test"