Rules

From KynetxDocs

Jump to: navigation, search

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's 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 class="MLN_link" 
     }
   }
}

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

Contents

Rule Selection

A select statement determines whether or not a rule is selected.

The syntax for the select using statement is:

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

Select Using

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

The <regexp> 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.

Select Foreach

Select statements may have one or more optional foreach clauses that allow looping. The expression following the foreach must return an array when evaluated. The rule body (everything in the rule after the select statement) is executed once for each member of the array. On each execution, the selected value in the array is bound to the setting variable in the rule environment.

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.

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 class="announcement">
This is some text!!! It's 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 class="announcement">
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 a colon. The purpose of the name is to allow the analytics to properly report which action was executed in a choose, although names may be used in an every block and the analytics 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.

Actions

KRL supports a number of build-in actions. The purpose of an action is to produce JavaScript that is executed on the browser.

after(<selector>, <html>)

Place the the <html> after (as a sibling of) the element selected by <selector>.

after("#foo", "<p>This is the text</p>")

alert(<message>)

Pop an alert box on the client with the message given in the argument string.

alert("The market is down by " +  change +  "!!! The DOW is at " + 
            current_price +  "! Buy Something!")

append(<selector>, <html>)

Append the <html> to the inside of the element selected by <selector> as the last child.

append("#foo", "<p>This is the text</p>")

before(<selector>, <html>)

Place the the <html> before (as a sibling of) the element selected by <selector>.

before("#foo", "<p>This is the text</p>")

float(<position>, <vertical>, <horizontal>, <url>)

Float a window above the current contents of the browser (z=.9999). This is not a pop-up.

  • <position> is either "absolute" or "relative"
  • <verical> is a colon separated pair (written as a string) where the first part is either top or bottom and the second part is a measurement in CSS format. For example "right: 10px" would instruct window to have an initial position 10 pixels from the top of the viewable area.
  • <horizontal> is a colon separated pair (written as a string) where the first part is either left or right and the second part is a measurement in CSS format. For example "top: 10px" would instruct window to have an initial position 10 pixels from the right of the viewable area.
  • <url> is the place where the window content will be retrieved. Because of restrictions on cross site scripting (XSS) by browsers, if URL is not on the same site as the site hosting the KOBJ tags the content of the URL will be downloaded on the Kynetx engine and inserted.
float("absolute", "top: 10px", "right: 10px",
            "/cgi-bin/weather.cgi?city=" + city + "&tc=" + tc)

The following options are available float:

  • draggable Boolean value stating whether the window is draggable or not.
  • effect Takes the value "appear", "blind", or "slide".

These can be set with a with action modification.

float_html(<position>, <verical>, <horizontal>, <expr>)

<p> Like float but content from evaluating the expression rather than a URL.

let_it_snow()

Makes it..... snow.

let_it_snow();

move_after(<anchor>, <item>)

Move the element with the id given by item just after the element with the id given by anchor

move_after("friend-list", "kristen")

move_to_top(<item>)

Move the element with the id given by item to the top of whatever container it's in.

move_to_top("category")

noop()

This action does nothing. Because rules must have an action, this action is available. It is the default when creating a new rule.

noop()

notify(<header_text>, <message>)

Place a notification over the page. Similar to Growl notification on OS X. The parameters are

  • <header_text> - The header. Empty string results in empty header.
  • <message> - A string representing the message.

The following options are available:

  • <sticky> - (defaults to false) If true, the message will stay until the user dismisses it. If false the message will fade after 3 seconds.
  • <opacity> - (defaults to 0.8) Real number between 0 and 1 that sets opacity of the notification box and anything in it.
  • <position> - (defaults to top-right) A string specifying where to place the notification. Valid values are "top-left", "top-right", "bottom-left", and "bottom-right".
  • <color> - (defaults to #FFF) A string giving the CSS RGB value of the foreground color. Must contain a preceding '#'.
  • <background_color> - (defaults to #222) A string giving the CSS RGB value of the background color. Must contain a preceding '#'.
  • <width> - (defaults to 235px) A string giving the CSS width. Must be a valid width value such as "400px" or "30%".
  • <life> - (defaults to 3000) Length of time to display the notification (in ms). If the <sticky> option is true, this is ignored.

Examples:

notify("Attention!", "This is a test message.  It will fade away in time.");

notify("Attention again!", "This is another test message.  This one is sticky.")
  with sticky = true;

close_notification(<selector>)

Attach a close event for a notification to an element or set of elements identified by the selector string that is the argument.

The elements selected *must* be inside the notification for this to work since it finds the notification window that is a parent of the selected elements to close.

popup(<top>, <left>, <width>, <height>, <url>)

Pop up a window above the current contents of the browser. This is a pop-up. <top> is pixels to position the new window from the top of the current window <left> is pixels to position the window from the left of the current window <width> is width of the new window in pixels <height> is height of the new window in pixels <url> is the place where the window content will be retrieved. Because this is a separate window, the URL can be on any site.

popup(250, 250, 150, 400, "/kynetx/google_ad.inc");

The defaults for other options are no toolbar, no statusbar, no menbar, resizable, and scrollable. Note the I'm seeing a bug in Firefox 2 that doesn't recognize some of these options and the left to right positioning.

prepend(<selector>, <html>)

Prepend the <html> to the inside of the element selected by <selector> as the first child.

prepend("#foo", "<p>This is the text</p>")

redirect(<url>)

Redirect to the given URL.

redirect("http://www.google.com")

replace(<sel>, <url>)

Replace the page element (usually a <div>) with the element selected by <sel> with content given by the URL. Because of restrictions on cross site scripting (XSS) by browsers, if URL is not on the same site as the site hosting the KOBJ tags the content of the URL will be downloaded on the Kynetx engine and inserted.

replace("#kobj_test", "/kynetx/newsletter_invite.inc")

Selectors follow the jQuery selector syntax.

replace_html(<sel>, <expr>)

Replace the page element (usually a <div>) with the id given by <sel> with content given by the result of evaluating the expression (a string, var, extended quote, or string concatenation).

replace("#kobj_test", "<p>Hello World</p>")

Selectors follow the jQuery selector syntax.

The new element is placed in a generated <div> element so that effects can be used. If you don't want a new element, see replace_inner.

replace_inner(<sel>, <expr>)

Replace the contents of the page element with the id given by <sel> with content given by the result of evaluating the expression (a string, var, extended quote, or string concatenation).

replace_inner("#kobj_test", "<p>Hello World</p>")

replace_image_src(<sel>, <url>)

Replace the source of the image tag having ID of <sel> with the given URL. The effect is that the picture changes without flicker on modern browsers if the image tag has height and weight attributes and replacement image has the same size.

replace("#kobj_test", "/images/new_logo.png")

annotate_search_results(<selector_func>)

annotate_search_results is used to add annotations to the search engine result pages (SERP) of Google, Yahoo! and Bing (at present). The annotate_search_results method is detailed in SearchAnnotation

annotae_local_search_results(<selector_func>)

annotate_search_results is used to add annotations to the local search results of Google, Yahoo! and Bing (at present). The annotate_local_search_results method is detailed in SearchAnnotation

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 class="newsletter"
  } 

  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 <expr> 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 "^$")

} </pre>

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
}
Personal tools