`act`: proposing writes
act is how a rule asks for a change to product data. A rule never writes: each act call is a
proposal, recorded with the rule and cycle that made it. What happens to proposals is the
caller’s choice:
| Call | Proposals |
|---|---|
POST /rules/execute, POST /rules/run, kis rules | returned as actions; never applied |
POST /rules/evaluate, a sweep | returned as actions; applied only with apply: true |
| a data change a scope reacts to | applied, as the system user |
A proposal is applied only when the static scope’s writes
allow it, the caller may apply the scope, and the source lets the caller write that record. See
Evaluating records.
Methods
Section titled “Methods”| Method | Proposes |
|---|---|
act.Set(field, value) | a field of the record being evaluated: an update of the scope’s over record |
act.Update(source, entity, id, fields) | fields of another record, named by its key |
act.Create(source, entity, fields) | a new record |
act.Upsert(source, entity, keys, fields) | a record created, or updated when one matches on keys, a comma-separated list of its fields |
act.Delete(source, entity, id) | a record deleted |
act.Count() | how many proposals the run has made so far |
source is a source name the scope declares under sources; entity is an entity of that source;
fields is a map, built with map.Of(key, value, …) in the call itself.
Proposing writes
Section titled “Proposing writes”rule Senior "large invoices go to a senior reviewer, with a task" salience 10 { when in.Double("invoice.total", 0.0) > 1000.0 then act.Set("review_tier", "senior"); act.Create("shop", "task", map.Of("id", "t-" + in.Str("invoice.id", ""), "subject", "review " + in.Str("invoice.id", ""))); out.Set("tier", "senior");}
rule Auto "small invoices are approved" salience 5 { when in.Double("invoice.total", 0.0) <= 1000.0 then act.Set("review_tier", "auto"); out.Set("tier", "auto");}
rule Counted "a rule can read what was proposed" salience 1 { when act.Count() > 1 then out.Set("proposed", act.Count());}{ "invoice": { "id": "inv-big", "total": 1500.0 } }{ "proposed": 2, "tier": "senior" }[ { "op": "set", "field": "review_tier", "value": "senior", "rule": "Senior", "cycle": 1 }, { "op": "create", "source": "shop", "entity": "task", "fields": { "id": "t-inv-big", "subject": "review inv-big" }, "rule": "Senior", "cycle": 1 }]out and act are separate: out is the decision the caller reads, actions the writes the
rules want. A later rule sees each proposal as it is made, so act.Count() in a when reads the
current count.
Other records
Section titled “Other records”rule Others "a customer's other records" { when in.Has("customer.id") then act.Update("shop", "customer", in.Str("customer.id", ""), map.Of("segment", "gold")); act.Upsert("shop", "contact", "customer_id,email", map.Of("customer_id", in.Str("customer.id", ""), "email", in.Str("customer.email", ""))); act.Delete("shop", "hold", in.Str("customer.hold", ""));}{}[ { "op": "update", "source": "shop", "entity": "customer", "id": "c1", "fields": { "segment": "gold" }, "rule": "Others", "cycle": 1 }, { "op": "upsert", "source": "shop", "entity": "contact", "keys": ["customer_id", "email"], "fields": { "customer_id": "c1", "email": "[email protected]" }, "rule": "Others", "cycle": 1 }, { "op": "delete", "source": "shop", "entity": "hold", "id": "h-9", "rule": "Others", "cycle": 1 }]What has to hold
Section titled “What has to hold”- Declared. The scope’s
writesmust allow the source, entity, operation and every field. A call whose names are written out is checked when the scope loads, and one thewritesdo not allow fails the load, naming the rule. A name the rule computes is checked when the record’s proposals are planned, dry or applied: the record answers with the errorrules-write-not-declared, and none of its proposals is applied. - A record to set.
act.Setupdates the record being evaluated, so it needs a scope withdata.over. Underexecutethere is no such record: the proposal comes back and nothing applies it. - Versions. An update or delete carries the version of the record it read and conflicts when the
record changed since; the record is decided again once. An entity without versions needs
concurrency: last-write-winson its write. - No-ops are dropped. A field set to the value it already holds is not written, so applying the same decision twice writes nothing the second time.
Continue with
Section titled “Continue with”- Scopes:
sources,data.overandwrites. - Evaluating records: applying proposals and what comes back.
out: writing results.