Persistent Variables in KRL: Threading Sites Together


Kynetx Logo

Yesterday I released build 325 of Kynetx Network Services (KNS) which includes a significant addition to the feature set of Kynetx Rule Language (KRL): persistent variables. Persistent variables allow KRL rulesets to store and react to data over multiple visits. This data isn't personally identifying information, but rather the kind of information that makes writing intelligent Web applications easier. Here's an example: an information box placed on a Web site can now have a "don't show this to me again" check box and act accordingly.

Persistent variables, or just persistents, will ultimately come in two flavors:

  • Entity variables store persistent data about an entity. Think "user, browser, device, etc."
  • Application variables store persistent data about an application, or ruleset. Think "class variable."

In this build we're only supporting entity variables. Application variables will follow later.

All persistent variables are one of three type: flags, counters, or trails. We may add others in the future.

  • Flags store boolean data and can be set or cleared.
  • Counters store integer data and can be initialized, incremented, and decremented.
  • Trails store places in a 20 place stack. You can think of places as URLs, although trails allow you to store anything. Trails can be marked (push a place on the stack) and places can be forgotten (removing them from the trail).

These mutations to the persistent data can occur as a result of rule firing (or not) in the rule's postlude or as the result of user action on the page in the rule's callback specification.

In addition to the mutations of persistents indicated in the preceding description, entity variables can be referenced in KRL expressions and tested in predicates. For example, the following action will fire when the persistent variable ent:archive_pages is greater than 3:

if ent:archive_pages > 3 then
   notify(...)

There are also some special predicates available for persistents. Persistent flags and counters can also be tested with an associated timeframe. For example, the following action will fire when persisent variable ent:archive_pages is greater than 3 and the last time it as set was within the last 2 hours:

if ent:archive_pages > 3 within 2 hours then
   notify(...)
The value to test against and the time value are both KRL expressions. The timeframe (hours in the example) can be seconds, minutes, and so on through years. This reflects our belief that its often useful to know that action has been taken within a certain timeframe.

Persistent trails can be tested using the seen predicate. There are two forms:

  • The first form asks whether a regular expression has been seen within an optional timeframe (specified in the same way as timeframes for counters and flags). So you can say:
    if seen "/archive/2006" in ent:my_trail then 
       notify(...)
    
    or
    if seen "/archive/2006" in ent:my_trail within 3 days then 
       notify(...)
    
    The first argument is a string that is interpreted as a regular expression.
  • The second form tests whether a place in a trail matching the first regular expression comes before or after a place in the same trail matching a second regular expression. This example
    if seen "/archive/2006" 
        before "/archive/2007" in ent:my_trail then 
      notify(...)
    
    would fire the action when a place matching the regular expression "/archive/2006" was placed on the trail before a place matching the regular expression "/archive/2007".

Complete documentation for persistents is available on the Kynetx developer's site.

Here's how you could use persistents to create a notification that has a "don't show this to me again" checkbox:

rule show_note is active {
  select using "www.windley.com/cto_forum" setting ()

  pre {
    h = <<
Welcome to the CTO Breakfast page!!!<br/>
<input id="KOBJ_clear" type="checkbox" 
         name="option1" value="Clear">Don't show again
 >>
  }

  if not ent:dont_show then
    notify("Welcome", h)
        with sticky = true
\t\t\t
  callbacks {
    failure {
      change id="KOBJ_clear" triggers set ent:dont_show
    }
  }
}

This shows a notification as long as the entity persistent ent:dont_show is false (or undefined). It also gives a persistent statement (set ent:dont_show) to be triggered whenever the user changes an element with the ID KOBJ_clear.

We could put another rule in the ruleset to clear the flag:

rule  clear_notice is active {
   select using "www.kynetx.com" setting ()

   notify("Notice", 
        "I'm clearing the notice flag for www.windley.com")

   fired {
      clear ent:dont_show
   }
}

This clears the ent:dont_show flag whenever this rule fires, which it always will as long as the selection statement is matched since the action has no conditional. So in this example, you'd see the notification on the CTO Breakfast page until you checked the box, then you'd never see them again unless you visited the Kynetx web site. If you did, the flag would be cleared and you'd begin getting the notifications again.

This example ruleset is a good example of the abstractive power of KRL and the cross-site capbilities of the KNS system. Clearly you could do something like this with Javascript, PHP, or other systems, but it would take more code and the purpose would not be as clear. Moreover, doing it across multiple Web sites would require the cooperation of those Web sites in some way, whereas with KNS, the client is controlling the cross-site capabilites--and it's all built-in.

In addition to adding persistents, there were a few other changes:

  • The KRL runtime is now based on jQuery 1.3.2. The previous runtime was based on 1.2.6. The complete power of the jQuery system is available to KRL developers who dip below the abstraction layer provided by the language and write their own Javascript.
  • We added a page:url datasource to return information about the calling page's URL such as domain name, path, query string, etc. This could previously be done using the replace operator, but seemed like such a likely use case that we built it in to make it easy.
  • We added a page:param datasource to return information about page parameters (set in the KNS_config object by the endpoint). This data has previously been available via the page:env datasource but that availability will be curtailed in future releases for security reasons.

If any of this looks interesting to you, please signup for a Kynetx developer's account--it's free and come to our developer's conference in November. We'll show you how to easily create context-aware, cross-site Web applications that do things server-side solutions can't even imagine.