- 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
‎11-15-2016 08:05 AM
Update to this: GlideEvaluator does not work in scoped apps. Instead you need to use GlideScopedEvaluator. Unfortunately, it only has the evaluateScript() method (which requires a GlideRecord object and field name) - no evaluateString() method is available as of this writing.
What I did as a workaround is to simply instantiate a GlideRecord (no table reading/writing needed) and populate the script field, then use that.
Example:
var dummy = new GlideRecord('sysauto_script');
dummy.initialize();
dummy.script = "gs.debug('Hello World');"
new GlideScopedEvaluator().evaluateScript(dummy, 'script', '');
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-16-2017 06:33 PM
Hi Gregor,
I think you're right.
What has been deprecated is the heaps of the glideapp, glide and snc sub packages within Packages. The Packages.java apparently is still usable. It is actually called in 41 out of the box script includes in my personal instance:
Build name: Helsinki
Build date: 03-22-2017_2340
Build tag: glide-helsinki-03-16-2016__patch9-hotfix0a-03-17-2017
Thanks,
Michel

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-01-2017 06:44 AM
Thanks to andrew.venables for find this for me... you can just instantiate an object with a script field and run it. It seems that in later releases (J and perhaps I or earlier) you need to actually use a real record. This from Andrew...
My best guess is that the developers inserted a gr.isValidRecord() check before the evaluation, check out the following use cases:
var gse = new GlideScopedEvaluator();
var dummy = new GlideRecord('sysauto_script');
dummy.initialize();
gs.info(dummy.isValidRecord()); // will return false
dummy.script = "Math.PI;"
var res = gse.evaluateScript(dummy, 'script', '');
gs.info(res); // result is null L
var dummy = new GlideRecord('sysauto_script');
dummy.get('3e0c3a350fd483009410fa6ce1050e18');
gs.info(dummy.isValidRecord()); // will return true
dummy.script = "Math.PI;"
var res = gse.evaluateScript(dummy, 'script', '');
gs.info(res); // result is 3.14…
var dummy = new GlideRecord('sysauto_script');
dummy.query();
dummy.next();
gs.info(dummy.isValidRecord()); // will return true
dummy.script = "Math.PI;"
var res = gse.evaluateScript(dummy, 'script', '');
gs.info(res); // result is 3.14…
So the technique just needs a slight modification to first get a real/valid record then it can be modified.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎03-08-2018 09:55 PM
Hi andrew,
Is there any way out, as I want lists refresh automatically? eg. after 30 mins whole bunch of new tickets reflects in list view.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-18-2017 03:10 AM
Here is an updated version that should work now that later versions enforce the requirement of it not being an unsaved record. Credit my colleague: David Sturman
Issue
The issue is that the GlideScopedEvaluator will only execute script which is specified in a GlideRecord object. Previously it has been sufficient to just initialize one in memory and pass it to the .evaluateScript() method without saving the record first. It appears that sometime after Istanbul_Patch2 ServiceNow has implemented a change which prevents this usage. (Whether on purpose or not, I have no idea. I cannot find any documentation for this change.)
Workaround
Retrieve any record with a script field, dynamically set the script value but don't actually update and it will still execute the updated script. For extra protection against any inadvertent updates, I suggest doing this against a record with a 'Read-only' protection policy.
var evaluator = new GlideScopedEvaluator();
var gr = new GlideRecord('sys_script');
gr.addQuery('sys_policy','read'); //additional protection against accidental update
gr.setLimit(1);
gr.query();
if (gr.next()) {
gr.setValue('script','gs.print("This script has executed and my user id is " + gs.getUserID());');
try {
evaluator.evaluateScript(gr, 'script');
}
catch (e) {
gs.error(e);
}
}
else {
throw new Error('No rows were found to dynamically use GlideScopedEvaluator against');
}
I've also thrown some error handling and protection in here. Hopefully that will assist anyone else finding this article in future.
Daniel