Giter Site home page Giter Site logo

jruizgit / rules Goto Github PK

View Code? Open in Web Editor NEW
1.1K 60.0 209.0 4.91 MB

Durable Rules Engine

License: MIT License

Python 27.18% HTML 0.93% JavaScript 30.69% Ruby 25.55% C 14.06% C++ 1.55% Makefile 0.03%
durable-rules ruby python statechart rete node-javascript business-rules events

rules's Introduction

durable_rules

for real time analytics (a Ruby, Python and Node.js Rules Engine)

Build Status Gem Version npm version PyPI version

durable_rules is a polyglot micro-framework for real-time, consistent and scalable coordination of events. With durable_rules you can track and analyze information about things that happen (events) by combining data from multiple sources to infer more complicated circumstances.

A full forward chaining implementation (Rete) is used to evaluate facts and events in real time. A simple meta-linguistic abstraction lets you define simple and complex rulesets as well as control flow structures such as flowcharts, statecharts, nested statecharts and time driven flows.

The durable_rules core engine is implemented in C, which enables fast rule evaluation as well as muti-language support.

durable_rules can be scaled out by offloading state to a data store out of process such as Redis. State offloading is extensible, so you can integrate the data store of your choice.

In durable_rules V2, less is more: The Rete tree is fully evaluated in C. Thus, the framework is 5x to 10x faster (depending on the scenario) and does not require Redis. The programming model for posting events, asserting and retracting facts is synchronous and does not prescribe any web framework.

Getting Started

Using your scripting language of choice, simply describe the event to match (antecedent) and the action to take (consequent).

Node.js

To install the framework do: npm install durable

var d = require('durable');

d.ruleset('test', function() {
    // antecedent
    whenAll: m.subject == 'World'
    // consequent
    run: console.log('Hello ' + m.subject)
});

d.post('test', {subject: 'World'});

Python

To install the framework do: pip install durable_rules

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' })

Ruby

To install the framework do: gem install durable_rules

require "durable"

Durable.ruleset :test do
  # antecedent
  when_all (m.subject == "World") do
    # consequent
    puts "Hello #{m.subject}"
  end
end

Durable.post :test, { :subject => "World" }

Forward Inference

durable_rules super-power is the foward-chaining evaluation of rules. In other words, the repeated application of logical modus ponens to a set of facts or observed events to derive a conclusion. The example below shows a set of rules applied to a small knowledge base (set of facts).

Node.js

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'});

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' })        

Ruby

require "durable"

Durable.ruleset :animal do
  when_all c.first = (m.predicate == "eats") & (m.object == "flies"),  
          (m.predicate == "lives") & (m.object == "water") & (m.subject == first.subject) do
    assert :subject => first.subject, :predicate => "is", :object => "frog"
  end

  when_all c.first = (m.predicate == "eats") & (m.object == "flies"),  
          (m.predicate == "lives") & (m.object == "land") & (m.subject == first.subject) do
    assert :subject => first.subject, :predicate => "is", :object => "chameleon"
  end

  when_all (m.predicate == "eats") & (m.object == "worms") do
    assert :subject => m.subject, :predicate => "is", :object => "bird"
  end
  
  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 == "chameleon") 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 :animal1, { :subject => "Kermit", :predicate => "eats", :object => "flies" }
Durable.assert :animal1, { :subject => "Kermit", :predicate => "lives", :object => "water" }
Durable.assert :animal1, { :subject => "Greedy", :predicate => "eats", :object => "flies" }
Durable.assert :animal1, { :subject => "Greedy", :predicate => "lives", :object => "land" }
Durable.assert :animal1, { :subject => "Tweety", :predicate => "eats", :object => "worms" }

Pattern Matching

durable_rules provides string pattern matching. Expressions are compiled down to a DFA, guaranteeing linear execution time in the order of single digit nano seconds per character (note: backtracking expressions are not supported).

Node.js

var d = require('durable');

d.ruleset('test', function() {
    whenAll: m.subject.matches('3[47][0-9]{13}')
    run: console.log('Amex detected in ' + m.subject)
    
    whenAll: m.subject.matches('4[0-9]{12}([0-9]{3})?')
    run: console.log('Visa detected in ' + m.subject)
    
    whenAll: 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}')
    run: console.log('Mastercard detected in ' + m.subject)
});

d.assert('test', { subject: '375678956789765' });
d.assert('test', { subject: '4345634566789888' });
d.assert('test', { subject: '2228345634567898' });

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' })
assert_fact('test', { 'subject': '4345634566789888' })
assert_fact('test', { 'subject': '2228345634567898' })

Ruby

require "durable"
Durable.ruleset :test do
  when_all m.subject.matches('3[47][0-9]{13}') do
    puts "Amex detected in #{m.subject}"
  end
  
  when_all m.subject.matches('4[0-9]{12}([0-9]{3})?') do
    puts "Visa detected in #{m.subject}"
  end
  
  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}') do
    puts "Mastercard detected in #{m.subject}"
  end
end

Durable.assert :test, { :subject => "375678956789765" }
Durable.assert :test, { :subject => "4345634566789888" }
Durable.assert :test, { :subject => "2228345634567898" }

Business Rules and Miss Manners

durable_rules can also be used to solve traditional Production Business Rules problems. This example is an industry benchmark. Miss Manners has decided to throw a party. She wants to seat her guests such that adjacent people are of opposite sex and share at least one hobby.

Note how the benchmark flow structure is defined using a statechart to improve code readability without sacrificing performance nor altering the combinatorics required by the benchmark. For 128 guests, 438 facts, the execution time is 450 ms in node.js and 600 ms in Python and Ruby.

IMac, 4GHz i7, 32GB 1600MHz DDR3, 1.12 TB Fusion Drive

Image recognition and Waltzdb

Waltzdb is a constraint propagation problem for image recognition: given a set of lines in a 2D space, the system needs to interpret the 3D depth of the image. The first part of the algorithm consists of identifying four types of junctions, then labeling the junctions following Huffman-Clowes notation. Pairs of adjacent junctions constraint each other’s edge labeling. So, after choosing the labeling for an initial junction, the second part of the algorithm iterates through the graph, propagating the labeling constraints by removing inconsistent labels.

In this case too, the benchmark flow structure is defined using a statechart to improve code readability. The benchmark requirements are not altered. The execution time, for the case of 4 regions, is 430 ms in node.js, 654 ms in Python and 552 ms in Ruby.

IMac, 4GHz i7, 32GB 1600MHz DDR3, 1.12 TB Fusion Drive

To Learn More

Reference Manual:

rules's People

Contributors

danmacnaughtan avatar hea-lab avatar impredicative avatar jasonrig avatar jruizgit avatar ksheurs avatar mcolacino57 avatar mrryanjohnston avatar nixphix avatar rbalyan avatar tknuth avatar tonythomas01 avatar westonplatter avatar zlarsen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rules's Issues

npm install fails on Node 4.6.2

Hello.

Tried to launch the installer on clean installation of version 4.6.2 of node.js (Windows10 Pro) and if fails on node-gyp. Additional details can be found in the attached file.

