- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-09-2013 09:24 PM
So according to best practices we are supposed to avoid the use of eval and instead use the GlideEvaluator.evaluateString (formerly Packages.com.glide.script.Evaluator.evaluateString). But how can you pass context to the GlideEvaluator? For eval, the context is always the current context but for GlideEvaluator (as demonstrated in the wiki) the context is always global. This makes it problematic to use GlideEvaluator instead of eval from within script includes. For example:
var NewInclude = Class.create();
NewInclude.prototype = {
initialize: function() {
},
runScript: function(script) {
var msg = 'Hello';
return eval(script);
},
type: "NewInclude"
}
var example = new NewInclude();
gs.print(example.runScript("msg = msg + ' World'"));
In the above example, gs.print will print out 'Hello World'. This is because the context for eval is the same as the context being executed within the Script Include function, thus it has access to the 'msg' variable set up before the eval line.
If you replace eval with the proper GlideEvaluator.evaluateString (or the Package call as appropriate), an error is thrown stating that 'msg' is not defined. Digging into this further, you will discover that the 'this' context executed within the GlideEvaluator is in fact the global context, not the context within the script include function.
Is there a way to pass the correct context into the GlideEvaluator or is it best to just to use eval? Or is there another option?
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-10-2013 11:26 AM
Starting with Calgary, you won't be able to make Packages calls. What about something like below - it allows you to send in a script that references a local variable in the runScript method and that variable is not global:
var NewInclude = Class.create();
NewInclude.prototype = {
initialize: function () {
// do nothing
},
runScript: function (script) {
// Local vars
var hasOwn = Object.prototype.hasOwnProperty,
name,
tmpScript;
// Define variables needed on 'this'
this.msg = "Hello";
this.obj = {
name: "ServiceNow"
};
// Start tmpScript
tmpScript = "(function() {\n";
// Get all non-functions from 'this'
for (name in this) {
if (hasOwn.call(this, name)) {
if (typeof this[name] !== "function") {
tmpScript += " var " + name + " = " + new JSON().encode(this[name]) + ";\n";
}
}
}
// Finish tmpScript
tmpScript += script;
tmpScript += "\n})();";
// This is just to show what it's doing
gs.print("Using script:\n" + tmpScript + "\n");
return GlideEvaluator.evaluateString(tmpScript);
},
type: "NewInclude"
};
// Instantiate NewInclude and call runScript method
var example = new NewInclude();
example.runScript(" msg = msg + ' World';\n gs.print(msg + ' from ' + obj.name + '\\n');");
// Proof of not polluting global context
// NOTE: If 'msg' existed globally, it would retain its existing value
gs.print("typeof msg: " + typeof msg);
Output:
*** Script: Using script:
(function() {
var obj = {"name":"ServiceNow"};
var msg = "Hello";
msg = msg + ' World';
gs.print(msg + ' from ' + obj.name + '\n');
})();
*** Script: Hello World from ServiceNow
*** Script: typeof msg: undefined
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-09-2013 10:12 PM
I found a possible answer for this.
Using Packages.com.glide.script.GlideController instead of the Evaluator allows the use of a evaluateString function but with a caveat. You can also use a function called putGlobal to place a variable in the global context prior to execution of the script. Example:
var NewInclude = Class.create();
NewInclude.prototype = {
initialize: function() {
},
runScript: function(script) {
var msg = 'Hello';
var gc = Packages.com.glide.script.GlideController;
gc.putGlobal('msg', msg);
return gc.evaluateString(script);
},
type: "NewInclude"
}
var example = new NewInclude();
gs.print(example.runScript("msg = msg + ' World'"));
The function yields the same result as eval, but does so by creating a global variable 'msg' with the same contents as the local variable 'msg'. So the script is still evaluated on the global context. This method could be dangerous as it could pollute the global context.
Thoughts?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-10-2013 11:26 AM
Starting with Calgary, you won't be able to make Packages calls. What about something like below - it allows you to send in a script that references a local variable in the runScript method and that variable is not global:
var NewInclude = Class.create();
NewInclude.prototype = {
initialize: function () {
// do nothing
},
runScript: function (script) {
// Local vars
var hasOwn = Object.prototype.hasOwnProperty,
name,
tmpScript;
// Define variables needed on 'this'
this.msg = "Hello";
this.obj = {
name: "ServiceNow"
};
// Start tmpScript
tmpScript = "(function() {\n";
// Get all non-functions from 'this'
for (name in this) {
if (hasOwn.call(this, name)) {
if (typeof this[name] !== "function") {
tmpScript += " var " + name + " = " + new JSON().encode(this[name]) + ";\n";
}
}
}
// Finish tmpScript
tmpScript += script;
tmpScript += "\n})();";
// This is just to show what it's doing
gs.print("Using script:\n" + tmpScript + "\n");
return GlideEvaluator.evaluateString(tmpScript);
},
type: "NewInclude"
};
// Instantiate NewInclude and call runScript method
var example = new NewInclude();
example.runScript(" msg = msg + ' World';\n gs.print(msg + ' from ' + obj.name + '\\n');");
// Proof of not polluting global context
// NOTE: If 'msg' existed globally, it would retain its existing value
gs.print("typeof msg: " + typeof msg);
Output:
*** Script: Using script:
(function() {
var obj = {"name":"ServiceNow"};
var msg = "Hello";
msg = msg + ' World';
gs.print(msg + ' from ' + obj.name + '\n');
})();
*** Script: Hello World from ServiceNow
*** Script: typeof msg: undefined
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎12-10-2013 12:09 PM
So basically, the runScript function is splicing the variables into the GlideEvaluator script string and placing that script within an anonymous function. That way the variables are scoped locally to the anonymous function instead of globally... brilliant!
And yes, in Calgary the Package call is replaced with the GlideEvaluator object. This is great, thank you!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-31-2014 08:37 AM
Hi it is still possible to make "Package calls" after Calgary. I propose this as a more elegant solution:
var evaluateScriptWithGlobals = function(script, globals) {
function convertObjToMap(obj) {
var map = new Packages.java.util.HashMap();
for (var o in obj) {
if (obj.hasOwnProperty(o)) {
map.put(o, obj[o]);
}
}
return map;
}
return GlideEvaluator.evaluateStringWithGlobals(script, convertObjToMap(globals));
}
And then making a call:
var msg = "Hello";
evaluateScriptWithGlobals("msg = msg + ' World!';", {msg: "Goodbye"});
gs.print(msg); // Output: Goodbye World!
Tested succesfully on
Build name: HEAD (Eureka)
Build date: 07-19-2014_0835
Build tag: glide-eureka-04-08-2014__demo2-07-09-2014