Rules

A rule is declared using the keyword rule followed by the rule name which is an identifier.

After the name, the pattern is status is used to indicate the rule status. Rules can be active or inactive. Inactive rules are ignored by the rules engine. This allows a rule to be taken out of service, but left in the ruleset.

Inside a rule, there are five main parts:

  1. Selector
  2. Prelude (declarations)
  3. Action
  4. Callback declaration
  5. Postlude

A typical rule thus looks like this:

rule book_notfication is active {
   select using "www.amazon.com/gp/product/(\d+)/" setting(isbn)
   pre {
      book_data = datasource:library_search("q="+isbn);
      url = book_data.pick("$..docs[0].url");
      title = book_data.pick("$..docs[0].title");
      msg = <<
This book is available at your local library.
Click here to see: <a id="MLN_link" href="#{url}">#{title}</a>
>>;
   }
   if(book_data.pick("$..numFound") > 0) then {
      notify("Local Library", msg)
        with sticky = true;
   }
 
   callbacks {
     success {
       click
     }
   }
}

The following sections describe each of the major features of a rule.

Rule Selection

Every rule must contain a select statement that specifies the conditions under which it is to be selected for evaluation. Rules are selected based on events raised by the endpoints. KRL includes a powerful event expression language (described below) for specifying event scenarios under which a rule is selected.

The select statement comes in two flavors:

  • select using - this is a legacy form that is used to only specify pageview events in the Web domain.
  • select when - this construct is used to specifying more complext event scenarios or event scenarios that involve events other than "pageview".

Each of these select forms can contain an optional foreach statement that loops the rule body for each member of the array given in the foreach clause.

These are described below.

Select Using

The select using statement indicates that what follows is a page (URL) matching selection. At present every rule must have a select using selector. The syntax for the select using statement is:

select using <regexp> setting (<var>*)
 [foreach <array-expr> setting (<var>)]*

The string is matched against the URL of the calling page. If it matches, then the rule is selected. If the regular expression contains any variable captures (areas within parentheses) then the variables in the setting section of the statement are set in order from the variable captures.

Note: Any valid regular expression can be used in a select statement. Also note, some characters that are meaningful in regular expressions are common in URLs and must be escaped. The most common examples areĀ ? and +. The slash (/) is not special in regular expressions inside a select using statement and should not be escaped.

Also, the regular expression is given as a string, even though KRL has a regexp type because of historical implementation issues. This may change to be a proper regular expression, rather than a string that is interpreted as a regular expression, in the future.

Select When

The select when construct is used to specify complex event scenarios that cross multiple event domains. The syntax is as follows:

select when <event_expr> 
 [foreach <array-expr> setting (<var>)]*

Event expressions come in two forms:

  • primitive events - these specify the primitive event in a given domain.
  • complex events - these combine primitive events using event constructurs such as then or between

Primitive Event Expressionss

The syntax for primitive event expressions is domain dependent. The following event domains are built into KRL and have a custom syntax:

  • web
  • mail

There is also a generalized primitive event syntax.

Web Primitive Events

For the Web event domain, the following primitives are accepts:

  • pageview
    pageview <regexp> [setting (<var>*)]
    

    A primitive pageview event has the same semantics as the select using construct described above. The select using contruct is merely syntactic sugar for the pageview event.

    The string is matched against the URL of the calling page. If it matches, then the rule is selected. If the regular expression contains any variable captures (areas within parentheses) then the variables in the setting section of the statement are set in order from the variable captures.

    Note: Some characters that are meaningful in regular expressions are common in URLs and must be escaped. The most common examples areĀ ? and +. The slash (/) is not special in regular expressions inside a select using statement and should not be escaped.

    Also, the regular expression is given as a string, even though KRL has a regexp type because of historical implementation issues. This may change to be a proper regular expression, rather than a string that is interpreted as a regular expression, in the future.

  • submit

    submit <selector>
    

    A submit event selector uses the given jQuery selector (<selector>) to identify a form on the Web page that will be processed. The form parameters are given as ruleset parameters to the rule and can be accessed using the built-in page:param function.

    The watch action is used to bind submit events to page elements identified by the jQuery selector.

  • change

    change <selector>
    

    A change event selector uses the given jQuery selector (<selector>) to identify a form on the Web page that will be processed. The form parameters are given as ruleset parameters to the rule and can be accessed using the built-in page:param function.

    The watch action is used to bind change events to page elements identified by the jQuery selector.

  • click

    click <selector>
    

    A click event selector uses the given jQuery selector (<selector>) to identify a form on the Web page that will be processed. The form parameters are given as ruleset parameters to the rule and can be accessed using the built-in page:param function.

    The watch action is used to bind click events to page elements identified by the jQuery selector.

