...
Code Block |
---|
rule hello_world { select when echo hello pre{ name = event:attr("name").defaultsTo(ent:name,"use stored name") } send_directive("say", {"something":"Hello " + name}) } |
Test the persistence by raising an echo/hello
event to hello_world
without providing a value. This In order to send the echo/hello
event without providing a value, you will need to add an entry to your __testing
events
array.
Code Block |
---|
{ "domain": "echo", "type": "hello" } |
Notice that this entry does not include an attribute name, so that when you click on the corresponding button, no value will be provided. This will cause the defaultsTo
operator to provide the persisted name, which will be mentioned in the directive.
You have now created and used a simple entity variable to give your pico persistent state.
Complex Entity Variables
We used an entity variable to store a simple string, but you can also store complex objects in your entity variables. To access deep branches of your object you use a hash path.
Let's change the hello_world
ruleset to know multiple users.
To do this we will need to do the following:
- Change our entity variable to be a map of users.
- Modify
store_name
to store multiple users (one at a time). - Modify
hello_world
to greet from a user id.
Modify the entity variable in store_name
to be a hash of user id's and names.
Start by adding a rule to initialize a structure to contain the map of user names. We'll bind a name clear_name
to a structure with one, default, name. Be sure to put this declaration in the globals section of the ruleset.
Code Block |
---|
global { ... clear_name = { "_0": { "name": { "first": "GlaDOS", "last": "" } } } ... } rule clear_names { select when hello clear always { ent:name := clear_name } } |
If you add the following to the "events" array in the "__testing" structure, then you'll have a UI in the "Testing" tab of your Pico to initialize the entity variable "name".
Code Block |
---|
{ "domain": "hello", "type" : "clear" } |
Validate your code changes, and refresh the "Testing" tab. Click on the "hello/clear" button to trigger the event.
Refresh the "Rulesets" tab to verify that the entity variable has been set to the default name structure.
Now, let's modify the hello_world
rule to obtain the name for the greeting from the structure. All we need to change is the code in the rule prelude.
Code Block |
---|
id = event:attr("id").defaultsTo("_0") first = ent:name{[id,"name","first"]} last = ent:name{[id,"name","last"]} name = first + " " + last |
You'll notice that where this rule used to expect the name as an event attribute, it now expects the id. So, adjust the entry accordingly in the "__testing" object.
Code Block |
---|
{ "domain": "echo", "type": "hello", "attrs": [ "id" ] } |
Validate and install the modified ruleset. Refresh the "Testing" tab and click on the "echo/hello" button. Notice the directive now suggests a greeting to the default name.
Now, we need to modify the store_name
rule to add another name to our entity variable structure.
Code Block |
---|
rule store_name { select when hello name pre{ passed_id = event:attr("id").klog("our passed in id: ") passed_first_name = event:attr("first_name").klog("our passed in first_name: ") passed_last_name = event:attr("last_name").klog("our passed in last_name: ") } send_directive("store_name", { "id" : passed_id, "first_name" : passed_first_name, "last_name" : passed_last_name }) always{ ent:name := ent:name.defaultsTo(clear_name, "initialization was needed"); ent:name := ent:name.put([passed_id,"name","first"], passed_first_name) .put([passed_id,"name","last"], passed_last_name) } } |
Update the entry for this rule in the __testing
structure, to reflect the fact that it now expects three event attributes.
Code Block |
---|
{ "domain": "hello", "type": "name", "attrs": [ "id", "first_name", "last_name" ] } |
Refresh the "Testing" tab and add a new name, filling in the three fields and clicking on the "hello/name" button.
Notice also, in the web server console, these messages:
Code Block |
---|
[KLOG] our passed in id: pjw [KLOG] our passed in first_name: Phil [KLOG] our passed in last_name: Windley [DEBUG] { rid: 'hello_world', event: { eci: 'citdel5gz00012aaoo5ucc613', eid: '5', domain: 'hello', type: 'name' } } fired |
Now use the new "id" value and click the "echo/hello" button. Note the suggested greeting in the directive sent by this rule.
Splendid! You have learned to use complex entity variables.
Querying Persistent Variables
We could use our entity variable directly, but the code will quickly become un-readable.
Let's create some functions to access our entity variable.
To do this we will need to:
- Add a
users
function to retrieve all users - Add a
name
function to retrieve a user from id. - Modify
hello_world
to use the newusers
andname
functions
Create two functions in the global block that returns all users and that returns the user name given an id. You can read more about functions here. A function returns the value of its last expression.
Code Block |
---|
name = function(id){ all_users = users(); nameObj = id => all_users{[id,"name"]} | { "first": "HAL", "last": "9000" }; first = nameObj{"first"}; last = nameObj{"last"}; first + " " + last } users = function(){ ent:name } |
With the functions defined in the global block, we can simplify the prelude of the "hello_world" rule:
Code Block |
---|
pre{ id = event:attr("id").defaultsTo("_0") name = name(id) } |
After saving your changes to the ruleset, refresh the "Testing" tab and verify that all is working as before.
Sweet, you now can add as many users as you want and have your ruleset say hello to each user.
Next Level
We have multiple users our ruleset can recognize with an id, but it would be so much cooler if we knew how many times our ruleset says hello to a user.
Let's make our ruleset smarter.
To do this we will need to use the structure in our entity variable to count visits.
Code Block |
---|
rule hello_world { select when echo hello pre{ id = event:attr("id").defaultsTo("_0") name = name(id) visits = ent:name{[id,"visits"]} } send_directive("say", {"something":"Hello " + name}) fired { ent:name := ent:name.put([id,"visits"], visits + 1) } } |
After adding some users, and sending some "hello_world" events, your entity variable would look something like this:
Congratulations!
You have learned how to use complex entity variables and how your Picos can maintain state.