npm ERR! [email protected] install: node-gyp rebuildnpm ERR! Exit status 1 npm ERR! npm ERR! Failed at the [email protected] install script 'node-gyp rebuild'. npm ERR! Make sure you have the latest version of node.js and npm installed. npm ERR! If you do, this is most likely a problem with the durable package, npm ERR! not with npm itself. npm ERR! Tell the author that this fails on your system: npm ERR! node-gyp rebuild npm ERR! You can get information on how to open an issue for this project with: npm ERR! npm bugs durable npm ERR! Or if that isn't available, you can get their info via: npm ERR! npm owner ls durable npm ERR! There is likely additional logging output above. npm verb exit [ 1, true ]

Best regards,
DP.
npm-debug.txt

Adding Json rule during runtime

Hi Jesus,

I am developing a kind of dashboard for our data-logger sensors. I am willing to be able to define new rules during runtime in the dashboard. I am wondering if durable module accepts online rules via JSON? In other words, I wanna generate a new JSON rule online in the dashboard (webpage), send it to the server, and then (durable) server should add this JSON to its database and use it. Is it OK?

Regarding scalability: we have around 35,000 devices and each device sends one message per second. Each device can have around 10 unique rules. I am wondering if Durable module is able to handle this traffic?
Thank you.

Issue with any rule

Hi:

I am having difficulties with the current scenarios and I am not sure why.

The rule is simple and taking from the test examples.

Durable.ruleset :a5 do
when_all any(m.subject == "jumbo", m.subject == "approve"),
any(m.amount == 10000, m.amount == 100) do
puts "a5 action #{s.sid}"
end
when_start do
post :a5, {:id => 3, :sid => 2, :subject => "jumbo"}
post :a5, {:id => 4, :sid => 2, :amount => 10000}
post :a5, {:id => 1, :sid => 1, :subject => "approve"}
post :a5, {:id => 2, :sid => 1, :amount => 100}
end
end

Durable.run_all

I would expect the engine to report 2 actions for sid 1 & 2.

However the engine only report 1 action.
The interesting part is that when I switch the condition in the any block, the engine then report the missing action but not the first one any more.
I do clear the redis key between test.

Allow timeout more than once per sid

I don't know how difficult this is, but would it be possible to have timeouts fire multiple times for a sid?

I want to be able to kick a sid out of a flowchart stage if it has been in the stage for longer than the timeout. I don't know if there is another way of doing this that is recommended.

Accessing host through npm package

What is the best way to get at the host object when using the npm package? I notice that when using run I could grab it if I give it a start callback, is that the best way to do it, or is there another way while still using the runAll method?

Auditing sids

Hey Jesus!

I am looking for a way to audit my sids. Essentially the sids that I use correlate to ids in a database row, and I want to make sure that everything that currently exists in the cache belongs there. For example, if I didn't use deleteState at a termination node of a flowchart, or if I ended up migrating the rule definition and something got left in an abandoned state. Also, I probably want to write some cli stuff for debugging what is currently going on. What is the best way to do that?

how to deploy durable_rules server on Gunicorn?

hi,jruizgit
Thanks your great job on durable_rules and it helps my project a lot. When I finished my development, I want to deploy it on production env using Gunicorn.
In "engine_server.py" :

if name == 'main':
run_all(host_name = '127.0.0.1', port = 5600)

start gunicorn:

gunicorn -c config/gun.conf engine_server:app

However, I found it failed when deploy on gunicorn.
How can I delpoy durable_rules on gunicorn ?

Blow is gunicorn log:

[2016-09-22 13:48:18 +0000] [589] [INFO] Starting gunicorn 19.6.0
[2016-09-22 13:48:18 +0000] [589] [DEBUG] Arbiter booted
[2016-09-22 13:48:18 +0000] [589] [INFO] Listening at: http://0.0.0.0:5600 (589)
[2016-09-22 13:48:18 +0000] [589] [INFO] Using worker: gevent
[2016-09-22 13:48:18 +0000] [594] [INFO] Booting worker with pid: 594
[2016-09-22 13:48:18 +0000] [597] [INFO] Booting worker with pid: 597
[2016-09-22 13:48:18 +0000] [600] [INFO] Booting worker with pid: 600
[2016-09-22 13:48:18 +0000] [594] [DEBUG] Exception while loading the application
Traceback (most recent call last):
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/arbiter.py", line 557, in spawn_worker
worker.init_process()
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/workers/ggevent.py", line 190, in init_process
super(GeventWorker, self).init_process()
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/workers/base.py", line 126, in init_process
self.load_wsgi()
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/workers/base.py", line 136, in load_wsgi
self.wsgi = self.app.wsgi()
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 65, in load
return self.load_wsgiapp()
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 52, in load_wsgiapp
return util.import_app(self.app_uri)
File "/home/frank/.pyenv/versions/2.7.12/envs/env2712/lib/python2.7/site-packages/gunicorn/util.py", line 370, in import_app
raise AppImportError("Failed to find application: %r" % module)
AppImportError: Failed to find application: 'engine_server'
[2016-09-22 13:48:18 +0000] [594] [INFO] Worker exiting (pid: 594)

Timers Example Failing

I am trying to run the timer example from the docs and 5 seconds later getting this error on the local server:

peekTimers err string ERR Error running script (call to f_41deee5150afc8fbc6364545d32fe6ce92c385d4): @user_script:6: @user_script: 6: Lua redis() command arguments must be strings or integers
unexpected error Could not assert timers, error code: 302
peekTimers err string ERR Error running script (call to f_41deee5150afc8fbc6364545d32fe6ce92c385d4): @user_script:6: @user_script: 6: Lua redis() command arguments must be strings or integers
unexpected error Could not assert timers, error code: 302
peekTimers err string ERR Error running script (call to f_41deee5150afc8fbc6364545d32fe6ce92c385d4): @user_script:6: @user_script: 6: Lua redis() command arguments must be strings or integers
unexpected error Could not assert timers, error code: 302
peekTimers err string ERR Error running script (call to f_41deee5150afc8fbc6364545d32fe6ce92c385d4): @user_script:6: @user_script: 6: Lua redis() command arguments must be strings or integers
unexpected error Could not assert timers, error code: 302
peekTimers err string ERR Error running script (call to f_41deee5150afc8fbc6364545d32fe6ce92c385d4): @user_script:6: @user_script: 6: Lua redis() command arguments must be strings or integers
unexpected error Could not assert timers, error code: 302
peekTimers err string ERR Error running script (call to f_41deee5150afc8fbc6364545d32fe6ce92c385d4): @user_script:6: @user_script: 6: Lua redis() command arguments must be strings or integers
unexpected error Could not assert timers, error code: 302

Nested event properties

Hi,

is it possible to define conditions for nested properties of an event? For example:

Event:

{
  "id": "some id",
  "sid": "some sid",
  "content": {
    "id": "some external id",
    "sid": "some external sid",
    "value": 10
  }
}

Rule:

{
    whenAll: m.content.value.lt(20),
    run: function (c) {
        console.log(`Value of ${c.m.content.id} is less than 20.`);
    }
}

The rule throws a runtime error because m.content.value is undefined. This is probably caused by the proxy. I could not find an example with nested properties. Hence, I assume that it is not supported.