Mail Events

More information about mail events will be forthcoming.

Generalize Events

Generalized event selectors are specified as follows

select when <event_domain> <event_type> {<param_name> <regexp>}*
  [setting (<var>*)]

The <event_domain> and <event_type> are required and must match the event domain and type in the API call made by the endpoint. Parameter filters are optional. In a parameter filer the parameter name must match the event parameter name and the regular expression must match the name's associated value. The setting clause is used to bind names to values captured in the regular expressions (in order given). Only event parameters an be tested. (i.e. ruleset parameters which are prefixed by the RID cannot be tested in a primitive event selector).

Complex Event Expressions

Complex event expression combine primitive events to form event scenarios.

  • A before B - event A occured before event B:
    select when   pageview "bar.html" 
           before pageview "/archives/(\d+)/foo.html" setting (year)
  • A then B - event A occured then event B occured with no intervening (salient) events
    select when pageview "bar.html" 
           then pageview "/archives/(\d+)/foo.html" setting (year)
  • A and B - event A occured and event B occured in any order.
    select when pageview "bar.html" 
           and  pageview "/archives/(\d+)/foo.html" setting (year)
  • A or B - event A occured or event B occured.
    select when pageview "bar.html" 
           or   pageview "/archives/(\d+)/foo.html" setting (year)
  • A between(B, C) - event A occured between event B and event C
    select when pageview "mid.html" 
                      between(pageview "firs(.).html" setting(b),
                              pageview "las(.).html" setting(c))
  • A not between(B, C) - event A did not occured between event B and event C
    select when pageview "mid.html" 
                    not between(pageview "firs(.).html" setting(b),
                                pageview "las(.).html" setting(c))

Of course, these can be nested as well. Parentheses specify execution order where precedent is not apparent.

select when (pageview "mid.html" 
                  between(pageview "firs(.).html" setting(b),
                          pageview "las(.).html" setting(c)))
       before pageview "/archives/(\d+)/foo.html" setting (year)

When the event scenario specified by the event expression is met, the rule is scheduled to execution.

Select Foreach

Select statements may have one or more optional foreach clauses that allow looping. The expression following the foreach must return an array, a hash, or a singleton when evaluated. Singletons are treated as one-element arrays.

The syntax of the foreach is

select ...
  [foreach <expr> setting(<var> [, <var>]) ]+

If the expression evaluates to an array or singleton there should be one variable in the setting clause. One each iteration over the array, the variable will be set to the current value of the array.

If the expression evaluates to an hash, there should be two variables in the setting clause. On each iteration over the hash, the first variable will be set to the key of the current hash line and the second variable will be set to the value of the current hash line. Note that the order that the hash lines are iterated over is unpredictable.

The rule body (everything in the rule after the select statement) is executed once for each iteration.

You can nest foreach clauses arbitrarily deep and the expression of an inner foreach may depend on the value of the loop variable of an enclosing foreach.

Note: KNS optimizes the prelude declarations so that they are not executed with each loop unless they depend on the value of the setting variable (directly or indirectly).

Using a foreach allows KRL programmers to create FLWR statements because the prelude allows variables to be declared (i.e. "let"), the premise of the action functions as a "where", and the "result" is the action.

Select Examples

Here are a few examples. In this example, the variables year and month would be set with the digits captured in the regular expression.

select using "/archives/(\d+)/(\d+)/" setting (year,month)

In this example, the question mark is escaped.

select using"http://www.windley.com/cgi-bin/mojo/mojo.cgi\?f=n&l=wecn" setting ()

When you need to use parentheses for grouping inside your regular expression, note that they will capture that portion of the matched URL unless you use non-capturing groups:

select using "/(?:archives|logs)/(\d+)/(\d+)/" setting (year,month)

The ?: inside the first parenthized expression keeps that match from being captured so that year and month are still set correctly.

This shows a foreach loop over an array:

select using "/archives/" setting ()
  foreach [1, 2, 3] setting (x)

You can also given an expression rather than an explicit array.

select using "/archives/" setting ()
  foreach f.pick("$..store") setting (x)

Of course, f would have been defined in the global block and the pick would have to return an array. Note that if it is empty, the rule will not be selected.

This shows a foreach over a hash:

