### Setup and First Rule in Durable.js Source: https://github.com/jruizgit/rules/blob/master/docs/js/reference.md Demonstrates how to set up a new Durable.js project, install the library, and define a simple rule that triggers an action based on a posted fact. It includes steps for terminal setup and running the initial JavaScript code. ```javascript var d = require('durable'); d.ruleset('a0', function() { whenAll: m.amount < 100 run: console.log('a0 approved') }); d.post('a0', { amount: 10 }); ``` -------------------------------- ### Setup and First Rule in Ruby Source: https://github.com/jruizgit/rules/blob/master/docs/rb/reference.md Demonstrates how to set up a new project, install the durable_rules gem, and write a basic 'Hello World' rule using Ruby. This involves defining a ruleset and posting an event to trigger the rule. ```ruby require "durable" Durable.ruleset :test do when_all (m.subject == "World") do puts "Hello #{m.subject}" end end Durable.post :test, { :subject => "World"} ``` -------------------------------- ### Durable Rules Timer Example Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Illustrates using timers in Durable Rules to schedule events. It shows how to start a timer, define a timeout condition, and print a message when the timer expires. The example also demonstrates posting an event to initiate the timer. ```Python from durable.lang import * with ruleset('timer'): @when_all(m.subject == 'start') def start(c): c.start_timer('MyTimer', 5) @when_all(timeout('MyTimer')) def timer(c): print('timer timeout') post('timer', { 'subject': 'start' }) ``` -------------------------------- ### String Pattern Matching - Starts With Source: https://github.com/jruizgit/rules/blob/master/docs/json/reference.md This snippet demonstrates case-sensitive string pattern matching using '$mt' to find strings that start with 'hello'. It logs the matching strings. ```javascript { "strings": { "r_0": { "all": [ { "m": { "$mt": { "subject": "hello.*" } } } ], "run": "logStarts" } } } ``` -------------------------------- ### Node.js: Forward Inference Example Source: https://github.com/jruizgit/rules/blob/master/README.md This Node.js example demonstrates forward inference using durable_rules. It defines a ruleset 'animal' with multiple 'whenAll' conditions and 'run' actions to infer facts about animals based on their diet and habitat, ultimately logging the inferred properties. ```javascript var d = require('durable'); d.ruleset('animal', function() { whenAll: { first = m.predicate == 'eats' && m.object == 'flies' m.predicate == 'lives' && m.object == 'water' && m.subject == first.subject } run: assert({ subject: first.subject, predicate: 'is', object: 'frog' }) whenAll: { first = m.predicate == 'eats' && m.object == 'flies' m.predicate == 'lives' && m.object == 'land' && m.subject == first.subject } run: assert({ subject: first.subject, predicate: 'is', object: 'chameleon' }) whenAll: m.predicate == 'eats' && m.object == 'worms' run: assert({ subject: m.subject, predicate: 'is', object: 'bird' }) whenAll: m.predicate == 'is' && m.object == 'frog' run: assert({ subject: m.subject, predicate: 'is', object: 'green' }) whenAll: m.predicate == 'is' && m.object == 'chameleon' run: assert({ subject: m.subject, predicate: 'is', object: 'green' }) whenAll: m.predicate == 'is' && m.object == 'bird' run: assert({ subject: m.subject, predicate: 'is', object: 'black' }) whenAll: +m.subject run: console.log('fact: ' + m.subject + ' ' + m.predicate + ' ' + m.object) }); d.assert('animal', { subject: 'Kermit', predicate: 'eats', object: 'flies'}); d.assert('animal', { subject: 'Kermit', predicate: 'lives', object: 'water'}); d.assert('animal', { subject: 'Greedy', predicate: 'eats', object: 'flies'}); d.assert('animal', { subject: 'Greedy', predicate: 'lives', object: 'land'}); d.assert('animal', { subject: 'Tweety', predicate: 'eats', object: 'worms'}); ``` -------------------------------- ### Choice of Sequences with 'any' and 'all' Source: https://github.com/jruizgit/rules/blob/master/docs/json/reference.md Demonstrates using 'any' to trigger an action if any of the specified sequences match, and 'all' within those sequences to require all conditions to be met. This example logs events based on approval or 'jumbo' status. ```javascript { "expense": { "r_0": { "any": [ { "m_0$all": [ { "first": { "subject": "approve" } }, { "second": { "amount": 1000 } } ] }, { "m_1$all": [ { "third": { "subject": "jumbo" } }, { "fourth": { "amount": 10000 } } ] } ], "run": "log" } } } ``` -------------------------------- ### Durable Rules Flowchart Example Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Demonstrates creating a flowchart with stages, conditions, and actions using Durable Rules. It shows how to define transitions between stages based on message content and amount, and how to post events to trigger the flowchart. ```Python from durable.lang import * with flowchart('expense'): # initial stage 'input' has two conditions with stage('input'): to('request').when_all((m.subject == 'approve') & (m.amount <= 1000)) to('deny').when_all((m.subject == 'approve') & (m.amount > 1000)) # intermediate stage 'request' has an action and three conditions with stage('request'): @run def request(c): print('requesting approve') to('approve').when_all(m.subject == 'approved') to('deny').when_all(m.subject == 'denied') # reflexive condition: if met, returns to the same stage to('request').when_all(m.subject == 'retry') with stage('approve'): @run def approved(c): print('expense approved') with stage('deny'): @run def denied(c): print('expense denied') # events for the default flowchart instance, approved after retry post('expense', { 'subject': 'approve', 'amount': 100 }) post('expense', { 'subject': 'retry' }) post('expense', { 'subject': 'approved' }) # events for the flowchart instance '1', denied after first try post('expense', { 'sid': 1, 'subject': 'approve', 'amount': 100}) post('expense', { 'sid': 1, 'subject': 'denied'}) # event for the flowchart instance '2' immediately denied post('expense', { 'sid': 2, 'subject': 'approve', 'amount': 10000}) ``` -------------------------------- ### Python: Define and Execute a Simple Rule Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Demonstrates how to define a simple rule using `when_all` and execute it by posting a fact. This example shows the basic structure of a rule in Durable Rules, including setting up a ruleset and defining a condition and action. ```python from durable.lang import * with ruleset('test'): @when_all(m.subject == 'World') def say_hello(c): print ('Hello {0}'.format(c.m.subject)) post('test', { 'subject': 'World' }) ``` -------------------------------- ### Pattern Matching with URLs Source: https://github.com/jruizgit/rules/blob/master/docs/rb/reference.md Demonstrates using durable_rules for pattern matching on URLs. It defines a ruleset that triggers when a URL matches a specific pattern, printing the matched URL. Includes examples of posting different URLs to test the rule. ```ruby require "durable" Durable.ruleset :match do when_all (m.url.matches("(https?://)?([0-9a-z.-]+)%.[a-z]{2,6}(/[A-z0-9_.-]+/?)*")) do puts "match -> #{m.url}" end end Durable.post :match, { :url => "https://github.com" } Durable.post :match, { :url => "http://github.com/jruizgit/rul!es" }, -> e, state { puts "match expected:#{e}" } Durable.post :match, { :url => "https://github.com/jruizgit/rules/blob/master/docs/rb/reference.md" } Durable.post :match, { :url => "//rules" }, -> e, state { puts "match expected:#{e}" } Durable.post :match, { :url => "https://github.c/jruizgit/rules" }, -> e, state { puts "match expected:#{e}" } ``` -------------------------------- ### Choice of Event Sequences Source: https://github.com/jruizgit/rules/blob/master/docs/rb/reference.md Shows how to express and evaluate multiple event sequences using durable_rules. The example defines a ruleset that triggers an action if either of two distinct sequences of events (based on subject and amount) occurs. ```ruby require "durable" Durable.ruleset :expense do when_any all(c.first = m.subject == "approve", c.second = m.amount == 1000), all(c.third = m.subject == "jumbo", c.fourth = m.amount == 10000) do if first puts "Approved #{first.subject} #{second.amount}" else puts "Approved #{third.subject} #{fourth.amount}" end end end Durable.post :expense, { :subject => "approve" } Durable.post :expense, { :amount => 1000 } Durable.post :expense, { :subject => "jumbo" } Durable.post :expense, { :amount => 10000 } ``` -------------------------------- ### Manage Timers in Durable Rules Source: https://github.com/jruizgit/rules/blob/master/docs/rb/reference.md Shows how to use timers within Durable rules. This includes starting a timer, triggering actions on timeout, and scheduling events. ```ruby require "durable" Durable.ruleset :timer do when_all m.subject == "start" do start_timer "MyTimer", 5 end when_all timeout("MyTimer") do puts "timer-> timeout" end end Durable.post :timer, { :subject => "start" } ``` -------------------------------- ### Define Statechart Flow Structures in JavaScript Source: https://github.com/jruizgit/rules/blob/master/docs/json/reference.md Illustrates the organization of rules using statecharts, which are deterministic finite automata. This example shows states, transitions, and associated actions for managing an expense approval process. ```javascript { "expense$state": { "input": { "t_0": { "all": [ { "m": { "$and": [ { "subject": "approve" }, { "$gt": { "amount": 1000 } } ] } } ], "to": "denied", "run": "logDenied" }, "t_1": { "all": [ { "m": { "$and": [ { "subject": "approve" }, { "$lte": { "amount": 1000 } } ] } } ], "to": "pending", "run": "logPending" } }, "pending": { "t_0": { "all": [ { "m": { "subject": "approved" } } ], "to": "approved", "run": "logApproved" }, "t_1": { "all": [ { "m": { "subject": "denied" } } ], "to": "denied", "run": "logDenied" } }, "denied": {}, "approved": {} } } ``` -------------------------------- ### Durable Statechart Example Source: https://github.com/jruizgit/rules/blob/master/docs/js/reference.md Defines an 'expense' statechart with 'input', 'pending', 'denied', and 'approved' states. It demonstrates how to define triggers with conditions and actions, and how to post events to the statechart to transition between states. ```javascript var d = require('durable'); d.statechart('expense', function() { // initial state 'input' with two triggers input: { // trigger to move to 'denied' given a condition to: 'denied' whenAll: m.subject == 'approve' && m.amount > 1000 // action executed before state change run: console.log('Denied amount: ' + m.amount) to: 'pending' whenAll: m.subject == 'approve' && m.amount <= 1000 run: console.log('Requesting approve amount: ' + m.amount); } // intermediate state 'pending' with two triggers pending: { to: 'approved' whenAll: m.subject == 'approved' run: console.log('Expense approved') to: 'denied' whenAll: m.subject == 'denied' run: console.log('Expense denied') } // 'denied' and 'approved' are final states denied: {} approved: {} }); d.post('expense', { subject: 'approve', amount: 100 }); d.post('expense', { subject: 'approved' }); // events directed to statechart instance with id '1' d.post('expense', { sid: 1, subject: 'approve', amount: 100 }); d.post('expense', { sid: 1, subject: 'denied' }); // events directed to statechart instance with id '2' d.post('expense', { sid: 2, subject: 'approve', amount: 10000 }); ``` -------------------------------- ### Load Ruleset Source: https://github.com/jruizgit/rules/blob/master/testjs/testdynamic.html Loads a ruleset from the server. It takes the ruleset name from an input field, makes a GET request to the server to fetch the ruleset definition, and then populates a textarea with the formatted JSON. It also updates an iframe with a sample admin page. ```JavaScript function load() { var rulesetName = document.getElementById('name').value; d3.json('http://localhost:5000/' + rulesetName + '/definition', function(err, result) { if (err) { alert('Server error ' + err.responseText); } else { d3.select('iframe') .attr('src', '/' + rulesetName + '/sample/admin.html?disableScratchpad=true&size=350'); d3.select('textarea[id="rulesetpad"]') .text(JSON.stringify(result, null, 4)); } }); } ``` -------------------------------- ### Load Ruleset Source: https://github.com/jruizgit/rules/blob/master/testpy/testdynamic.html Loads a ruleset from the server. It takes the ruleset name from an input field, makes a GET request to the server to fetch the ruleset definition, and then populates a textarea with the formatted JSON. It also updates an iframe with a sample admin page. ```JavaScript function load() { var rulesetName = document.getElementById('name').value; d3.json('http://localhost:5000/' + rulesetName + '/definition', function(err, result) { if (err) { alert('Server error ' + err.responseText); } else { d3.select('iframe') .attr('src', '/' + rulesetName + '/sample/admin.html?disableScratchpad=true&size=350'); d3.select('textarea[id="rulesetpad"]') .text(JSON.stringify(result, null, 4)); } }); } ``` -------------------------------- ### Implement Statecharts for Flow Control in Python Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Illustrates the use of statecharts to organize rules in Durable Functions, creating deterministic finite automata. The example shows defining states, transitions, and actions using Python and the `durable.lang` library, managing expense approval workflows. ```python from durable.lang import * with statechart('expense'): # initial state 'input' with two triggers with state('input'): # trigger to move to 'denied' given a condition @to('denied') @when_all((m.subject == 'approve') & (m.amount > 1000)) # action executed before state change def denied(c): print ('denied amount {0}'.format(c.m.amount)) @to('pending') @when_all((m.subject == 'approve') & (m.amount <= 1000)) def request(c): print ('requesting approve amount {0}'.format(c.m.amount)) # intermediate state 'pending' with two triggers with state('pending'): @to('approved') @when_all(m.subject == 'approved') def approved(c): print ('expense approved') @to('denied') @when_all(m.subject == 'denied') def denied(c): print ('expense denied') # 'denied' and 'approved' are final states state('denied') state('approved') # events directed to default statechart instance post('expense', { 'subject': 'approve', 'amount': 100 }) post('expense', { 'subject': 'approved' }) # events directed to statechart instance with id '1' post('expense', { 'sid': 1, 'subject': 'approve', 'amount': 100 }) post('expense', { 'sid': 1, 'subject': 'denied' }) # events directed to statechart instance with id '2' post('expense', { 'sid': 2, 'subject': 'approve', 'amount': 10000 }) ``` -------------------------------- ### String Pattern Matching - Contains (Case Insensitive) Source: https://github.com/jruizgit/rules/blob/master/docs/json/reference.md This example demonstrates case-insensitive string matching using '$imt' to find strings that contain 'hello' anywhere within them. It logs the matching strings. ```javascript { "strings": { "r_2": { "all": [ { "m": { "$imt": { "subject": ".*hello.*" } } } ], "run": "logContains" } } } ``` -------------------------------- ### Manage State Transitions in a Flow Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md This example demonstrates state management in Durable Rules. Rules are defined to transition the state based on conditions using `s.status`. The `update_state` function initializes the state, and `c.delete_state()` removes it upon completion. ```python from durable.lang import * with ruleset('flow'): # state condition uses 's' @when_all(s.status == 'start') def start(c): # state update on 's' c.s.status = 'next' print('start') @when_all(s.status == 'next') def next(c): c.s.status = 'last' print('next') @when_all(s.status == 'last') def last(c): c.s.status = 'end' print('last') # deletes state at the end c.delete_state() update_state('flow', { 'status': 'start' }) ``` -------------------------------- ### Python: Forward Inference with Multiple Rules Source: https://github.com/jruizgit/rules/blob/master/docs/py/README.txt Illustrates durable_rules' forward-chaining capabilities by defining multiple rules that infer facts based on initial assertions. This example demonstrates how to assert facts and how the rules chain to derive new conclusions, such as identifying animal types and colors. ```Python from durable.lang import * with ruleset('animal'): @when_all(c.first << (m.predicate == 'eats') & (m.object == 'flies'), (m.predicate == 'lives') & (m.object == 'water') & (m.subject == c.first.subject)) def frog(c): c.assert_fact({ 'subject': c.first.subject, 'predicate': 'is', 'object': 'frog' }) @when_all(c.first << (m.predicate == 'eats') & (m.object == 'flies'), (m.predicate == 'lives') & (m.object == 'land') & (m.subject == c.first.subject)) def chameleon(c): c.assert_fact({ 'subject': c.first.subject, 'predicate': 'is', 'object': 'chameleon' }) @when_all((m.predicate == 'eats') & (m.object == 'worms')) def bird(c): c.assert_fact({ 'subject': c.m.subject, 'predicate': 'is', 'object': 'bird' }) @when_all((m.predicate == 'is') & (m.object == 'frog')) def green(c): c.assert_fact({ 'subject': c.m.subject, 'predicate': 'is', 'object': 'green' }) @when_all((m.predicate == 'is') & (m.object == 'chameleon')) def grey(c): c.assert_fact({ 'subject': c.m.subject, 'predicate': 'is', 'object': 'grey' }) @when_all((m.predicate == 'is') & (m.object == 'bird')) def black(c): c.assert_fact({ 'subject': c.m.subject, 'predicate': 'is', 'object': 'black' }) @when_all(+m.subject) def output(c): print('Fact: {0} {1} {2}'.format(c.m.subject, c.m.predicate, c.m.object)) assert_fact('animal', { 'subject': 'Kermit', 'predicate': 'eats', 'object': 'flies' }) assert_fact('animal', { 'subject': 'Kermit', 'predicate': 'lives', 'object': 'water' }) assert_fact('animal', { 'subject': 'Greedy', 'predicate': 'eats', 'object': 'flies' }) assert_fact('animal', { 'subject': 'Greedy', 'predicate': 'lives', 'object': 'land' }) assert_fact('animal', { 'subject': 'Tweety', 'predicate': 'eats', 'object': 'worms' }) ``` -------------------------------- ### String Operations with Pattern Matching Source: https://github.com/jruizgit/rules/blob/master/docs/rb/reference.md Illustrates using durable_rules for common string operations, including case-sensitive and case-insensitive pattern matching. It defines rules to identify strings starting with, ending with, or containing specific substrings. ```ruby require "durable" Durable.ruleset :strings do when_all m.subject.matches("hello.*") do puts "string starts with hello: #{m.subject}" end when_all m.subject.matches(".*hello") do puts "string ends with hello: #{m.subject}" end when_all m.subject.imatches(".*Hello.*") do puts "string contains hello (case insensitive): #{m.subject}" end end Durable.assert :strings, { :subject => "HELLO world" } Durable.assert :strings, { :subject => "world hello" } Durable.assert :strings, { :subject => "hello hi" } Durable.assert :strings, { :subject => "has Hello string" } Durable.assert :strings, { :subject => "does not match" } ``` -------------------------------- ### Load Ruleset Definition Source: https://github.com/jruizgit/rules/blob/master/testrb/testdynamic.html This function loads a ruleset definition from the server. It retrieves the ruleset name from the 'name' element and then fetches the ruleset definition from the server using a GET request. It uses D3.js for making the HTTP request and updating the iframe and textarea. ```JavaScript function load() { var rulesetName = document.getElementById('name').value; d3.json('http://localhost:4567/' + rulesetName + '/definition', function(err, result) { if (err) { alert('Server error ' + err.responseText); } else { d3.select('iframe') .attr('src', '/' + rulesetName + '/admin.html?disableScratchpad=true&size=330'); d3.select('textarea[id="rulesetpad"]') .text(JSON.stringify(result, null, 4)); } }); } ``` -------------------------------- ### Python: Asynchronous Actions with Callbacks Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Shows how to implement asynchronous consequent actions in Durable Rules. Actions can signal completion using a `complete` callback. The example demonstrates managing state transitions ('first' to 'second' to 'third') with timed callbacks and handling errors by passing an exception to `complete`. ```python from durable.lang import * import threading with ruleset('flow'): timer = None def start_timer(time, callback): timer = threading.Timer(time, callback) timer.daemon = True timer.start() @when_all(s.state == 'first') # async actions take a callback argument to signal completion def first(c, complete): def end_first(): c.s.state = 'second' print('first completed') # completes the action after 3 seconds complete(None) start_timer(3, end_first) @when_all(s.state == 'second') def second(c, complete): def end_second(): c.s.state = 'third' print('second completed') # completes the action after 6 seconds # use the first argument to signal an error complete(Exception('error detected')) start_timer(6, end_second) # overrides the 5 second default abandon timeout return 10 update_state('flow', { 'state': 'first' }) ``` -------------------------------- ### Fact Assertion and Rule Chaining in Ruby Source: https://github.com/jruizgit/rules/blob/master/docs/rb/reference.md Shows how to assert facts and define rules that chain together based on asserted facts. This example demonstrates using `assert` to add new facts and `when_all` with conditions on predicates and objects to trigger subsequent rules, including a rule that prints fact details. ```ruby require "durable" Durable.ruleset :animal do # will be triggered by 'Kermit eats flies' when_all c.first = (m.predicate == "eats") & (m.object == "flies") do assert :subject => first.subject, :predicate => "is", :object => "frog" end when_all (m.predicate == "eats") & (m.object == "worms") do assert :subject => m.subject, :predicate => "is", :object => "bird" end # will be chained after asserting 'Kermit is frog' when_all (m.predicate == "is") & (m.object == "frog") do assert :subject => m.subject, :predicate => "is", :object => "green" end when_all (m.predicate == "is") & (m.object == "bird") do assert :subject => m.subject, :predicate => "is", :object => "black" end when_all +m.subject do puts "fact: #{m.subject} #{m.predicate} #{m.object}" end end Durable.assert :animal, { :subject => "Kermit", :predicate => "eats", :object => "flies" } ``` -------------------------------- ### Python: Control Consequent Order with Salience Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Illustrates how to control the execution order of consequents using the `pri` (salience) function. Rules with lower salience values are executed first. This example defines three rules with different priorities based on the 'amount' fact. ```python from durable.lang import * with ruleset('attributes'): @when_all(pri(3), m.amount < 300) def first_detect(c): print('attributes P3 ->{0}'.format(c.m.amount)) @when_all(pri(2), m.amount < 200) def second_detect(c): print('attributes P2 ->{0}'.format(c.m.amount)) @when_all(pri(1), m.amount < 100) def third_detect(c): print('attributes P1 ->{0}'.format(c.m.amount)) assert_fact('attributes', { 'amount': 50 }) assert_fact('attributes', { 'amount': 150 }) assert_fact('attributes', { 'amount': 250 }) ``` -------------------------------- ### Fact Assertion and Chaining Rules in Durable.js Source: https://github.com/jruizgit/rules/blob/master/docs/js/reference.md Explains how to use facts to represent data and how rules can be chained together by asserting new facts. This example demonstrates defining multiple rules that react to specific fact patterns and assert new facts, leading to a sequence of actions. ```javascript var d = require('durable'); d.ruleset('animal', function() { // will be triggered by 'Kermit eats flies' whenAll: m.predicate == 'eats' && m.object == 'flies' run: assert({ subject: m.subject, predicate: 'is', object: 'frog' }) whenAll: m.predicate == 'eats' && m.object == 'worms' run: assert({ subject: m.subject, predicate: 'is', object: 'bird' }) // will be chained after asserting 'Kermit is frog' whenAll: m.predicate == 'is' && m.object == 'frog' run: assert({ subject: m.subject, predicate: 'is', object: 'green'}) whenAll: m.predicate == 'is' && m.object == 'bird' run: assert({ subject: m.subject, predicate: 'is', object: 'black'}) whenAll: +m.subject run: console.log('fact: ' + m.subject + ' ' + m.predicate + ' ' + m.object) }); d.assert('animal', { subject: 'Kermit', predicate: 'eats', object: 'flies' }); ``` -------------------------------- ### Python: String Pattern Matching for Credit Cards Source: https://github.com/jruizgit/rules/blob/master/docs/py/README.txt Demonstrates durable_rules' ability to perform string pattern matching using regular expressions. This example shows how to define rules to detect American Express, Visa, and Mastercard numbers based on their formats. ```Python from durable.lang import * with ruleset('test'): @when_all(m.subject.matches('3[47][0-9]{13}')) def amex(c): print ('Amex detected {0}'.format(c.m.subject)) @when_all(m.subject.matches('4[0-9]{12}([0-9]{3})?')) def visa(c): print ('Visa detected {0}'.format(c.m.subject)) @when_all(m.subject.matches('(5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|2720)[0-9]{12}')) def mastercard(c): print ('Mastercard detected {0}'.format(c.m.subject)) assert_fact('test', { 'subject': '375678956789765' }) ``` -------------------------------- ### Python: Batch Actions with Count and Cap Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Demonstrates batching of consequent actions based on rule satisfaction. The `count` function triggers an action after a specific number of events match, while the `cap` function limits the number of times an action is triggered. This example shows approving expenses based on count and rejecting based on cap. ```python from durable.lang import * with ruleset('expense'): # this rule will trigger as soon as three events match the condition @when_all(count(3), m.amount < 100) def approve(c): print('approved {0}'.format(c.m)) # this rule will be triggered when 'expense' is asserted batching at most two results @when_all(cap(2), c.expense << m.amount >= 100, c.approval << m.review == True) def reject(c): print('rejected {0}'.format(c.m)) post_batch('expense', [{ 'amount': 10 }, { 'amount': 20 }, { 'amount': 100 }, { 'amount': 30 }, { 'amount': 200 }, { 'amount': 400 }]) assert_fact('expense', { 'review': True }) ``` -------------------------------- ### Durable Rules: Trigger actions with 'all' and 'any' event sequences Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Demonstrates how to use the 'all' and 'any' functions in Durable Rules to define event sequences that trigger actions. 'all' requires all conditions to match, while 'any' triggers on any single match. This example shows conditional printing based on which sequence matched. ```Python from durable.lang import * with ruleset('expense'): @when_any(all(c.first << m.subject == 'approve', c.second << m.amount == 1000), all(c.third << m.subject == 'jumbo', c.fourth << m.amount == 10000)) def action(c): if c.first: print ('Approved {0} {1}'.format(c.first.subject, c.second.amount)) else: print ('Approved {0} {1}'.format(c.third.subject, c.fourth.amount)) post('expense', { 'subject': 'approve' }) post('expense', { 'amount': 1000 }) post('expense', { 'subject': 'jumbo' }) post('expense', { 'amount': 10000 }) ``` -------------------------------- ### Manage State Transitions in a Workflow Source: https://github.com/jruizgit/rules/blob/master/docs/js/reference.md This example illustrates managing state transitions within a workflow. It defines a ruleset that updates a state object (`s`) based on its current value, progressing through 'start', 'next', 'last', and finally 'end', deleting the state upon completion. ```javascript var d = require('durable'); d.ruleset('flow', function() { // state condition uses 's' whenAll: s.status == 'start' run: { // state update on 's' s.status = 'next'; console.log('start'); } whenAll: s.status == 'next' run: { s.status = 'last'; console.log('next'); } whenAll: s.status == 'last' run: { s.status = 'end'; console.log('last'); // deletes state at the end deleteState(); } }); // modifies context state d.updateState('flow', { status: 'start' }); ``` -------------------------------- ### Python: Define and Post Simple Rule Source: https://github.com/jruizgit/rules/blob/master/docs/py/README.txt Demonstrates how to define a simple rule using 'when_all' to match an event and execute a print statement. It then shows how to post an event to trigger the rule. ```Python from durable.lang import * with ruleset('test'): # antecedent @when_all(m.subject == 'World') def say_hello(c): # consequent print ('Hello {0}'.format(c.m.subject)) post('test', { 'subject': 'World' }) ``` -------------------------------- ### Durable Rules Manual Reset Timer for Velocity Measurement Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Demonstrates using a manual reset timer in a statechart to measure event velocity. It includes logic to start, reset, and restart the timer, and defines conditions for handling events within a time window and when no events occur. The example posts a series of events to test the velocity measurement. ```Python from durable.lang import * with statechart('risk'): with state('start'): @to('meter') def start(c): c.start_timer('VelocityTimer', 5, True) with state('meter'): @to('meter') @when_all(cap(5), m.amount > 100, timeout('VelocityTimer')) def some_events(c): print('velocity: {0} in 5 seconds'.format(len(c.m))) # resets and restarts the manual reset timer c.reset_timer('VelocityTimer') c.start_timer('VelocityTimer', 5, True) @to('meter') @when_all(pri(1), timeout('VelocityTimer')) def no_events(c): print('velocity: no events in 5 seconds') c.reset_timer('VelocityTimer') c.start_timer('VelocityTimer', 5, True) post('risk', { 'amount': 200 }) post('risk', { 'amount': 300 }) post('risk', { 'amount': 50 }) post('risk', { 'amount': 500 }) post('risk', { 'amount': 600 }) ``` -------------------------------- ### Python: Compare Facts/Events in Rules Source: https://github.com/jruizgit/rules/blob/master/docs/py/reference.md Demonstrates comparing properties of the same event or correlated events within a ruleset. The first example compares debit and credit within a single event, evaluated client-side. The second example compares two correlated events, evaluated server-side, using aliases for clarity. ```python from durable.lang import * with ruleset('risk'): # compares properties in the same event, this expression is evaluated in the client @when_all(m.debit > m.credit * 2) def fraud_1(c): print('debit {0} more than twice the credit {1}'.format(c.m.debit, c.m.credit)) # compares two correlated events, this expression is evaluated in the backend @when_all(c.first << m.amount > 100, c.second << m.amount > c.first.amount + m.amount / 2) def fraud_2(c): print('fraud detected ->{0}'.format(c.first.amount)) print('fraud detected ->{0}'.format(c.second.amount)) post('risk', { 'debit': 220, 'credit': 100 }) post('risk', { 'debit': 150, 'credit': 100 }) post('risk', { 'amount': 200 }) post('risk', { 'amount': 500 }) ``` -------------------------------- ### Querying Nested Objects with '.' Notation Source: https://github.com/jruizgit/rules/blob/master/docs/json/reference.md Shows how to query properties within nested objects using the '.' notation. This example checks for a 'bill' event and compares 'invoice.amount' between nested 'bill' and 'account' events. ```javascript { "expense": { "r_0": { "all": [ { "bill": { "$and": [ { "t": "bill" }, { "$gt": { "invoice.amount": 50 } } ] } }, { "account": { "$and": [ { "t": "account" }, { "payment.invoice.amount": { "bill": "invoice.amount" } } ] } } ], "run": "log" } } } ``` -------------------------------- ### UI Initialization with D3.js Source: https://github.com/jruizgit/rules/blob/master/testpy/testdynamic.html Initializes the user interface using D3.js. This includes appending elements like labels, input fields, textareas, buttons, and an iframe to the body. It also sets up event listeners for buttons to trigger JavaScript functions. ```JavaScript var body = d3.select('body'); body.append('span') .append('b') .text('Enter dynamic ruleset: '); body.append('br'); body.append('br'); body.append('small') .append('span') .text('Name: '); body.append('input') .attr('type', 'text') .attr('value', 'first') .attr('id', 'name') .attr('style', "border:1px solid grey"); body.append('small') .append('span') .text(' Type: '); body.append('input') .attr('type', 'text') .attr('value', 'state') .attr('id', 'type') .attr('style', "border:1px solid grey"); body.append('br'); body.append('br'); body.append('textarea') .attr('rows', '25') .attr('cols', '44') .attr('id', 'rulesetpad') .text('{\r\n' + ' "start": {\r\n' + ' "trigger": {\r\n' + ' "all":[{\r\n' + ' "m": {"$gt":{"amount":100}}\r\n' + ' }],\r\n' + ' "to": "suspect",\r\n' + ' "run": "suspect"\r\n' + ' }\r\n' + ' },\r\n' + ' "suspect": {\r\n' + ' "trigger": {\r\n' + ' "all":[{\r\n' + ' "m": {"$gt":{"amount":1000}}\r\n' + ' }],\r\n' + ' "to": "fraud",\r\n' + ' "run": "fraud"\r\n' + ' }\r\n' + ' },\r\n' + ' "fraud":{}\r\n' + '}\r\n'); body.append('br'); body.append('br'); body.append('input') .attr('type', 'button') .attr('id', 'approve') .attr('value', 'Save') .on('click', save); body.append('input') .attr('type', 'button') .attr('id', 'approve') .attr('value', 'Load') .on('click', load); body.append('br'); body.append('br'); body.append('iframe') .attr('width', 350) .attr('height', 350) .attr('scrolling', 'no') .attr('style', "border:1px solid grey"); body.append('br'); body.append('br'); body.append('textarea') .attr('rows', '5') .attr('cols', '44') .attr('id', 'messagepad') .text('{"id": 1, "amount": 2000}'); body.append('br'); body.append('br'); body.append('input') .attr('type', 'button') .attr('id', 'approve') .attr('value', 'Post') .on('click', post); ```