I can work around this issue by flattening the JSON object before posting it to the rule engine, but before I do this I wanted to verify whether it is necessary to do so or if there is a more elegant solution built into durable_rules.

Best regards,

Tobias

Direct sequence of events

Hi,

first of all, thank you for developing and maintaining this great project. What I will explain next is probably no technical issue. I'm using the TypeScript API and want to detect three subsequent "error" events in a stream of events.

Each event has a type property and I tried to define a rule that is similar to the rule defined for https://github.com/jruizgit/rules/blob/master/docs/js/reference.md#correlated-sequence

This rule should trigger the action if three events of type error are posted right after each other, e.g. "info ok error error error", but not if there are other events in between, e.g. "info error ok error error".

This is my code:

import * as d from 'durable';
const { m, s, c } = d;
let counter = 0;

function createId(): string {
    counter += 1;
    return Date.now() + ' ' + counter;
}

const set: any = d.ruleset('test', 
    {
        whenAll: [
            c['id0'] = m['type'].eq('error'),
            c['id1'] = m['type'].eq('error'),
            c['id2'] = m['type'].eq('error'),
        ],
        run: (c) => {
            console.log('TRIGGERED: 3 error events in a row');
            Object.keys(c).forEach(key => {
                if (key.match(/^id/)) {
                    console.log(c[key]);
                }
            });
        }
    }
);

set.whenStart((host) => {
    console.log('inserting events');
    const sid: number = 2;
    for (let i = 0; i < 25; i++) {
        setTimeout(() => {
            const type: string = ['ok', 'info', 'error'][Math.floor(Math.random() * 3)];
            const id: string = createId();
            console.log(id, type);
            host.post('test', {
                id,
                sid,
                type,
            });
        }, Math.random() * 5000);
    }
});

console.log('run...');
d.runAll([{ host: '192.168.99.100', port: 6379 }]);

Unfortunately, the action is triggered whenever an error event is observed. For example, the program produced the following output:

run...
inserting events
Listening on 5000
1469713486428 1 ok
1469713486724 2 ok
1469713487548 3 ok
1469713487624 4 info
1469713487763 5 ok
1469713488117 6 ok
1469713488208 7 ok
1469713488401 8 ok
1469713488415 9 ok
1469713488732 10 ok
1469713488860 11 ok
1469713488883 12 info
1469713488985 13 error
TRIGGERED: 3 error events in a row
{ id: '1469713488985 13', type: 'error', sid: 2 }
{ id: '1469713488985 13', type: 'error', sid: 2 }
{ id: '1469713488985 13', type: 'error', sid: 2 }
1469713489349 14 info
1469713489415 15 info
1469713489647 16 error
1469713489798 17 ok
1469713489851 18 ok
TRIGGERED: 3 error events in a row
{ id: '1469713489647 16', type: 'error', sid: 2 }
{ id: '1469713489647 16', type: 'error', sid: 2 }
{ id: '1469713489647 16', type: 'error', sid: 2 }
1469713489990 19 info
1469713490027 20 info
1469713490261 21 ok
1469713490309 22 error
TRIGGERED: 3 error events in a row
{ id: '1469713490309 22', type: 'error', sid: 2 }
{ id: '1469713490309 22', type: 'error', sid: 2 }
{ id: '1469713490309 22', type: 'error', sid: 2 }
1469713490483 23 ok
1469713490678 24 ok
1469713490907 25 error
TRIGGERED: 3 error events in a row
{ id: '1469713490907 25', type: 'error', sid: 2 }
{ id: '1469713490907 25', type: 'error', sid: 2 }
{ id: '1469713490907 25', type: 'error', sid: 2 }

symbol issue

Hi

When I am trying to use this I am getting the following error on an Ubuntu box:

ImportError: /home/aaron/.virtualenvs/rules2/local/lib/python2.7/site-packages/rules.so: undefined symbol: redisFormatCommandArgv

Any ideas what could be wrong here?

Make rules work with Python's Flask framework

Project seems exciting.
I created a test project and tried to integrate rules with Flask's end points. If i put flask code on top then i get json response what Flask sends. It does not trigger rule which is mentioned.
My code is below:

`from durable.lang import *
from flask import Flask
app = Flask(name)
@app.route('/test/1', methods=['POST'])
def test():
return "Hello, cool!"

if name == 'main':
app.run(debug=True)
with ruleset('test'):
# antecedent
@when_all(m.subject == 'World')
def say_hello(c):
# consequent
print('Hello World {0}'.format(c.m.subject))

@when_start
def start(host):
    host.post('test', {'id': 1, 'sid': 1, 'subject': 'World'})

run_all()

`

If i put flask's route on top of rules then i get json response which is defined. But if i move rules cod on top then i get this response:
{"outcome": 212}

and no Flask's json.

How to integrate it with Flask?

Ruby - Rule and Timer Dispatcher Questions / Issues ?

Hi:

First of all, I would like to thank you for this very good piece of software.
I have been looking over the last couple of days at your Ruby implementation of the Engine.

While comparing the code to the Python and JS implementation, there is a significant difference in the management of the Rule & Timer dispatchers.

In both JS and Python, it seems you have create 2 threads that Poll every 250ms including the ability to dispatch immediately, While in Ruby it seems that there is only 1 timer thread created that poll every 10ms and dispatch Timer at every 5 iterations while dispatching Rule at all other 4 occasions.

Is there any reason, why you have not use the same principle as Python/JS and create 2 Threads with 250ms poll interval because as I read the previous discussion too high poll interval could stall the engine if each rule action take some time.

Python installation fails on raspberry

Hello,

Just wanted to experiment. Maybe raspberry is not suitable for durable_rules? On pip install durable_rules I get the following:

Downloading/unpacking durable-rules
Downloading durable_rules-0.33.75.tar.gz (96kB): 96kB downloaded
Running setup.py (path:/tmp/pip-build-E1bO6l/durable-rules/setup.py) egg_info for package durable-rules

Requirement already satisfied (use --upgrade to upgrade): werkzeug in /usr/lib/p                                                     ython2.7/dist-packages (from durable-rules)
Installing collected packages: durable-rules
  Running setup.py install for durable-rules
    building 'rules_py' library
    arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prot                                                     otypes -fno-strict-aliasing -std=c99 -D_GNU_SOURCE -fPIC -c deps/hiredis/hiredis                                                     .c -o build/temp.linux-armv7l-2.7/deps/hiredis/hiredis.o
    arm-linux-gnueabihf-gcc: internal compiler error: Segmentation fault (progra                                                     m as)
    Please submit a full bug report,
    with preprocessed source if appropriate.
    See <file:///usr/share/doc/gcc-4.9/README.Bugs> for instructions.
    error: command 'arm-linux-gnueabihf-gcc' failed with exit status 4
    Complete output from command /usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip-build-E1bO6l/durable-rules/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-Gq224q-record/install-record.txt --single-version-externally-managed --compile:
    running install

running build

running build_py

creating build

creating build/lib.linux-armv7l-2.7

creating build/lib.linux-armv7l-2.7/durable

copying libpy/durable/engine.py -> build/lib.linux-armv7l-2.7/durable

