官术网_书友最值得收藏!

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.

  1. 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:
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/.

http://kangax.github.io/nfe/.

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.

  1. 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 
    
  2. 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 10Packaging 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

  1. 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 to SimpleMath, without altering the prototype. This means that you don't use the new operator.

  2. 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

  1. 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.

  2. 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 1ServiceNow 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.

主站蜘蛛池模板: 阿勒泰市| 桃园市| 大洼县| 静宁县| 巴林右旗| 清流县| 湘阴县| 雷州市| 乐业县| 平舆县| 五莲县| 鸡东县| 平陆县| 南充市| 乐业县| 泰宁县| 葵青区| 韩城市| 岳阳县| 开封县| 张家川| 哈密市| 郑州市| 郧西县| 永清县| 蒙自县| 钟祥市| 沽源县| 鸡泽县| 高清| 修水县| 牡丹江市| 雅安市| 淅川县| 万州区| 绥棱县| 库伦旗| 兖州市| 丘北县| 中江县| 甘孜县|