Utilities.jsonStringify and object methods - google-apps-script

I haven't found a way yet to properly handle methods in objects when calling Utilities.jsonStringify(). Basically, I cannot use my object after I retrieve it from the CacheService and apply Utilities.jsonParse() to it.
Does anyone have a hint ?
Thanks in advance.
Marc

json does not include functions when stringifying/parsing. You have to use something homebrewn like:
function func2String(obj) {
var res={};
for (x in obj) {
var value=obj[x];
res[x]=(typeof(value)=='function')?value.toString():value;
}
return res;
}
function string2Func (obj) {
var res={};
for (x in obj) {
var value=obj[x];
if(typeof(value)!='string') {
res[x]=value;
}
else {
res[x]=(value.substring(0,9)=='\nfunction')?eval('('+value+')'):value;
}
}
return res;
}
usage:
var obj=string2Func (Utilities.jsonParse(q.diff));
var str=Utilities.jsonStringify(func2String(diff));
Of course the unpacked funcs lost all their closures.

Related

ES6 Classes implement indexer like arrays

I want to implement indexer to get elements from data property with index as JavaScript arrays. I heard about ES6 proxies but I couldn't implement it to my class. Is it possible now or should I wait more to come with ES7.
class Polygon {
constructor() {
this.data = new Set(arguments)
}
[Symbol.iterator](){
return this.data[Symbol.iterator]()
}
add(vertex){
this.data.add(vertex)
}
remove(vertex){
this.data.delete(vertex)
}
get perimeter(){
}
get area(){
}
}
let poly = new Polygon()
let first_vertex = poly[0]
AFAIK there is no proposal for something like "indexing" into arbitrary objects, so yes, you would have to go with proxies.
I couldn't really test this since no environment seems to support both classes and proxies, but in theory, you'd have to return the new proxied object from the constructor. Tested in Chrome v52.
Example:
class Test {
constructor(data) {
let self = this;
this.data = data;
this.foo = 'bar';
return new Proxy(this, {
get(target, prop) {
if (Number(prop) == prop && !(prop in target)) {
return self.data[prop];
}
return target[prop];
}
});
}
}
var test = new Test([1,2,3]);
console.log(test[0]); // should log 1
console.log(test.foo); // should log 'bar'

store and retrieve game state using HTML5 DOM storage and JSON

I am using helper functions to store and retrieve game state using HTML5 DOM storage and the JSON features built into ECMAScript5, my code is:
function saveState(state) {
window.localStorage.setItem("gameState",state);
}
function restoreState() {
var state = window.localStorage.getItem("gameState");
if (state) {
return parse(state);
} else {
return null;
}
}
but anyhow I am not getting desired output, as i am new to JSON its hard to resolve. HELP please !
Try below code:
function saveState(state) {
window.localStorage.setItem("gameState", JSON.stringify(state));
}
function restoreState() {
var state = window.localStorage.getItem("gameState");
if (state) {
return JSON.parse(state);
} else {
return null;
}
}

dynamic objects in if condition

how to make if condition which contains dynamic object? i tried this way, but error
function pass(xxx:String,yyy:String,zzz:String)
{
//trace(xxx,yyy,zzz);
if (this[xxx].hitTestObject(this[yyy])) //an original if (obj1.hitTestObject(obj2))
{
trace("right");
}
else
{
trace("fail");
}
}
"this[]" is not work, TypeError: Error #1010: A term is undefined and has no properties.
"this[]" can work if it is outside "if".
Is there any other way for this problem? Thanks before
You should use getChildByName(), if you are transferring names of the MCs, but check if that name is a direct child of this.
function pass(xxx:String,yyy:String,zzz:String):void {
var x=this.getChildByName(xxx);
if (!x) return;
var y=this.getChildByName(yyy);
if (!y) return; // insert similar for zzz here
if (x.hitTestObject(y)) {
trace("right");
}
else
{
trace("fail");
}
}
Otherwise specify what inputs does thje function have.
Unless you have a specific reason to be supplying the object names as Strings, I suggest changing the argument types to DisplayObject:
function pass(a:DisplayObject, b:DisplayObject):void
{
if(a.hitTestObject(b))
{
trace("right");
}
else
{
trace("fail");
}
}
If you need to use the Strings, just do this:
var obj1:DisplayObject = getChildByName("obj1");
var obj2:DisplayObject= getChildByName("obj2");
pass(obj1, obj2);