copying libpy/durable/lang.py -> build/lib.linux-armv7l-2.7/durable

copying libpy/durable/__init__.py -> build/lib.linux-armv7l-2.7/durable

copying libpy/durable/interface.py -> build/lib.linux-armv7l-2.7/durable

running build_clib

building 'rules_py' library

creating build/temp.linux-armv7l-2.7

creating build/temp.linux-armv7l-2.7/deps

creating build/temp.linux-armv7l-2.7/deps/hiredis

creating build/temp.linux-armv7l-2.7/src

creating build/temp.linux-armv7l-2.7/src/rules

arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -std=c99 -D_GNU_SOURCE -fPIC -c deps/hiredis/hiredis.c -o build/temp.linux-armv7l-2.7/deps/hiredis/hiredis.o

arm-linux-gnueabihf-gcc: internal compiler error: Segmentation fault (program as)

Please submit a full bug report,

with preprocessed source if appropriate.

See <file:///usr/share/doc/gcc-4.9/README.Bugs> for instructions.

error: command 'arm-linux-gnueabihf-gcc' failed with exit status 4

----------------------------------------
Cleaning up...
**Command /usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip-build-E1bO6l/durable-rules/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-Gq224q-record/install-record.txt --single-version-externally-managed --compile failed with error code 1 in /tmp/pip-build-E1bO6l/durable-rules
Storing debug log for failure in /root/.pip/pip.log**

Node-Red nodes to integrate rules into our flow

Hi,

Node-Red is a visual tool for wiring services among others things.
We can see it as a visual programming or workflow tool.
It's popular these days and we heard more buzz from it, particularly with talks on IoT, Bluemix...

This is very interesting to use a rule based engine with a workflow tool.
This is what we have with node-red-contrib-nools
https://github.com/mapero/node-red-contrib-nools providing nools rule engine for node-RED.

I suggest to do the same with Durable rules.

Error when running npm install durable

Hi, getting the following when trying to install the javascript version for node. Here is the error extract from the npm-debug.log

4606 error Windows_NT 6.3.9600
4607 error argv "C:\Program Files\nodejs\node.exe" "C:\Users\andyf_000\AppData\Roaming\npm\node_modules\npm\bin\npm-cli.js" "install" "durable"
4608 error node v5.9.0
4609 error npm v2.6.0
4610 error code ELIFECYCLE
4611 error [email protected] install: node-gyp rebuild
4611 error Exit status 1
4612 error Failed at the [email protected] install script 'node-gyp rebuild'.
4612 error This is most likely a problem with the durable package,
4612 error not with npm itself.
4612 error Tell the author that this fails on your system:
4612 error node-gyp rebuild
4612 error You can get their info via:
4612 error npm owner ls durable
4612 error There is likely additional logging output above.
4613 verbose exit [ 1, true ]

SID ?

I am not able to grasp the meaning of SID here, say I have a url --> http://localhost:5000/a0/3
3 becomes the sid and it even replaces the existing sid in the payload. What's the purpose of this sid field ? secondly when I send a payload to this url, http://localhost:5000/a0, without sid, it's supposed to post a new ruleset, but it just breaks for me. Please clear my doubts as I am totally bugged with the use of sid.

ReferenceError: sid is not defined

When I try to compare state to other state using the TypeScript abstraction, finish: { whenAny: s['count'].gt(s['limit']) } I get the following stacktrace.

Stack Trace:

ReferenceError: sid is not defined
    at Object.that.define (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:142:21)
    at Object.that.define (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:150:45)
    at Object.that.define (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:390:41)
    at Object.that.define (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:810:30)
    at Object.that.define (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:853:52)
    at Object.that.define (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:906:64)
    at module.exports.createHost (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:931:62)
    at Object.module.exports.runAll (/Users/enriched/git/scratch/node_modules/durable/libjs/durable.js:951:25)
    at Object.<anonymous> (/Users/enriched/git/scratch/built/index.js:57:11)
    at Module._compile (module.js:425:26)

When I change to finish: { whenAny: s['count'].gt(10) } it works as expected.

node-gyp build fails after updating to node version 6.3.1

Hi,

I've recently updated my node version to 6.3.1 and now I cannot build durable_rules anymore. Before the update I had no issues.

  • node v6.3.1
  • npm v3.10.6
  • node-gyp v3.4.0

I think the main issue is this:

..\src\rulesjs\rules.cc(1031): error C2872: 'Proxy': ambiguous symbol [C:\Users\myuser\Downloads\node_modules\durable\build\rulesjs.vcxproj]
  ..\src\rulesjs\rules.cc(9): note: could be 'Proxy'
  c:\users\myuser\.node-gyp\6.3.1\include\node\v8.h(3396): note: or       'v8::Proxy' (compiling source file ..\src\rulesjs\rules.cc)

I assume that this a naming conflict. My old node version (5.0.0) did not support Proxy class, but node 6.3.1 does.

The naming conflict arises at https://github.com/jruizgit/rules/blob/master/src/rulesjs/rules.cc#L9 where your Proxy class is defined also using the v8 namespace.

Based on your package.json, I guess node 6.x.x is not supported yet. However, I would greatly appreciate being able to use node version 6.x.x as I need the Proxy class.

Remark:
To verify my assumption regarding the issue's cause, I cloned your repository, renamed Proxy in https://github.com/jruizgit/rules/blob/master/src/rulesjs/rules.cc, and ran npm install which includes the node-gyp build. The node-gyp build then worked like a charm.

Best regards,

Tobias

[Proposal] Put the facts and rules into the redis server.

Why not put the facts and rules into the redis server since the alpha and beta nodes have been stored?

First this should be supported:

  • General facts: not depend on the session or ruleset.
    • all keys are facts on redis if do not think about backwards compatibility.
      • the rulesets are the facts too.
  • Use the key prefix '/AI/ruleset/', '/AI/session/' to group ruleset and session.
    • '/AI/ruleset/:ruleset-id/rule/[rule-name]': the rule of the ruleset-id
    • '/AI/ruleset/fact/': the facts related to ruleset
    • '/AI/session/:session-id/fact/': the facts related to session
    • '/AI/session/:session-id/ruleset/:ruleset-id/fact/': the facts related to session and ruleset
var d = require('durable');
var Redis = require('ioredis');
var redis = new Redis();

with (d.ruleset('a0')) {
    whenAll(m.amount.lt(100), function (c) {
        console.log('general fact approved from ' + c.s.sid);
    });
    whenAll(m['/AI/ruleset/a0/fact/amount'].lt(100), function (c) {
        console.log('a0 approved from ' + c.s.sid);
    });
    whenStart(function (host) {
        redis.set('amount', 10);
        redis.set('/AI/ruleset/a0/fact/amount', 10);//related to the ruleset
        redis.set('/AI/session/1/fact/amount', 10);//related to the session
        // host.post('a0', {id: 1, sid: 1, amount: 10});
    });
}
d.runAll();

The storage of the rules are similar. It's so easy if the rules are running on the redis via lua.
And it will make the redis become the distributed inference engine completely.
But if not:

redis.config('set', 'notify-keyspace-events', 'KEA');

