如何使库与调用者脚本 PropertiesService 一起工作?

2024-04-16

直到谷歌将导入/导出 API 扩展到容器绑定的 Apps 脚本项目 https://code.google.com/p/google-apps-script-issues/issues/detail?id=2922,我已将项目的大部分内容移至可以使用该 API 的库,然后将 Google Docs 项目放入仅调用该库的 shell 中。

我的问题是让库访问相同的属性(PropertiesService https://developers.google.com/apps-script/reference/properties/)作为 Google 文档项目。由于我的文档附加组件有现有用户,因此我需要继续使用这些属性。

在我的 Google Doc 项目中,我尝试过

$.PropertiesService = PropertiesService;

(where $是我的图书馆)。

它不起作用。图书馆继续使用自己的财产。

然后我尝试了:

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);

这有效。


此时,我想知道:

  1. 为什么前两种方法不行,第三种方法却行呢?

  2. 我可以期望它继续发挥作用吗?

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

文档很少。有this https://developers.google.com/apps-script/guide_libraries,但是PropertiesService没有提到。


资源共享

如你所知,图书馆 https://developers.google.com/apps-script/guides/libraries#resource_scoping有共享和非共享资源。PropertiesService列在下面非共享资源,意味着图书馆拥有its own引用服务时所访问的服务实例在库代码中.

const getStore = () => PropertiesService.getScriptProperties();

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


V8运行时解决方案

V8运行时不创建特殊的上下文 https://developers.google.com/apps-script/guides/v8-runtime/migration#adjust_handling_of_global_this用于您的代码,并让您可以直接访问内置服务。因此,在使用运行时时,可以通过简单地定义或替换全局上的属性来注入服务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!
}

在某些全球范围内thisundefined(我在将库添加到绑定脚本时遇到了这个问题)。在这种情况下,只需定义一个私有全局命名空间(以避免泄漏到调用者脚本):

//in the library;
var Dependencies_ = {
    properties : PropertiesService
};

var use = (service) => {
    if ("getScriptProperties" in service) {
        Dependencies_.properties = service;
    }
};

//in the calling script;
$.use(PropertiesService);

Rhino运行时解决方案

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


问题

  1. 为什么前两种方法不行,第三种方法却行呢?

您的方法的所有问题都归结为:

  1. 资源共享(图书馆有自己的服务实例)
  2. 特殊的隐式上下文(Rhino 中无法从外部访问内置库)

但有一个问题:所有 3 种方法do work按设计。

一、方法一确实有效如果您特别引用PropertiesService on $。这是有道理的,因为库作为名称空间包含在内,其中的成员映射到库中的全局声明。例如:

//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 of the 调用脚本并应用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"
  1. 我可以期望它继续发挥作用吗?

是和不是。是的,因为你的_mock函数行为在所有情况下都表现出预期的行为。没有为什么apply依赖于getScriptProperties没有被实现为箭头函数,其中this覆盖将被忽略。

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

对于 Rhino 运行时 - 不这么认为。对于 V8 - 直接注入服务就足够了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使库与调用者脚本 PropertiesService 一起工作? 的相关文章

