Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

When defined as a module, any of the global definitions may be provided to rulesets that use the module. Rules cannot be shared.

Table of Contents
maxLevel1

Using a Module

A ruleset uses another ruleset as a module with the use module pragma. The syntax is:

Code Block
theme
languagejavascriptjsConfluence
meta {
  ...
  use module <ruleset name>
    [alias <string>]
    [with <var> = <expr> [<var> = <expr>]*]
}

...

For example, if ruleset with RID com.windley.krl.blast provides flip then it can be referenced in a ruleset using com.windley.krl.blast as follows:

Code Block
languagejavascriptthemeConfluencejs
global {
  x = com.windley.krl.blast:flip * 3;
}


Note
title

Code doesn't compile

The above code doesn't compile. The syntax of a ruleset identifier (RID) allows it to contain periods and hyphens. But those are not allowed in identifiers, so the string preceding the colon above is not an identifier. The next section shows how to work around this.

...

The optional alias keyword declares an alias for the ruleset name. So if com.windley.krl.blast ruleset is used as follows:

Code Block
languagejavascriptthemeConfluencejs
use module com.windley.krl.blast alias please

then, the preceding example would look like this:

Code Block
languagejavascriptthemeConfluencejs
global {
  x = please:flip * 3;
}

Note: the following are reserved for the Node engine and should not be used as module aliases:

  • app

  • csv

  • engine

  • ent

  • event

  • ical

  • keys

  • http

  • indy

  • math

  • meta

  • random

  • rss

  • schedule

  • time

  • uri

  • wrangler

Configuration

The optional with clause declares configuration values for the used module. The expressions on the righthand side of the with clause must be evaluable before anything in the ruleset has been evaluated. That means that the righthand side is limited to expressions that use:

  • literals

  • keys

  • event attributes

  • environment values

See below for more information on module configuration.

...

The syntax for provides is

Code Block
languagejavascriptjs
themeConfluence
provides <var> [,<var>]*

The list of provided declaration is given as a comma separated list of names.

...

When a module is used, the with clause allows the module to be configured. This provides for module parameterization. The configuration is declared using the configure pragma in the meta section of the module. The syntax is as follows:

Code Block
theme
languagejavascriptjsConfluence
configure using <var> = <expr> [<var> = <expr>]*

...

Variables declared in the configuration are available as regular variables throughout the module. A typical use of the module configuration is to pass in user keys from a ruleset's keys declaration so that the module can use them.

...

Consider the following ruleset that is intended for use as a module (note the provides and configure pragmas):

Code Block
languagejavascriptthemeConfluencejs
ruleset com.windley.krl.blast {
  meta {
    name "cs_module"
    description <<
For testing modules
System tests depend on this ruleset. 
>>
    configure using c = "Hello"
    provides a, f, g
  }
  global {
    a = 5;
    b = 6; 
    f = function(x){x + b}; 
    g = function(){ent:c} 
  }
}

Suppose that we used this module as follows:

Code Block
theme
languagejavascriptjsConfluence
ruleset foobar {
  meta {
    use module com.windley.krl.blast alias blast
  }
  global {
    x = blast:a + 4;
    y = blast:f(4);
  }
  ...
}

...

The following ruleset defines a module that defines a function and two actions to interact with a data service called StringBin (a simple cloud-based key-value pair storage system). StringBin requires a developer pin.

Code Block
theme
languagejavascriptjsConfluence
ruleset com.windley.krl.stringbin {
  meta {
    name "StringBin Module"
    provide read, write, destroy
    configure using pin = "nopin"
  }
  global {
    write = defaction (k, v) {
      http:post("http://api.stringbin.com/1/write",  params =
                     { "pin": pin,
                       "key": k,
                       "value": v
                     }  
    }
    destroy = defaction (k) {
      http:post("http://api.stringbin.com/1/write", params =
                     { "pin": pin,
                       "key": k,
                       "value": ""
                     }
    }
    read = function(k) {
      http:get("http://api.stringbin.com/1/read", params =
                     { "pin": pin,
                       "key": k
                     }
    }
  }
}

...

We use a module by declaring its use in the meta section, giving any configuration parameters using a with clause. The use module pragma requires a ruleset name and an optional alias (StringBin in this case)

Code Block
languagejavascriptthemeConfluencejs
meta {
  use module com.windley.krl.stringbin alias StringBin 
    with pin = "X9ooUUsrR180MkpxZ2N1M"
}

Later we can use one of the module’s actions in a rule like so:

Code Block
languagejavascriptthemeConfluencejs
rule write_to_stringbin {
  select when web pageview
  StringBin:write("yellow", "mellow")
}

...

Because modules are parameterized and can be aliased, you can use a given module multiple times in a single ruleset with different parameters. For example, suppose we had two StringBins and we wanted to copy a value out of one into the other. First, we declare the module’s use twice with different aliases and configurations:

Code Block
languagejavascriptthemeConfluencejs
meta {
  use module com.windley.krl.stringbin alias BinA 
    with pin = "X9ooUUsrR180MkpxZ2N1M"
  use module com.windley.krl.stringbin alias BinB 
    with pin = "ada98u0ada0kkdad0a0da"
}

...

We can use the aliased modules to write a rule that copies data from BinA into BinB:

Code Block
languagejavascriptthemeConfluencejs
rule copy_stringbin {
  select when web pageview
  pre {
    stuffToCopy = BinA:read("yellow");
  }
  BinB:write("yellow", stuffToCopy);
}

...

Rulesets form closures over the value defined in them. This is true when the ruleset is used as a module as well. All variables are scoped statically, not dynamically, whether in the global variable bindings or inside rules. This applied applies to persistent variables as well. 

This has important implications for how rulesets that are also modules behave.   Here’s a detailed look at what this means. Suppose we defined the following ruleset:

Code Block
languagejavascriptthemeConfluencejs
ruleset foo {
  meta {
    provides get_item
  }
  global { 
    get_item = function (k) {
      ent:elements{k};
    };
  }

  rule add_item {
    select when pds new_data_available
    noop();
    always {
      ent:elements{event:attr("key")} := event:attr("value");
      raise pds event "new_item_added";
    }
  }
}

The provides pragma in the meta section indicates that this ruleset is intended to be used as a module. Note that get_item() references an entity variable (ent:elements). An entity variable with the same name is mutated in the rule add_item. Because this ruleset will function as a closure over the value in  ent:elements,  the value of ent:elements retrieved by get_item() is the same as the one mutates mutated in the rule.

Module Caching

...

In general, as long as the global declarations in your module fall into the following categories, your module will be cachable:

  • function definitions

  • action definitions

  • literals

  • simple variables

  • expressions involving the preceding items

Any expression involving a persistent variable will not be cachable unless the persistent variable is wrapped in a function or action definition. Also, some specific built-in functions that have dynamic values cannot be cached. If your module cannot be cached, it will still work, but may perform poorly. Developers should work to ensure their modules are cachable. 

title
Warning

Don't Try to Fool the Caching Algorithm

You can wrap something that isn't cachable (such as a persistent variable) in a function to make it cachable and then immediately call the function, providing the result from the module (see the examples below). While the system will determine that your module is cachable, the result provided will be stale if the wrapped persistent variable is updated, since that will not invalidate the cache.

Examples

Code Block
languagejavascriptthemeConfluencejs
global {
  // the following declarations are cachable
  a = 5;
  b = x + 5;
  get_item = function (k) {
      ent:elements{k};
    };
 
  // the following declarations are not cachable
  a = ent:elements{"name"};
  b = meta:callingRID();
 
  // the following is cachable, but will lead to stale results
  q = get_item("name") 
 
}

...