redis.psubscribe('__keyspace@*__:/AI/ruleset/*/rule/*', function (err, count) {
  if (err) console.log(err)
})

redis.on('pmessage', function (pattern, channel, message) {
    if(pattern is '__keyspace@*__:/AI/ruleset/*/rule/*'){
        //Here, update the rulesets to the script engine.
    }
})

Test dynamic and admin view

Hi,

First, let me say that I really love your framework and how you build your architecture!
I'm trying to use the testdynamic.js and testdynamic.html test in order to understand how it works. Unfortunately, just out of the box, it gives endless errors on the client side while trying to register new rules(but it works). The post message doesn't work as well and the HTTP request failed with no clear error. I tried to debug it without success.
Also, I was trying to get one example running the admin.html and the D3 example. Both fail on the client side with errors. I tried both Chrome and Safari. I'm opening a http://localhost:5000/first/a/admin.html. The page is found but never loads, no matter what the rule is.
Would love to get your help.

Thanks,
D.

Can I maintain event sequence based on a given timestamp?

Hi,

I would like to know that is it possible that durable_rules processes events in the same sequence as they are posted from the client if I have only one local client which can attach an strictly incremental timestamp to each event.

I try to approach this with the following code, but no luck.

from durable.lang import *

gid = 0

with ruleset('fraud_detection'):
    @when_all(m.time == gid + 1)
    def detected(c):
        global gid
        gid = gid + 1
        print ('purchase from {0}'.format(c.m.location))

    @when_start
    def start(host):
        host.post('fraud_detection', {'id': 1, 'sid': 10, 't': 'purchase', 'location': 'US', 'time': 1})
        host.post('fraud_detection', {'id': 2, 'sid': 10, 't': 'purchase', 'location': 'CA', 'time': 3})
         host.post('fraud_detection', {'id': 3, 'sid': 10, 't': 'purchase', 'location': 'TW', 'time': 2})

run_all()

I am expecting the result would be

purchase from US 
purchase from TW 
purchase from CA

However, the actual result is

purchase from US

I really appreciate any help you can provide.
Thanks

Install fails

Intriguing package! Unfortunately getting started is a bit hard.

