Skip to content

Creating and Using Ruleset Includes

Updated pdexter 2024-01-30

Often, you may want to create common functions and processes that may be used from different rulesets, but only want to code once. For this, you might create a RulesetInclude.

A RulesetInclude is a document that contains JSON javascript to be included into Rulesets, as if it was written there directly. It is similar in concept to the C/C++ usage of the #include directive.

Editing a RulesetInclude

RulesetIncludes can be created from the template with id /form/94869c359bf4131553761097

RuleSetInclude2

Field Description
Name The key and descriptor of the RulesetInclude
Description A fuller description of the RulesetInclude, if required.
Group A group name used to group RulesetIncludes, such as 'your-company-name' or 'system'
Included By Rulesets This section can be expanded to show the Rulesets that are using this Ruleset Include.
Include Script The actual code you wish to create and share to Rulesets.

Structure of a RulesetInclude script

The content of an include script may seem complicated at first, but basically it should follow the format:

object : {
    object-property-1 : value/object/function,
    object-property-2 : value/object/function,
    ...
}

For example, a very simple include might be

widgetFunctions : {

    version : '2018-09-07-B',

    rollDie : function() {
        var dieValue = Math.floor(Math.random() * 6) + 1;
        return dieValue;
    }

}

As for any JSON object, the different elements (value/object/function) on the same sibling level should be separated by comma.

You can define a number of root level objects, if you wish, providing they are separated by commas.

Creating PreRun Rules in Ruleset Includes

Applies to JayRule (Haughty Hedgehog) & later.

It is possible to write rules directly into Ruleset Includes, and they will be run before the rules of the ruleset itself.

rulePrepareService84088260 : {
    ruleCondition : function(ntf) { ... },
    ruleAction : function(ntf) { 
        ...
    }
}

As for rulesets, the requirement is that :

  • the rulename starts with the string "rule"
  • Suggestion -- be sure to name your rule something quite distinct that is not going to possibly be duplicated in the ruleset itself, otherwise this included rule will be overwritten and never run. Adding a random number of some length is usually sufficient.
  • Because these rules run before any others in the ruleset, ensure these rules are truly independent of any assumptions of the client ruleset.

You can also write other objects as usual within the Ruleset Include (remembering to separate all blocks with a comma), which can be used as normal.

It is theoretically possible to put rules in Ruleset Includes, and then include them within the ruleset : { .... } block in the ruleset script, therefore having them treated in sequence with the others. However this is not general practice, and no guarantees are given with regard to performance and correctness.

Referencing sibling functions in a RulesetInclude

Often we might want for one function within our RulesetInclude to use another function within the same RulesetInclude object.

Generally we can use the keyword this to reference the parent object, and thus utilise that sibling function.

However, this can sometimes change depending on the context we are in at the time, eg, within a callback function. A more robust method is to preset a variable within the function to be this at the beginning, and to use that as reference to the containing object, eg

insectService : {
    funcA : function() {
        return "caterpillar";
    },

    funcB : function() {
        var includeMain = this;

        return "Green " + includeMain.funcA();
    }
}

BEWARE -- this relies on the final usage referencing the function through the RulesetInclude's root level object; if the function has been assigned to a free floating function variable, neither this or the preset variable will work.

// Using the above myIncludeObject
var grubName = ft3.insectService.funcB();    // Okay - will work


var getGrubName = ft3.insectService.funcB;
var grubName = getGrubName();    // XXX This will fail, eg "includeMain.funcA is not a function"

A robust way of preventing, or highlighting this problem, is to include a self check in your RulesetInclude, ala

insectService : {
    name : 'insectService',      // give the main object a name property

    funcA : function() {
        return "caterpillar";
    },

    funcB : function() {
        var includeMain = this;

        // Do a check to make sure this is being called correctly
        if (!(includeMain.name === 'insectService')) {
            throw 'This function needs to be called as a child of "insectService"';
        }

        return "Green " + includeMain.funcA();
    }
}

Including a RulesetInclude in a Ruleset

To use a RulesetInclude in your Ruleset, simply write a #include line at the beginning of your Ruleset script, after the first open bracket ({). Reference the required RulesetInclude by its name, within double quotes.

Eg:

// RuleSet: MyRuleset
// Updated by: ...
{
#include "Widget Functions JS",
...

The Formbird application will take the content of that RulesetInclude, and copy it into the ruleset at that point where #include was called, at runtime.

Using the included RulesetInclude

To use the functions of your RulesetInclude, under a JayRule ruleset, simply reference the main object of your RulesetInclude via ft3 (= ntf.scope). Eg

var ft3 = ntf.scope; 

var widgetFunctionsVersion = ft3.widgetFunctions.version;

var diceRollNumber = ft3.widgetFunctions.rollDie();

If editing pre-Jayrule Rulesets, ie those using a mainRule, you would use the main object, (assuming main has been set to this at the beginning of mainRule ).

var main = this;
...

var widgetFunctionsVersion = main.widgetFunctions.version;

var diceRollNumber = main.widgetFunctions.rollDie();

Alternative Usage

It is possible to omit a containing object altogether when creating a RulesetInclude, and just have the functions defined, eg

// RulesetInclude Script

rollDie : function() {
    var dieValue = Math.floor(Math.random() * 6) + 1;
    return dieValue;
}

The RulesetInclude functions can then be used as direct child functions of ft3, eg

var ft3 = ntf.scope; 

var diceRollNumber = ft3.rollDie();

The disadvantage here is that if for some reason other RulesetIncludes have a function of the same name, or a function is declared directly in the ruleset with the same name, one of the function declarations is going to override the other.