rule test0 is active {
  select when pageview ".*"
     foreach {"a": 1, "b": 2, "c":3} setting(k,v)
     pre {
        msg = "#{k} has the value #{v}";
      }
      alert(msg)
  }
}

This would result in three alerts being fired with the messages "a has the value 1", "b has the value 2", and "c has the value 3". The order would be unpredictable.

Declarations

The rule prelude (pre block of a rule) allows variables to be declared and manipulated from data sources and entity variables. Any valid KRL expression can appear on the right hand side of a declaration. You can also declared variables that contain HTML using an extended literal.

Here's an example with a referer data source and a HTML declaration:

pre {
  keywords = referer:search_terms();
 
  announcement_1 = <<
<div id="kobj_announcement_1">
<p>
This is some text!!! It is cool.  You searched on <strong>#{keywords}</strong>
</p>
</div>
  >>;
 
}

The expressions in the prelude are executed in the KRL engine. The results are stored in variables that are passed as variables inside the JavaScript sent to the browser. The results of any declaration are always available in the browser as a variable with the name on the left-hand side of the declaration.

Note: the equal sign (=) in expressions in the prelude is not an assignment operator, but a declaration. You should not use it to mutate the value of variables already declared. In particular, the results of the following expression are not guaranteed as you would expect:

a = a + 1

Data Sources

Datasource declarations take the following form:

<var> = <source>:<function>(<args>);

There are two kinds of data sources available in KRL:

Entity Variables

Counters can be declared and manipulated to control the action of rules

HTML Declarations

HTML declarations are performed using extended quotes (see Literals). The HTML is contained between double angle brackets (<<...>>)

pre {
   announcement_1 = <<
<div id="kobj_announcement_1">
<p>
This is some text!!! It's cool.  You searched on <strong>#{keywords}</strong>
</p>
</div>
   >>;
}

This declares a single variable named announcement_1 that contains the HTML between the double brackets.

Page IDs

You can use the contents of elements on the page with a specific CSS ID using the page ID delclaration. For example, the following comment will make the contents of the element with the ID itemnum available as the variable item_no.

item_no = page:id("itemnum")

Note that page IDs cannot be used in predicates since the values are not available on the server, but only on the client. They can be used in declarations and as arguments to actions.

Conditions and Actions

The principal part of any rule is a conditional action. The condition is called the "premise."

Premise

In KRL, the premise can be empty. If it is not empty, it is introduced using the if...then construct:

if (nighttime() && outside_state("UT"))
then
  replace("#test","test")

See the discussion on predicate exressions for a complete description of the predicate language and built-in predicates that are available in KRL.

Action

Actions are the heart of KRL. See Actions for a complete description of the actions that are available in KRL.

Actions come in several types.

Simple actions consist of an single action like so:

replace("#test","test")

More than one simple action can be combined into a complex action by enclosing them in curly braces and separating them with semicolons. Complex actions are either an every action block that executes all of it's included simple actions or a choose action block that executes one of the included simple actions at random:

if daytime() then
every {
  replace("#kobj_test1", "/kynetx/newsletter_invite_1.inc");
  replace("#kobj_test2", "/kynetx/newsletter_invite_2.inc")
}
 
if daytime() then
choose {
  replace("#kobj_test", "/kynetx/newsletter_invite_1.inc");
  replace("#kobj_test", "/kynetx/newsletter_invite_2.inc")
}

Using choose in combination with callbacks allows A/B testing of rules through analytics.

The every keyword is optional:

{
  replace("#kobj_test1", "/kynetx/newsletter_invite_1.inc");
  replace("#kobj_test2", "/kynetx/newsletter_invite_2.inc")
}

Complex blocks may not contain complex blocks

Action Names

Any simple action can have a prepended name like so:

first_rule_name =>
    replace("#kobj_test", "/kynetx/newsletter_invite_1.inc")

The name is separated from the action with the => separator. The purpose of the name is to allow the analytics engine to properly report which action was executed in a choose, although names may be used in an every block and the analytics engine will record them.

Action Tags

Any simple action can have tags inside the action configuration like so:

replace("#kobj_test", "/kynetx/newsletter_invite_2.inc")
	   with tags = ["discount", "blue"];

Tags are an array of strings. They are not used anyway inside KRL but are reported back in the analytics.

Action Configuration

All actions can be augmented with the with keyword following the action like so

alert("Hello world!")
  with delay = 3

The right hand side of the configuration can be any valid KRL expression.