Although the docs (https://github.com/jruizgit/rules/blob/master/docs/py/reference.md) only tell us to install Redis I presumed a pip install durable was in order.
On PyPi I find 2 packages durable and durable_rules. Both fail to install.
Finally, the section "First App" is almost empty, but for a beginner probably the most relevant.

durable:

DEPRECATION: --no-install, --no-download, --build, and --no-clean are deprecated.  See https://github.com/pypa/pip/issues/906.
Downloading/unpacking durable
  Could not find any downloads that satisfy the requirement durable
Cleaning up...
No distributions at all found for durable
Storing debug log for failure in /Users/mjm/.pip/pip.log

durable_rules:

DEPRECATION: --no-install, --no-download, --build, and --no-clean are deprecated.  See https://github.com/pypa/pip/issues/906.
Downloading/unpacking durable-rules
  Downloading durable_rules-0.31.16.tar.gz
  Running setup.py (path:/private/var/folders/wd/dw1f3w9x0xv2nm57vcs7z3g00000gn/T/pycharm-packaging0.tmp/durable-rules/setup.py) egg_info for package durable-rules

Downloading/unpacking werkzeug (from durable-rules)
Installing collected packages: durable-rules, werkzeug
  Running setup.py install for durable-rules
    building 'rules' extension
    clang -fno-strict-aliasing -fno-common -dynamic -I/usr/local/include -I/usr/local/opt/sqlite/include -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -Isrc/rules -I/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c src/rulespy/rules.c -o build/temp.macosx-10.10-x86_64-2.7/src/rulespy/rules.o
    src/rulespy/rules.c:2:10: fatal error: 'rules.h' file not found
    #include <rules.h>
             ^
    1 error generated.
    error: command 'clang' failed with exit status 1
    Complete output from command /Users/mjm/Dropbox/Devel/Rules/venv/bin/python -c "import setuptools, tokenize;__file__='/private/var/folders/wd/dw1f3w9x0xv2nm57vcs7z3g00000gn/T/pycharm-packaging0.tmp/durable-rules/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/wd/dw1f3w9x0xv2nm57vcs7z3g00000gn/T/pip-2LrYKg-record/install-record.txt --single-version-externally-managed --compile --install-headers /Users/mjm/Dropbox/Devel/Rules/venv/bin/../include/site/python2.7:
    running install

running build

running build_py

creating build

creating build/lib.macosx-10.10-x86_64-2.7

creating build/lib.macosx-10.10-x86_64-2.7/durable

copying libpy/durable/__init__.py -> build/lib.macosx-10.10-x86_64-2.7/durable

copying libpy/durable/engine.py -> build/lib.macosx-10.10-x86_64-2.7/durable

copying libpy/durable/interface.py -> build/lib.macosx-10.10-x86_64-2.7/durable

copying libpy/durable/lang.py -> build/lib.macosx-10.10-x86_64-2.7/durable

running build_ext

building 'rules' extension

creating build/temp.macosx-10.10-x86_64-2.7

creating build/temp.macosx-10.10-x86_64-2.7/src

creating build/temp.macosx-10.10-x86_64-2.7/src/rulespy

clang -fno-strict-aliasing -fno-common -dynamic -I/usr/local/include -I/usr/local/opt/sqlite/include -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -Isrc/rules -I/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c src/rulespy/rules.c -o build/temp.macosx-10.10-x86_64-2.7/src/rulespy/rules.o

src/rulespy/rules.c:2:10: fatal error: 'rules.h' file not found

#include <rules.h>

         ^

1 error generated.

error: command 'clang' failed with exit status 1

----------------------------------------
Cleaning up...
Command /Users/mjm/Dropbox/Devel/Rules/venv/bin/python -c "import setuptools, tokenize;__file__='/private/var/folders/wd/dw1f3w9x0xv2nm57vcs7z3g00000gn/T/pycharm-packaging0.tmp/durable-rules/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/wd/dw1f3w9x0xv2nm57vcs7z3g00000gn/T/pip-2LrYKg-record/install-record.txt --single-version-externally-managed --compile --install-headers /Users/mjm/Dropbox/Devel/Rules/venv/bin/../include/site/python2.7 failed with error code 1 in /private/var/folders/wd/dw1f3w9x0xv2nm57vcs7z3g00000gn/T/pycharm-packaging0.tmp/durable-rules
Storing debug log for failure in /Users/mjm/.pip/pip.log

which 'id' is available

Hi, I try Durable Rules in python, I write a python script to make POST request

import requests
import json

url = 'http://127.0.0.1:5000/test/1'
payload = {'id':1, 'subject':'World'}
headers = {'content-type': 'application/json'}
r = requests.post(url, data=json.dumps(payload), headers=headers)
print('ok' if r.status_code == 200 else 'Nok')

I use the "Example 1" script, it's work well (I got the "Hello World") but just 1 time, i see that the 'id' must be unique for each request.

So if I use separte scripts who make the same request how can i know which 'id' was not used before ?

Thanks for your reply,

Sorry for my english (i'm french)

Different between Facts and Events

First of all, I would like to thank you for your great effort on developing this amazing framework. I am planning to use this in my own project.

I have tried the fraud detection example as below

from durable.lang import *
with ruleset('fraud_detection'):

    @when_all(c.first << m.amount > 100,
              c.second << m.amount > c.first.amount * 2,
              c.third << m.amount > (c.first.amount + c.second.amount) / 2)
    def detected(c): 
        print('fraud detected -> {0} {1}'.format(c.first.amount, c.first.id))
        print('               -> {0} {1}'.format(c.second.amount, c.second.id))
        print('               -> {0} {1}'.format(c.third.amount, c.third.id))

    @when_start
    def start(host):
        sid = 18
        host.post('fraud_detection', {'id': 30, 'sid': sid, 'amount': 120})
        host.post('fraud_detection', {'id': 50, 'sid': sid, 'amount': 1000})

run_all()

It works as I expected with the following output

fraud detected -> 120 30
               -> 1000 50
               -> 1000 50

As far as I know, the difference between facts and events is the life cycle. Each event will only trigger one single action and be retracted automatically after action dispatched, while facts will stay in the same inference set until someone manually retract them. Therefore, I am expecting to see the same result as above even if I assert facts instead of posting events.

I change the source code to this

from durable.lang import *
with ruleset('fraud_detection'):

    @when_all(c.first << m.amount > 100,
              c.second << m.amount > c.first.amount * 2,
              c.third << m.amount > (c.first.amount + c.second.amount) / 2)
    def detected(c): 
        print('fraud detected -> {0} {1}'.format(c.first.amount, c.first.id))
        print('               -> {0} {1}'.format(c.second.amount, c.second.id))
        print('               -> {0} {1}'.format(c.third.amount, c.third.id))

    @when_start
    def start(host):
        sid = 18
        host.assert_fact('fraud_detection', {'id': 30, 'sid': sid, 'amount': 120})
        host.assert_fact('fraud_detection', {'id': 50, 'sid': sid, 'amount': 1000})

run_all()

In this case, there is no output at all.
Could you please share some light on this case if I misunderstand anything.

null values in a JSON object (Javascript) fact or event component prevents match

Simply if a fact or an event contains null as one of the property values, matching the rule fails.

e.g. for

const durable = require('durable');
let M = durable.m, S = durable.s, C = durable.c;

whenAll:[M.token.ex()],
   run: function(c) {
     console.log(JSON.stringify(c.m));
   }

works

c.assert('a0', {id: id, eenvId: eenvId, token: true, tokenVal:somenonnullvalue});

doesn't work

c.assert('a0', {id: id, eenvId: eenvId, token: true, tokenVal:null });

Question: Test events in python

Hi,

According to the events example in testpy/testevents.py, the events can also be asserted with assert_event. And the ruleset can be defined as a dictionary.

My question is how can I define the ruleset action(s) method(s). In the example, the action is executed with complete_action call https://github.com/jruizgit/rules/blob/master/testpy/testevents.py#L59
but result[2] is a long int value. It is not clear what is the action method in that case. And where should it be defined.

Thanks.

Use of 'with' is illegal in strict mode

Attempting to use your JS examples while also using strict mode does not work because the use of with is not allowed in strict mode. Expect this to become more and more of a problem. My workaround is explicit variable declaration (e.g. R = d.ruleset('a0'); and then later R.withAll). Syntactically, this is obviously more cumbersome.

Digital Ocean

Hello,

Does a one click install approach exist to deploy Durable rule on Digital Ocean?
If no, what other alternative do we have?

Thanks in advance

Events stops triggering rules after random number of events get dispatched

Hi,

I recently run into this problem that new events stop triggering rules after random number of events get dispatched.

Here is my ruleset definitions:

with ruleset('daware'):
    @when_all(pri(1),
              c.first << +s.last,
              c.second << (m.id == c.first.last + 1) & (m.action == 'open'))
    def daware_fopen(c):
        c.s.last = c.second.id
        logging.error('daware_fopen get message id {0}'.format(c.second.id))

    @when_all(pri(1),
              c.first << +s.last,
              c.second << (m.id == c.first.last + 1) & (m.action == 'write'))
    def daware_write_access(c):
        c.s.last = c.second.id
        logging.error('daware_write_access get message id {0}'.format(c.second.id))

    @when_all(pri(1),
              c.first << +s.last,
              c.second << (m.id == c.first.last + 1) & (m.action == 'read'))
    def daware_read_access(c):
        c.s.last = c.second.id
        logging.error('daware_read_access get message id {0}'.format(c.second.id))

    @when_all(pri(2), m.id > 0)
    def fallback(c):
        logging.error('fallback id {0}'.format(c.m.id))

    @when_start
    def start_test(host):
        host.patch_state('daware', {'sid': 1, 'last': 0})

Basically, I just log every event into a log file with python logging package. Since I want these events being logged in the same sequence as the event id, I take your advice from another post to store the last processed id in the host context.

I have another daemon running locally which sends events by HTTP POST around 30k events in 30 seconds to local address http://127.0.0.1/data_aware/1.

I add a line in Application class to dump all the received messages to my log file, so I can confirm that all the events I post from my daemon has been received by durable_rules.

The output log will be like this

ERROR:root:daware_write_access get message id 6684
ERROR:root:daware_write_access get message id 6685
INFO:root:Get POST: {u'sid': u'1', u'action': u'write', u'id': 6882}
INFO:werkzeug:127.0.0.1 - - [29/Aug/2016 17:48:45] "POST /data_aware/1 HTTP/1.1" 200 -

As you can see that 6685 is the last event id that my ruleset had logged, but the werkzeug package had logged an POST with id = 6882.

This issue cannot be consistently reproduced in every around of my experiment, but I can always reproduce this in 10 rounds which means that 300k events had been sent.

This looks like a bug to me, but I cannot really confirm it. Can you think of anything that could lead to this behaviour or point me some directions to help me clarify this problem?

If you need more information or if there is anything I can help, please feel free to let me know.

Definition of sid and id

Hi,

While going through documentation, I could not figure out what is the significance of id and sid. Can you please explain what these parameters mean and how they are used internally ?

Problem when installing on Ubuntu: npm ERR! Failed at the [email protected] install script.

Hi jruizgit,

when I try to create the simple demo test on ubuntu (heroku dyno), it fails in the npm package build due to compiling issues during node-gyp build.
It also looks like all the code updates in repo are not reflected in npm (3 months old).

Attached trace.

remote: Installing node modules
remote: npm WARN engine [email protected]: wanted: {"node":"0.8.x","npm":"1.1.x"} (current: {"node":"v0.10.17","npm":"1.3.26"})
remote:
remote: > [email protected] install /tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable/node_modules/hiredis
remote: > node-gyp rebuild
remote:
remote: make: Entering directory `/tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable/node_modules/hiredis/build'
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/hiredis.o
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/net.o
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/sds.o
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/async.o
remote: AR(target) Release/obj.target/deps/hiredis.a
remote: COPY Release/hiredis.a
remote: CXX(target) Release/obj.target/hiredis/src/hiredis.o
remote: CXX(target) Release/obj.target/hiredis/src/reader.o
remote: SOLINK_MODULE(target) Release/obj.target/hiredis.node
remote: SOLINK_MODULE(target) Release/obj.target/hiredis.node: Finished

remote: COPY Release/hiredis.node
remote: make: Leaving directory /tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable/node_modules/hiredis/build' remote: remote: > [email protected] install /tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable/node_modules/node-static/node_modules/mime remote: > node build/build.js > types.json remote: remote: remote: > [email protected] install /tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable remote: > node-gyp rebuild remote: remote: make: Entering directory/tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable/build'
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/hiredis.o
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/net.o
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/sds.o
remote: CC(target) Release/obj.target/hiredis/deps/hiredis/async.o
remote: AR(target) Release/obj.target/deps/hiredis.a
remote: COPY Release/hiredis.a
remote: CC(target) Release/obj.target/rules/src/rules/json.o
remote: ../src/rules/json.c: In function 'getString':
remote: ../src/rules/json.c:319:20: warning: 'delimiter' may be used uninitialized in this function [-Wmaybe-uninitialized]
remote: if (start[0] == delimiter) {
remote: ^
remote: ../src/rules/json.c: In function 'readNextName':
remote: ../src/rules/json.c:354:20: warning: 'delimiter' may be used uninitialized in this function [-Wmaybe-uninitialized]
remote: if (start[0] == delimiter) {
remote: ^
remote: ../src/rules/json.c:339:10: note: 'delimiter' was declared here
remote: char delimiter;
remote: ^
remote: CC(target) Release/obj.target/rules/src/rules/rete.o
remote: ../src/rules/rete.c: In function 'storeQuery':
remote: ../src/rules/rete.c:103:5: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int i = 0; i < lineCount; ++i) {
remote: ^
remote: ../src/rules/rete.c:103:5: note: use option -std=c99 or -std=gnu99 to compile your code
remote: ../src/rules/rete.c: In function 'createBeta':
remote: ../src/rules/rete.c:828:13: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (int i = 0; i < lengthDiff -1; ++i) {
remote: ^
remote: ../src/rules/rete.c: In function 'add':
remote: ../src/rules/rete.c:881:5: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int i = 0; i < left->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c: In function 'multiply':
remote: ../src/rules/rete.c:896:5: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int i = 0; i < left->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c:898:9: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int ii = 0; ii < right->nodesLength; ++ii) {
remote: ^
remote: ../src/rules/rete.c:910:13: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int iii = 0; iii < rightAll->namesLength; ++iii) {
remote: ^
remote: ../src/rules/rete.c:919:31: error: redefinition of 'iii'
remote: for (unsigned int iii = 0; iii < leftAll->namesLength; ++iii) {
remote: ^
remote: ../src/rules/rete.c:910:31: note: previous definition of 'iii' was here
remote: for (unsigned int iii = 0; iii < rightAll->namesLength; ++iii) {
remote: ^
remote: ../src/rules/rete.c:919:13: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int iii = 0; iii < leftAll->namesLength; ++iii) {
remote: ^
remote: ../src/rules/rete.c:932:27: error: redefinition of 'ii'
remote: for (unsigned int ii = 0; ii < leftAll->namesLength; ++ii) {
remote: ^
remote: ../src/rules/rete.c:898:27: note: previous definition of 'ii' was here
remote: for (unsigned int ii = 0; ii < right->nodesLength; ++ii) {
remote: ^
remote: ../src/rules/rete.c:932:9: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int ii = 0; ii < leftAll->namesLength; ++ii) {
remote: ^
remote: ../src/rules/rete.c:939:23: error: redefinition of 'i'
remote: for (unsigned int i = 0; i < right->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c:896:23: note: previous definition of 'i' washere
remote: for (unsigned int i = 0; i < left->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c:939:5: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int i = 0; i < right->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c:941:9: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int ii = 0; ii < rightAll->namesLength; ++ii) {
remote: ^
remote: ../src/rules/rete.c: In function 'createQueries':
remote: ../src/rules/rete.c:999:5: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int i = 0; i < betaPath->namesLength; ++ i) {
remote: ^
remote: ../src/rules/rete.c: In function 'fixupQueries':
remote: ../src/rules/rete.c:1059:5: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int i = 0; i < query->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c:1062:9: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int ii = 0; ii < currentAll->namesLength;++ii) {
remote: ^
remote: ../src/rules/rete.c:1068:27: error: redefinition of 'ii'
remote: for (unsigned int ii = 0; ii < currentAll->namesLength;++ii) {
remote: ^
remote: ../src/rules/rete.c:1062:27: note: previous definition of 'ii' was here
remote: for (unsigned int ii = 0; ii < currentAll->namesLength;++ii) {
remote: ^
remote: ../src/rules/rete.c:1068:9: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int ii = 0; ii < currentAll->namesLength;++ii) {
remote: ^
remote: ../src/rules/rete.c:1086:23: error: redefinition of 'i'
remote: for (unsigned int i = 0; i < query->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c:1059:23: note: previous definition of 'i' was here
remote: for (unsigned int i = 0; i < query->nodesLength; ++i) {
remote: ^
remote: ../src/rules/rete.c:1086:5: error: 'for' loop initial declarations are only allowed in C99 mode
remote: for (unsigned int i = 0; i < query->nodesLength; ++i) {
remote: ^
remote: make: *** [Release/obj.target/rules/src/rules/rete.o] Error 1
remote: make: Leaving directory /tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable/build' remote: gyp ERR! build error remote: gyp ERR! stack Error:makefailed with exit code: 2 remote: gyp ERR! stack at ChildProcess.onExit (/tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/.heroku/node/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:267:23) remote: gyp ERR! stack at ChildProcess.EventEmitter.emit (events.js:98:17) remote: gyp ERR! stack at Process.ChildProcess._handle.onexit (child_process.js:789:12) remote: gyp ERR! System Linux 3.13.0-40-generic remote: gyp ERR! command "node" "/tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/.heroku/node/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild" remote: gyp ERR! cwd /tmp/build_2aa3e8b6eeba3c027492030ab7e84d1c/node_modules/durable remote: gyp ERR! node -v v0.10.17 remote: gyp ERR! node-gyp -v v0.12.2 remote: gyp ERR! not ok remote: npm ERR! [email protected] install:node-gyp rebuild`
remote: npm ERR! Exit status 1
remote: npm ERR!
remote: npm ERR! Failed at the [email protected] install script.

why post a nested json message in python is failed?

hi,
I want to post a nested json to a rule set like this:
post("http://localhost:5000/pin_fen/1", params_dic).
params_dic is a dic which data come from a json string:
{
"cc_n_all":15,
"CreditCard":
[
{
"Last4Digit": "9832",
"Issue Bank": "111",
"Statement_Date": 18,
"Payment_Due_Date": 5,
"CreditAmt": 100000
},
{
"Last4Digit": "9832",
"Issue Bank": "33333",
"Statement_Date": 18,
"Payment_Due_Date": 5,
"CreditAmt": 100000
}]
}

The rule set is :
with ruleset('pin_fen'):
@when_all(pri(1), m.cc_n_all > 0)
def rule_1(c):
print('before: c.m: {0}'.format(c.m))
if c.m.cc_n_all >1 and c.m.cc_n_all<28:
c.m.cc_n_all_woe = 11.12

Why in the c.m, I can not find the c.m.CreditCard in memory? Does support post a nested dic such as
a list is a dic value or more complex dic value, like:

{
  "ApplyID":"123456",
  "IDCardNum":"452122197905161234",
  "Name":"测试1",
  "cc_n_all":15,
  "cc_n_all_woe":null,
  "CreditCard": {
    "Last4Digit": "9832",
    "Issue Bank": "7777777",
    "Statement_Date": 18,
    "Payment_Due_Date": 5,
    "CreditAmt": 100000,
    "Statement": [
      {
        "Statement_Period": "2016.04.12-2016.05.11",
        "Expense": 982,
        "Payment": 1525.72,
        "LastBalance": -1525.72,
        "Last_Min_Payment": 152.57,
        "Balance": 982,
        "Min_Payment": 98.2,
        "Late_Fee": 0,
        "Statement_Detail": [
          {
            "Last4Digit": "9832",
            "Trans_Date": "2016/4/14",
            "Post_Date": "2016/4/14",
            "Trans_ Type": "777777",
            "Trans_Desc": "7777777777777)"

          },
          {
            "Last4Digit": "9832",
            "Trans_Date": "2016/4/20",
            "Post_Date": "2016/4/20",
            "Trans_ Type": "777777777",
            "Trans_Desc": "666666666"

          }
        ]
      },
      {
        "Statement_Period": "2016.05.12-2016.06.11",
        "Expense": 64.97,
        "Payment": 995,
        "LastBalance": 982,
        "Last_Min_Payment": 98.2,
        "Balance": 51.97,
        "Min_Payment": 5.2,

        "Late_Fee": 4.91,
        "Statement_Detail": [
          {
            "Last4Digit": "9832",
            "Trans_Date": "2016/5/16",
            "Post_Date": "2016/5/16",
            "Trans_ Type": "6666666666",
            "Trans_Desc": "yyyyyyyyyy"

          },
          {
            "Last4Digit": "9832",
            "Trans_Date": "2016/6/5",
            "Post_Date": "2016/6/5",
            "Trans_ Type": "yyyyyyy",
            "Trans_Desc": "hhhhhhhhh"       
          }
        ]
      }
    ]
  }
}


Python 3.5 support seems broken

I got durable to install in a Python 3.5 virtualenv, but trying to import durable.lang first gives me a couple of import errors, eg.ImportError: No module named 'engine' which are easily fixed by adding a from . on the start of the line, but then when durable tries to import rules I get

ImportError: /home/sanotehu/durable-rules/lib/python3.5/site-packages/rules.cpython-35m-x86_64-linux-gnu.so: undefined symbol: Py_InitModule

That seems to point to something wrong in the C module.

Pypi installation issue

I was able to successfully download and install the package directly form GitHub, but there seems to be an issue with the Pypi durable_rules package (please see errors below).

$ sudo pip install durable_rules
Collecting durable-rules
  Downloading durable_rules-0.33.03.tar.gz (83kB)
    100% |████████████████████████████████| 86kB 678kB/s 
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 20, in <module>
      File "/private/tmp/pip-build-0IGFQX/durable-rules/setup.py", line 34, in <module>
        with open(path.join(here, 'README.txt'), encoding='utf-8') as f:
      File "/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/codecs.py", line 884, in open
        file = __builtin__.open(filename, mode, buffering)
    IOError: [Errno 2] No such file or directory: '/private/tmp/pip-build-0IGFQX/durable-rules/docs/py/README.txt'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /private/tmp/pip-build-0IGFQX/durable-rules

timers in python

Hi,

I have a problem with "timers" :

from durable.lang import *
with ruleset('VMC'):
    @when_all(m.Cmd == 'On')
    def start_timer(c):
        print("Start")
        c.start_timer('my_timer', 5)

    @when_all(timeout('my_timer'))
    def end_timer(c):
        print('Stop')

run_all()

It works only the first time I got the "Start" and 5sec later I got the "Stop", the second time I only got the "Start".

To make it work I have to change the name of the timer, for example 'my_timer' in 'my_timer1' restart the script and it works again one time.

The name of the timer is maybe save as an ID and it's work just one time ? or the timeout fonction destroy the object 'my_timer' ?

Thank's for your reply

Redis cannot specify DB?

Hi ,I've been using durable rules for several weeks,and here is my problem,I want to run two or more servers and choose different redis DB,but I can not find how to specify the redis DB?
Is there anyway to set this?
Ths.

def run_all(databases = [{'host': 'localhost', 'port': 6379, 'password':None}], host_name = '127.0.0.1', port = 5000, routing_rules = [], run = None, state_cache_size = 1024):
    main_host = create_host(databases, state_cache_size)
    main_app = interface.Application(main_host, host_name, port, routing_rules, run)
    main_app.run()

Uncaught TypeError: undefined is not a function in dutableVisual.js

Hello Jesus

I am trying to use Durable Rules on a Mac by using the Setup and Tutorial instructions but when I open the browser on http://localhost:5000/approve/1/admin.html I get a blank page and 'Uncaught TypeError: undefined is not a function' on Chromes console.

The above error message makes reference to durableVial.js:1616 which contains:
history.onNewRecord(addHistoryRecord);

Do you have any idea about what might be wrong?

I am using redis 2.8.16 and node 0.10.26

Thank you for you help and thank yoy for sharing such an interesting project

Armando

Support for node 4.5.x?

This is an amazingly cool engine. Only worry I have is that we are on node 4.5.0. Any plans on supporting this, or recommendations on how to test it before we jump in?

Fraud Detection Example not working as expected

Hi,

I am trying to run fraud detection example and using following code snippet

from durable.lang import *
with ruleset('fraud_detection'):

    @when_all(c.first << m.amount > 100,
              c.second << m.amount > c.first.amount * 2,
              c.third << m.amount > (c.first.amount + c.second.amount) / 2)
    def detected(c): 
        print('fraud detected -> {0} {1}'.format(c.first.amount, c.first.id))
        print('               -> {0} {1}'.format(c.second.amount, c.second.id))
        print('               -> {0} {1}'.format(c.third.amount, c.third.id))

    @when_start
    def start(host):
        sid = 18
        host.post('fraud_detection', {'id': 30, 'sid': sid, 'amount': 120})
        host.post('fraud_detection', {'id': 50, 'sid': sid, 'amount': 1000})

run_all()

Ideally since I am only posting only 2 events, fraud_detection ruleset should not complete. However I am getting following output on running above code

fraud detected -> 120 30
               -> 1000 50
               -> 1000 50

Can someone explain why this is happening ? Am I missing something here ?

Note: I cleared redis database before running this so that test starts afresh.

TODO: Context Events mids are generated using rand()

Context Events mids are generated using rand(), this is a temporary solution which needs to be improved because it can have unfortunate consequences:

  1. There is a probability of mid collision (1 in 1 billion per session)
  2. Idempotency broken in case of failure (might generate two events for one save)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.