随机推荐

  • 为什么 Google 的 OpenID 实现的 ID 会发生变化?

    我正在尝试为 Web 应用程序实现 OpenId 登录 每当新用户通过 OpenId 登录时 我都会在系统上创建一个新用户 并在数据中存储他们的 openid URL 以便下次他们使用该用户登录时 我正在使用 Gmail OpenID 对此
  • 如何为armv6、armv7和i386编译静态库(fat)

    我知道这个问题已经被提出过好几次了 但我的目标与我在网上搜索到的内容略有不同 具体来说 我已经能够为 iPhone 构建静态库 但我能够构建的最终 fat 文件仅包含arm和i386架构 并且我不确定arm指的是 v6还是v7 我无法专门针
  • C++ 中谓词是什么? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 您能举一些例子或主题链接吗 谓词是一
  • Jquery中的onclick函数不起作用

    在product showcase js 文件的帮助下 我将内容加载到 HTML 中 然后使用以下类单击产品链接即内在我正在尝试组织一个单击事件如product description js所示 数据是从 json 文件中获取的 我能够将数
  • 将照片上传到 MVC 4 应用程序

    我正在尝试创建一个控制器来在我的 MVC4 应用程序中上传照片 但我不断收到此错误 输入不是有效的 Base 64 字符串 因为它包含非 Base 64 字符 两个以上的填充字符或填充字符中包含非空白字符 照片控制器 cs public c
  • 使用 php 客户端库 v3 将视频上传到 youtube

    我正在尝试使用客户端库 v3 将视频上传到 youtube v3 库是实验性的 没有太多文档 提供的示例不包括 youtube 我已经使用 oauth 2 0 正确验证了用户身份 当我拥有访问令牌时 我正在尝试使用此代码 if client
  • 如何根据给定名称查找一个类的所有子类?

    我需要一种获取从 Python 基类继承的所有类的工作方法 新式类 即从object 这是 Python 3 中的默认设置 subclasses 返回子类的方法 class Foo object pass class Bar Foo pas
  • Xcode 4模板,创建空组

    我正在尝试创建一个 xcode 4 模板 除了我无法创建空组之外 一切工作正常 我想要这个项目结构 项目名 楷模 控制器 景观 服务
  • 检查字典的任何值是否与条件匹配[重复]

    这个问题在这里已经有答案了 python程序员如何检查字典的任何值是否匹配条件 大于0就我而言 我正在寻找对性能影响最小的最 Pythonic 方式 我的字典 pairs word1 0 word2 0 word3 2000 word4 6
  • 在不改变目标类型的情况下以最高精度序列化浮点数

    我需要反序列化原始二进制数据 BinaryFormatter 然后序列化为 JSON 用于编辑 然后再次将其序列化回二进制 显然 我输在了浮动上 原始浮点值0xF9FF4FC1 大端 大致 12 9999933 四舍五入为0xF6FF4FC
  • 通过代码在LinearLayout中对齐Imageview

    问题很简单 我正在使用代码动态创建一个图像视图 ImageView btnSend new ImageView this 并将其添加到 LinearLayout 中 问题是我想保持右对齐 怎么做 提前致谢 尝试使用 LayoutParams
  • 使用每行的列索引向量提取矩阵元素

    我有一个MxN矩阵 我想要一个列向量v 使用向量s它告诉我对于矩阵中的每一行我将采用哪一列 这是一个例子 Matrix 4 13 93 20 42 31 18 94 64 02 7 44 24 91 15 11 20 43 38 31 21
  • 指数代码,我不理解代码行为

    这是指数计算的代码 var exponent function a n if n 0 return 1 else return a exponent a n 1 console log exponent 5 3 我不明白这一行 return
  • 预订表中仅允许工作时间

    PostgreSql 9 2 保留表定义为 CREATE EXTENSION btree gist CREATE TABLE schedule id serial primary key during tsrange not null EX
  • 如何根据选择条件将节点集存储在变量中

    我有以下 XML 结构
  • 如何让 NSIS 安装并执行临时目录中的文件?

    我正在使用以下 NSIS 脚本 Name My app wrapper Outfile MyAppSetup exe InstallDir TEMP MyApp Install Function onInit SetSilent silen
  • angularJS如何避免重复http请求

    Angular 有没有办法避免重复 http 请求 正如您在上面的代码中看到的 我正在拨打电话以检索产品的详细信息 事实上 这个调用与一个按钮相关联 我会避免重复打电话 如果我点击了详细产品按钮 显然我不需要再次调用我的服务 正确的方法是加
  • 仅检查新添加的文件

    我使用 lint staged 和 husky 在提交之前对所有暂存文件运行 linter 但我的代码库很旧 所以暂时不想对现有 修改后的 文件运行 linter 有没有办法只针对新创建 添加 的文件运行 linter None
  • 如何为 SQL CLR 存储过程提供 sql_variant 参数?

    如何向 SQL CLR 存储过程添加 sql variant 参数 使用 System Object 不起作用 并且我没有看到任何可以使用的属性 Microsoft SqlServer Server SqlProcedure public
  • 如何使库与调用者脚本 PropertiesService 一起工作?

    直到谷歌将导入 导出 API 扩展到容器绑定的 Apps 脚本项目 https code google com p google apps script issues issues detail id 2922 我已将项目的大部分内容移至可