Multiple configuration options are separated by and like so

      float("absolute", "top: 10px", "right: 10px",
            "/cgi-bin/weather.cgi?city=" + city + "&tc=" + tc)
        with delay = 0 and
             draggable = true and
             effect = "appear";

The following configuration options are applicable to all actions:

delay

The delay in seconds before the action takes place. Note that this is from when the JavaScript is finished loading on the page, not when the page start to display. Applicable to all actions.

Callbacks

Callbacks allow KRL programmers to record the success or failure of a given rule inside the KNS logging system so that analytics can use the data.

A callback block contains a success specification, a failure specification, or both. These specifications are declarative in nature and are based on knowable user behavior.

Currently the only type of call back declaration is click which records success or failure of a rule based on where the user clicks after the rule has fired. Clicks can be defined for HTML elements on the page by ID or CLASS.

Here is an example of a callback:

callbacks {
  success {
    click id="rssfeed";
    click
  }
 
  failure {
    click id="close_rss"
  }
}

Triggers

Callbacks can trigger persistent variable mutator statements so that actions that users take on a page can be the basis for future rule executions.

Here is an example of a callback:

callbacks {
  success {
    click id="rssfeed" triggers mark ent:my_trail with "rss";
  }
}

When this callback is made because the user clicked on a page element with an CSS ID matching "rssfeed" the string "rss" will be placed on the trail ent:my_trail.

Care should be taken to not rely on variables set in the prelude of the rule since the callback executes later in time than the rule itself depending on user action and thus does not have access to values available when the rule fired. Consequently expressions in mutator statements used in callbacks should be limited to operations on literals, explicit calls to datasources, or other persistent variables.

Postlude

The rule postlude allows action to be taken based on whether or not the rule fired. The most common use is to manage persistent variables. You can also place explicit logging statements in the prelude.

Postludes are evaluated based on the status of the rule: fired, notfired, or always. The fired and notfired constructs take an optional else clause that is executed in the opposite state.

Guard Conditions

Any statement in a postlude can have an optional guard statement. The postlude statement will only fire if the predicate in the guard is true. The syntax is

<postlude_statement> if <predicate_expression>

For example:

fired {
  ent:page_count += 2 from 1 if sunny()
}

Persistent Statements

Persistent variables can be mutated in the postlude.

The following example would increment a persistent counter by 2 if the rule fired:

fired {
  ent:page_count += 2 from 1
}

The following example would clear the counter if the rule fires and increment a persistent counter by 2 otherwise:

fired {
  clear ent:page_count
} else {
  ent:page_count += 2 from 1
}

The following example would clear the counter if the rule doesn't fire and increment a persistent counter by 2 otherwise:

notfired {
  clear ent:page_count
} else {
  ent:page_count += 2 from 1
}

The following example would always clear the counter:

always {
  clear ent:page_count
}

Explicit Logging

The syntax of an explicit logging statement is:

log <expr>

where is an valid KRL expression that results in a string (or something that can be cast as a string such as a number).

The following example would place a string with the value of a variable named query in the log if the rule fired:

fired {
  log "query:"+query
}

The following would only log with an empty query: ired {

 log "Empty query" if(query like "^$")

Control Statements

You can place ruleset control statements in the postlude of a rule.

last

The last stops ruleset execution after the current rule.

This example will stop execution of the ruleset if the rule that this postlude is contained in fires.

fired {
  last
}

This example will stop execution of the ruleset if the rule that this postlude is contained in fires and x is equal to 4.

fired {
  last if(x==4)
}

The following construct ensures that no rules below the current rule will ever be executed:

always {
  last
}

Explicit Events

raise

You can raise events in a postlude which will cause rules that are selected on that event to fire. This provides a facility for having one rule dispatch one or more rules in the current ruleset or a different one. Events raised modify the schedule for the current request so that any rule selected will run in the current request. Rules in the current ruleset are run in the context of the already running ruleset.

The syntax for the raise statement is

raise <event_domain> event <event_type> [for <rid>]
   [with <name> = <expr> [and <name> = <expr>]*]

where the <event_domain> is one of explicit or post.

The semantics is to raise an event of event type <event_type> on the event domain <event_domain>. The optional for clause allows you to specify an RID. The current RID is the default. The modifiers after the optional with will be sent as parameters on the event.

Here are a few examples:

fired {
  raise explicit event foo;
}

fired {
  raise explicit event foo for a16x56;
}

fired {
  raise explicit event foo
    with a = 5
     and b = "Hello World";
}