How to obtain arguments.callee.caller?

I am trying to find out the name of the function that called my Google Apps Script function, by using arguments.callee.caller as in How do you find out the caller function in JavaScript?, but it seems there's no such property exported. (However, arguments.callee exists.)
How can I get that calling function's name in Google Apps Script?
As a secondary question, why isn't arguments.callee.caller there?
I made this function:
function getCaller()
{
var stack;
var ret = "";
try
{
throw new Error("Whoops!");
}
catch (e)
{
stack = e.stack;
}
finally
{
var matchArr = stack.match(/\(.*\)/g);
if (matchArr.length > 2)
{
tmp = matchArr[2];
ret = tmp.slice(1, tmp.length - 1) + "()";
}
return ret;
}
}
It throws as Error() and then gets the function name from the stack trace.
Try vary the '2' in matchArr[2] when using wrappers.
caller is a non-standard extension to JavaScript (that is, many browsers have it but it's not part of the EcmaScript standard) and not implemented in Apps Script.
I made a function to get the call stack based on jgrotius's answer:
function getCallStack()
{
var returnValue = "";
var framePattern = /\sat (.+?):(\d+) \((.+?)\)/;
try
{
throw new Error('');
}
catch (e)
{
returnValue = e.stack
.split('\n')
.filter(function(frame, index) {
return !frame.isBlank() && index > 0;
})
// at app/lib/debug:21 (getCaller)
.map(function(frame) {
var parts = frame.match(framePattern);
return {
file: parts[1],
line: parseInt(parts[2]),
func: parts[3]
};
});
}
return returnValue;
}
This is my updated version of the other two proposed solutions:
const getStacktrace = () => {
try {
throw new Error('')
} catch (exception) {
// example: at getStacktrace (helper:6:11)
const regex = /\sat (.+?) \((.+?):(\d+):(\d+)\)/
return exception
.stack
.split('\n')
.slice(1, -1)
.filter((frame, index) => {
return frame && index > 0
})
.map((frame) => {
const parts = frame.match(regex)
return {
function: parts[1],
file: parts[2],
line: parseInt(parts[3]),
column: parseInt(parts[4])
}
})
}
}
P.S.: please not that the regex has changed and also we are ignoring the first element of the stacktrace, since it is the getStacktrace function itself.

AS3 arguments

Why do you think the code below does not work?
What would you change/add to make it work?
Any help is appreciated..
function TraceIt(message:String, num:int)
{
trace(message, num);
}
function aa(f:Function, ...args):void
{
bb(f, args);
}
aa(TraceIt, "test", 1);
var func:Function = null;
var argum:Array = null;
function bb(f:Function, ...args):void
{
func = f;
argum = args;
exec();
}
function exec()
{
func.apply(null, argum);
}
I get an ArgumentError (Error #1063):
Argument count mismatch on test_fla::MainTimeline/TraceIt(). Expected 2, got 1.
..so, the passed parameter (argum) fails to provide all passed arguments..
..Please keep the function structure (traffic) intact.. I need a solution using the same functions in the same order.. I have to pass the args to a variable and use them in the exec() method above..
regards
Ok, here is the solution.. after breaking my head : )
function TraceIt(message:String, num:int)
{
trace(message, num);
}
function aa(f:Function=null, ...args):void
{
var newArgs:Array = args as Array;
newArgs.unshift(f);
bb.apply(null, newArgs);
}
aa(TraceIt, "test", 1);
var func:Function = null;
var argum:*;
function bb(f:Function=null, ...args):void
{
func = f;
argum = args as Array;
exec();
}
function exec():void
{
if (func == null) { return; }
func.apply(this, argum);
}
This way, you can pass arguments as variables to a different function and execute them..
Thanks to everyone taking the time to help...
When TraceIt() eventually gets called, it's being called with 1 Array parameter, not a String and int parameters.
You could change TraceIt() to:
function TraceIt(args:Array)
{
trace(args[0], args[1]);
}
Or you could change exec() to:
function exec()
{
func.apply(null, argum[0].toString().split(","));
}
...as it appears when you pass "test", 1, you end up with array whose first value is "test,1". This solution doesn't work beyond the trivial case, though.
Change your bb function to look like this:
function bb(f:Function, args:Array):void
{
func = f;
argum = args;
exec();
}
As you have it now, it accepts a variable number of arguments, but you are passing in an array(of the arguments) from aa.