- Mastering ServiceNow(Second Edition)
- Martin Wood
- 2186字
- 2021-07-08 10:36:40
Script Includes
Business rules are inherently tied to a database action. However, often you want to write code that is available in multiple places, perhaps creating utility functions or even creating your own data structures. Script Includes are designed specifically for this situation. The big advantage, aside from the separation that encourages efficient reuse, is that the code is only accessed and parsed on demand, meaning that your scripts only run when they need to. This process is cached, optimized, and controlled, giving meaningful performance benefits.
When you create a Script Include, you give the record a name. This is very different to the name of a business rule. The name in a Business Rule is purely descriptive, enabling you to find the script more easily. For a Script Include, the Name field must have a value identical to the name of the function or class you are defining, since it is used by the platform to identify the code when you try to call it. This means your Script Include names should contain no spaces and should be in accordance with your convention. Use the Description field to document what the script is doing.
Tip
When deciding upon names for your functions, it is a good idea to use CamelCase
, as is the general convention, but underscore_case
is also acceptable, except for classes. Always use CamelCase
there. However, the most important thing is consistency!
Creating classes
JavaScript is an object-based language, but it doesn't have a very strict class structure like Java. Using a variety of techniques, it allows you to simulate object-orientated behavior, such as inheritance. It is up to the coder to use an appropriate pattern. ServiceNow has a particular convention, which you should follow for ease of upgrading and maintainability.
Note
This section does contain some technical specifics of how ServiceNow has implemented Script Includes, but just following the convention will get you most of the way.
When you create a Script Include, and after you've populated the name field, the platform automatically fills in the script field with the beginnings of a class. If you are familiar with the various JavaScript libraries, you may recognize it as the style that jQuery and the prototype JavaScript framework use. For example, consider the following lines of code:
var MyClass = Class.create(); MyClass.prototype = { initialize: function() { }, type: 'MyClass' }
ServiceNow includes some functionality to structure the definition of a class through the Class
global variable. The create
function returns a function, and you can then specify, through the prototype, the methods you want. This pattern is relatively common. John Resig, as always, has a very helpful article available, which uses the same methodology as ServiceNow. It can be read at http://www.ejohn.org/blog/simple-class-instantiation/.
Coding a class
Let's write some code to see how this works.
- Create a new Script Include by navigating to System Definition > Script Includes and clicking on New. Fill out the fields, and Save.
- Name:
SimpleAdd
- Script:
- Name:
var SimpleAdd = Class.create(); SimpleAdd.prototype = { initialize: function (n) { gs.info('Creating new object'); this.number = (n - 0) || 0; }, increment: function () { this.number++; gs.info(this.number); return this; }, type: 'SimpleAdd' };
The first line creates a variable called SimpleAdd
and executes the Class.create
function. This starts the definition of the object.
The second line is where the prototype is edited in order to make the class actually do something. The prototype consists of two functions: initialize
, which is called when the object is created, and add
.
The initialize
function provides a variable called number
that attaches itself to the object by using the this
keyword. If you pass through a value when creating the object, number
will be set to it; otherwise, it will be zero. The double-bar or
function checks to see whether the little expression in the brackets is false
. If so, it provides a 0
. A little JavaScript trick is used to ensure that number
is indeed a number; by subtracting zero, JavaScript will try and turn n
into a number. It will succeed for numbers as strings (such as '100
') and fail for things it can make no sense of ('foo
').
The end result of this ensures that when initialize
runs, the number
variable is indeed a number, or else, it is set to zero.
Every time you call the increment
function, it increases number
by one and then displays it. The function then returns itself, which is useful for chaining.
Finally, the type
variable at the end is a convention; it allows you to easily identify which object you are dealing with.
Tip
The prototype definition has been accomplished by creating variables for every property. This includes defining the functions as function expressions, meaning increment
is a variable with an anonymous function. The alternative function declaration (function x() {}
) is a named function. Refer to these articles if you are interested in learning more:
http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/.
Once the class has been defined, you can try it by using the following code. Navigate to System Definition > Script - Background. Paste the following code in, and change the scope to be x_hotel
. Then click Run script.
var sa = new SimpleAdd(); sa.increment().increment(); gs.info('Accessing the variable number ' + sa.number)
Together, this shows that the sa
object is created from the SimpleAdd
class, the increment
function is called twice in a chain and how the number
property can be accessed and logged.
Tip
Note that it is generally considered bad form to rely on public variables. In this example, number
is accessed directly. This breaks the idea of encapsulation, which means that anything outside the class shouldn't worry about what is happening on the inside. Predefined functions that expose useful information (getters and setters) should generally be used.
Accessing across scope
Just like tables, Script Includes can be worked with from a different scope - if you allow it, and you use the right syntax.
To access a class from another scope, prefix the name with the scope and a period. This is shown in the API name in the Script Include record. For the SimpleAdd
class, it is x_hotel.SimpleAdd
.
Tip
It's best (and clearer) if you use this syntax all the time, even when you are running in scope. It also makes your code more portable.
You must also specify that this class is public by changing the Accessible from field. Setting it to All application scopes scripts running in different scopes can run this code.
- Run the following (slightly altered) code in Background Scripts in the global scope, without changing the Script Include:
var sa = new x_hotel.SimpleAdd(); sa.increment().increment(); gs.info('Accessing the variable number ' + sa.number)
You should get an error message saying:
Illegal access to private script include SimpleAdd in scope x_hotel being called from scope global
- If you wish, edit the SimpleAdd Script Include, and change the Accessible from field. Note how this allows you to run the code.
Tip
There is also a Protection Policy field that defines if other admins can read the code. This is discussed further in Chapter 10, Packaging with Applications, Update Sets, and Upgrades.
Using classes to store and validate data
One of the judgments needed when designing a more complex ServiceNow application is whether to use a class to represent your data and provide validation and data manipulation or to use the GlideRecord
objects, Business Rules, and other functionalities provided by ServiceNow.
For example, if you want to create a new Room in the Hotel, should you create a Script Include that takes in the parameters, validates it, and then creates the record?
Or should you use Business Rules to check the data once it has been submitted through the UI?
I suggest that in the majority of the situations, using Business Rules provides the most obvious, consistent, and scalable solution. If you decide to start accessing your data via web services, for instance, Business Rules will need little alteration, while a custom class will need to have access to it scripted. We've had to use several tricks in our initialize
function to validate input, something the platform already does well. The overhead in maintaining a complex class is often not needed-it quickly gets frustrating to create getter and setter functions if you add a new field, for instance.
Having an API for scoped apps
The exception to this advice is for scoped apps. Here, if you have data you want to protect, it makes sense to access it only through an API. Use the controls on the Table record to deny other apps access, and instead force them to use a Script Include you create.
Perhaps we want to strictly control how other applications create guest records. The Guest table would be useful for the Theme Park app, but rather than changing the Application Access to allow any and all records to be created from all scopes, we could create a Script Include that has a function to accept data, validate it-perhaps applying security restrictions-and only then commit the record to the database.
Extending classes
As with other object-oriented systems, extending a class allows you to take advantage of its defined functionality while adding to or altering it. Again, ServiceNow has a specific convention to do this easily. Extra functions have been added to the Object
object: extend
, extendsObject
, and clone
.
Let's create a new Script Include through System Definition > Script Includes, and saving the following fields:
- Name:
SimpleSubtract
- Script:
var SimpleSubtract = Class.create();SimpleSubtract.prototype = Object.extendsObject(SimpleAdd, { decrement: function () { this.number--; gs.info(this.number); return this; }, type: 'SimpleSubtract' });
The definition for this extended class begins in a similar way to SimpleAdd
, by calling the class.create
function and then defining its prototype. However, instead of providing the variables directly, it passes them through the extendsObject
function of Object
. This simulates inheritance by cloning the SimpleAdd
class prototype and then injecting all the new properties into the resulting object.
Note
These examples are rather artificial. You don't need to extend a class just to add another function.
This results in all the functions of SimpleAdd
, including initialize
, being part of any SimpleSubtract
object when new
is called:
var ss = new x_hotel.SimpleSubtract(2); ss.decrement().increment();
Taking advantage of utility classes
The classes defined so far have needed to be initialized with the new
operator to perform their job as a data structure. Sometimes, however, this is not appropriate. Code could be packaged up in a utility library, which contains functions without creating a new object. In other programming languages, you can achieve this using static functions or classes. JavaScript doesn't have static functions, but to avoid calling the new
operator, it's possible to define functions directly on the class rather than in the prototype.
Tip
ServiceNow provides several utility classes in the out-of-the-box platform. Try searching for Script Includes that contain Util
in the name. JSUtil
and ArrayUtil
are especially helpful. The code search in Studio is excellent for dicovering the uses of these functions.
Providing utility classes
- To create an example, navigate to System Definition > Script Includes, click New, set the following fields and Save:
- Name:
SimpleMath
- Script:
var SimpleMath = Class.create(); SimpleMath.square = function(x) { return x * x; };
Notice how the
square
function has been attached directly toSimpleMath
, without altering the prototype. This means that you don't use thenew
operator. - Name:
- Go to System Definition > Scripts - Background, and run this in the
x_hotel
scope:
gs.info(x_hotel.SimpleMath.square(10));
Storing functions
Often, you don't need the full structure of a class if you just want to store a single function. There is a lot of overhead code in the SimpleMath
class. Instead, let's create another implementation to simplify it.
Having functions in Script Includes
- Create another new Script Include, available at System Definition > Script Includes:
- Name:
square
- Script:
function square (x) { return x * x; }
Tip
Notice how
square
is written with an initial lowercase letter, since it is a single function.SimpleMath
started with an upper case letter, since it is a class. - Name:
- You can then run this in Background Scripts in the
x_hotel
scope very easily:gs.info(x_hotel.square(10));
Tip
It is much better to group similar functions together in a single utility class. This will also give them a namespace, which means it is less likely there will be collisions or variables with the same name.
Client Callable Script Includes
While we've been making Script Includes, you may have noticed the Client Callable checkbox in the form. If this box remains unticked, then any request to run this code that is initiated from the client will not be served. This can happen in many scenarios, including reference qualifiers, first discussed in Chapter 1, ServiceNow Foundations.
Due to the damage that can be caused by calling code from the client, any such calls are made in a sandbox environment that prevents the modification of the database. For example, any attempt to delete records through GlideRecord
will fail. This is discussed more in Chapter 7 , Exchanging Data – Import Sets, Web Services, and other Integrations. The next chapter discusses GlideAjax, a way of calling Script Includes from the browser.
- Intel Galileo Essentials
- Machine Learning with R Cookbook(Second Edition)
- Dependency Injection in .NET Core 2.0
- Java加密與解密的藝術(shù)
- 程序員修煉之道:通向務(wù)實(shí)的最高境界(第2版)
- Ext JS 4 Web Application Development Cookbook
- Hands-On Functional Programming with TypeScript
- SQL Server從入門到精通(第3版)
- Learning SciPy for Numerical and Scientific Computing(Second Edition)
- Julia高性能科學(xué)計(jì)算(第2版)
- Python深度學(xué)習(xí)原理、算法與案例
- Quantum Computing and Blockchain in Business
- Android Studio開發(fā)實(shí)戰(zhàn):從零基礎(chǔ)到App上線 (移動(dòng)開發(fā)叢書)
- Scratch從入門到精通
- 官方 Scratch 3.0 編程趣味卡:讓孩子們愛上編程(全彩)