I\'ve been trying to get some functions in a Google apps script (inside a spreadsheet) to modify a global variable, but I can\'t seem to figure it out.
Basically I
Despite CacheService would work, it has 6 hours maximum life time. This can be solved by using PropertiesService instead, as @consideRatio mentioned.
Example wrapper might be(injects variables into global context)
/* Wrap variable inside global context */
const Globals = {
global:this,
items:{},
/* Can be 'User', 'Script', or 'Document'
** Script - same values for all executions of this script
** User - same values for all executions by same user
** Document - same values for any user for same document
** Check reference for details.
** https://developers.google.com/apps-script/guides/properties
**
*/
context:'Script',
/* Get property service based on requested context */
get service() {
return PropertiesService['get' + this.context + 'Properties']()
},
/* Assign implementation */
set(name, value = null) {
this.service.setProperty(name, JSON.stringify(value));
return value;
},
/* Read implementation */
get(name) {
var value = this.service.getProperty(name);
return value !== null? JSON.parse(value) : null;
},
/* Shortcut for setter of complex objects */
save(name) {
this.set(name, this.items[name]);
},
/* Save all */
flush(name) {
Object.keys(this.items).map(name => this.save(name));
},
/* Delete implementation */
reset(name) {
this.service.deleteProperty(name);
delete this.items[name];
},
/* Add to global scope */
init(name, default_value = null) {
if(! this.items.hasOwnProperty(name)) {
if(this.service.getProperty(name) === null)
this.set(name, default_value);
this.items[name] = this.get(name);
Object.defineProperty(this.global, name, {
get: () => {return this.items[name]},
set: (value) => {return this.items[name] = this.set(name, value)},
})
}
return this.items[name];
}
}
After registering with Globals.init, variables can be used just like ordinary vars. This works with primitives, however, since watchers aren't supported to complex objects, they have to be flushed on script end or explicitly.
/* In case you need to start over */
function restart_simulations() {
Globals.reset('counter');
Globals.reset('state');
test_run();
}
function test_run() {
/* After running init once, you can use global var as simple variable */
Globals.init('counter', 1); // Required to use "counter" var directly, as simple variable
/* Complex objects are also accepted */
Globals.init('state', { logined: false, items: [] });
/* Using primitives is simple */
Logger.log('Counter was ' + counter);
counter = counter + 1;
Logger.log('Counter is now ' + counter);
/* Let's modify complex object */
Logger.log('State was ' + JSON.stringify(state));
state.items.push(state.logined ? 'foo' : 'bar');
state.logined = ! state.logined;
Logger.log('State is now ' + JSON.stringify(state));
/* Unfortunately, watchers aren't supported. Non-primitives have to be flushed */
/* Either explicitly */
//Globals.save('state');
/* Or all-at-once, e.g. on script end */
Globals.flush();
}
Here's what is preserved among different 3 runs
First run:
[20-10-29 06:13:17:463 EET] Counter was 1
[20-10-29 06:13:17:518 EET] Counter is now 2
[20-10-29 06:13:17:520 EET] State was {"logined":false,"items":[]}
[20-10-29 06:13:17:523 EET] State is now {"logined":true,"items":["bar"]}
Second run:
[20-10-29 06:13:43:162 EET] Counter was 2
[20-10-29 06:13:43:215 EET] Counter is now 3
[20-10-29 06:13:43:217 EET] State was {"logined":true,"items":["bar"]}
[20-10-29 06:13:43:218 EET] State is now {"logined":false,"items":["bar","foo"]}
Third run:
[20-10-29 06:14:22:817 EET] Counter was 3
[20-10-29 06:14:22:951 EET] Counter is now 4
[20-10-29 06:14:22:953 EET] State was {"logined":false,"items":["bar","foo"]}
[20-10-29 06:14:22:956 EET] State is now {"logined":true,"items":["bar","foo","bar"]}
You can check working example here.
Demo script