Initializing entity variables

Each ruleset installed in a pico can endow it with entity variables, which are specific to that pico and that ruleset. Together, the entity variables of all of the rulesets installed in a pico provide the pico with its persistent state.

Until a value is first assigned to an entity variable in a rule postlude, the value of the variable will be null. There are various techniques which can be used to deal with this issue.

The defaultsTo operator

The defaultsTo operator can provide a default value when an entity variable is used in a read-only context. For example, suppose ent:owners will be a map, but might not have been initialized. This code checks the map for a specific key.

nameExists = function(ownername){ ent:owners.defaultsTo({}) >< ownername }

Before the ent:owners variable has been assigned its value as a map, this ensures that it will behave as a map for the purposes of the membership operator ><.

The first time ent:owners is required to store a value, the defaultsTo operator can be used like this to ensure that it will hold a map, which contains an entry keyed by name with the value new_owner.

rule owner_token{ select when owner token_created pre { name = ... new_owner = ... } fired { ent:owners := ent:owners.defaultsTo({}).put(name, new_owner) } }

Every mention of ent:owners within the ruleset must be operated on by defaultsTo when this technique is used.

Using guard rules

An alternative is to have a specific rule which will be evaluated before any other rules which assigns a value to the entity variable. Reading the entity variable would still be done using defaultsTo in case a query comes in before any rule has been selected for evaluation.

rule guard_owners_map { select when owner token_created if ent:owners.isnull() then noop() fired { ent:owners := {} } }

This rule would be placed in the ruleset before any other ruleset that uses ent:owners. It will be selected in the same cases, and since it appears first it will be evaluated first. This means that ent:owners is guaranteed to be a map when subsequent rules are evaluated. So, the other rule can be simplified to this.

This form is also more efficient, especially for a map with a large number of entries.

Initializing when the ruleset is installed

Another alternative is to initialize all entity variables when the ruleset is installed in the pico, and thus before any part of it can be used.

This is done by a rule in the ruleset which selects on the wrangler:ruleset_installed event. This rule can be placed anywhere convenient within the ruleset, because it will be evaluated as soon as the ruleset is installed, before any other party has a chance to use any part of it, whether in a function or in another rule.

It is very important that the select clause include the clause where event:attr("rids") >< ctx:rid because many rulesets might be installed in a pico during its existence, and we don't want the intialization rule in this ruleset to respond when other rulesets are installed later on. Making sure the rule fires only when the entity variable is not already defined is also good policy.

Copyright Picolabs | Licensed under Creative Commons.