|
5 | 5 |
|
6 | 6 | Node-rules is a forward chaining Rules Engine, written on node.js. |
7 | 7 |
|
8 | | -## How to install |
| 8 | +## Installation |
9 | 9 |
|
10 | | -install this via |
| 10 | +install this via npm |
11 | 11 |
|
12 | 12 | npm install node-rules |
13 | 13 |
|
14 | 14 |
|
15 | 15 | ## Rules |
16 | 16 |
|
17 | | -Node-rules takes rules written in JSON format as input. These rules get applied on the specified iputs(facts) to the rule engine. |
| 17 | +Node-rules takes rules written in JSON friendly format as input. You can register different rules on the rule engine after initiating it. Once the rule engine is running with registered rule, you can feed it with different fact objects and the rule engine will process them with the various rules registred on it. |
18 | 18 |
|
19 | | -A Rules consist of |
20 | 19 |
|
21 | | -1. name - the name for the rule |
| 20 | +### 1. Defining a Rule |
22 | 21 |
|
23 | | -2. conditions - a function which takes inputs and upon returning its results the rule engine executes the corresponding consequence. |
| 22 | +A rule will consist of a condition and its corresponding consequence. If the fact you feed into the engine satisfies the condition, then the consequence will run. Also optionally user may choose to define the priority of a rule applied. The rule engine will be applying the rules on the fact according to the priority defined. |
24 | 23 |
|
25 | | -3. consequence - a function which gets executed accoring to the return value from a condititon after executed. |
| 24 | +Lets see how a sample rule will look like and then proceed to explain the different attributes of a rule. |
26 | 25 |
|
27 | | -4. description - the description for the rule |
| 26 | + { |
| 27 | + "name": "transaction minimum", |
| 28 | + "priority": 3, |
| 29 | + "on" : true, |
| 30 | + "condition": function(R) { |
| 31 | + R.when(this && (this.transactionTotal < 500)); |
| 32 | + }, |
| 33 | + "consequence": function(R) { |
| 34 | + this.result = false; |
| 35 | + R.stop(); |
| 36 | + } |
| 37 | + } |
28 | 38 |
|
29 | | -5. priority - number which decides the order at which the rule gets applied on the supplied facts. |
| 39 | +Above is a sample rule which has mandatory as well as optional parameters. You can choose to use which all attributes you need to use while defining your rule. Now let look into the attributes one by one. |
30 | 40 |
|
31 | | -6. on - boolean telling whether or not the rule should be considered by the rule engine. |
| 41 | +##### A. condition |
| 42 | +Condition is a function where the user can do the checks on the fact provided. The fact varaiable will be available in `this` context of the condition function. Lets see a sample condition below. |
32 | 43 |
|
33 | | -## Example rule |
| 44 | + "condition": function(R) { |
| 45 | + R.when(this && (this.transactionTotal < 500)); |
| 46 | + } |
34 | 47 |
|
| 48 | +As you can see, the we have to pass an expression on to the `R.when` API. If the expression evaluates to true for a fact, the corresponding consequence will execute. |
35 | 49 |
|
36 | | - { |
37 | | - "name": "transaction minimum", |
38 | | - "description": "blocks transactions below value x", |
39 | | - "priority": 3, |
40 | | - "on":1, |
41 | | - "condition": |
42 | | - function(fact,cb) { |
43 | | - cb(fact && (fact.transactionTotal < 500)); |
44 | | - }, |
45 | | - "consequence": |
46 | | - function(cb) { |
47 | | - console.log("Rule 1 matched for "+this.name+": blocks transactions below value 500. Rejecting payment."); |
48 | | - this.result = false; |
49 | | - this.process = true; |
50 | | - cb(); |
51 | | - } |
| 50 | +Its mandatory to have this field. |
| 51 | + |
| 52 | +##### B. consequnce |
| 53 | +The consequence is the part where the we define what happens when the condition evaluates to true for a particular fact. Just like in condition, fact varaiable will be available in `this` context. You may utilize it to add extra result attributes if needed. |
| 54 | + |
| 55 | + "consequence": function(R) { |
| 56 | + this.result = false; |
| 57 | + R.stop(); |
52 | 58 | } |
| 59 | +In the above example we use an additional parameter `result` to communicate to the code outside the rule engine that the fact was succeeded. Also the Rule API provides a number of functions here to control the flow of the rule engine. They are `R.stop()`, `R.restart()` and `R.next()`. Stop refers to stop processing the rule engine. Restart tells the rule engine to start applying all the rules again to the fact. Next is to instruct the rule engine to continue applying the rest of the rules to the fact before stoping. |
| 60 | + |
| 61 | +Its mandatory to have this field. |
| 62 | + |
| 63 | +##### C. priority |
| 64 | +This field is used to specify the priority of a rule. The rules with higher priority will be applied on the fact first and then followed by lower priority rules. You can have multiple rules with same priority and the engine will not ensure the order in that case. |
| 65 | + |
| 66 | +Its not mandatory to have this field. |
| 67 | + |
| 68 | +##### D. on |
| 69 | +This is field is used to store the state of a rule. This is used to activate and diactivate rules at run time. Rules with `on` set to `false` will not be applied on the facts. |
| 70 | + |
| 71 | +It is not mandatory to have this field. |
| 72 | + |
| 73 | +##### E. add a unique attribute |
| 74 | +It is suggested that you should add a property which can be used as a unique identifier for a rule. Why it is because when you need to dynamically turn on/off or change priority of a rule, you will need a filter to select a rule from the engine via the APIs. That time you may use the unique property as a key for the filter for selection process. |
| 75 | + |
| 76 | +Suppose that in the above example `name` is unique for each rule. Then for changing state or re prioritizing a rule at run time, you may use a filter like `{"name":"transaction minimum"}`. |
53 | 77 |
|
| 78 | +Again its optional to add a unique identifier. You may ignore adding it to your rules if you are not changing rule states at run time. |
54 | 79 |
|
55 | | -## Facts |
56 | 80 |
|
57 | | -Facts are those input json values on which the rule engine applies its rule to obtain results. A fact can have multiple attributes. |
| 81 | +### 2. Defining a Fact |
| 82 | +Facts are those input json values on which the rule engine applies its rule to obtain results. A fact can have multiple attributes as you decide. |
58 | 83 |
|
59 | | -## Example Fact |
| 84 | +Example Fact may look like |
60 | 85 |
|
61 | 86 | { |
62 | 87 | "userIP": "27.3.4.5", |
63 | 88 | "name":"user4", |
64 | | - "eventRiskFactor":8, |
65 | | - "userCredibility":2, |
66 | 89 | "application":"MOB2", |
67 | 90 | "userLoggedIn":true, |
68 | 91 | "transactionTotal":400, |
69 | 92 | "cardType":"Credit Card", |
70 | | - "cardIssuer":"VISA", |
71 | | - |
72 | | - } |
| 93 | + } |
73 | 94 |
|
74 | | -##Usage |
| 95 | +### 3. Using the Rule Engine |
75 | 96 |
|
76 | | -The example below shows how to use the rule engine to apply a sample rule on a specific fact. |
| 97 | +The example below shows how to use the rule engine to apply a sample rule on a specific fact. Rules fed into the rule engine may be as Array of rules or as individual rule objects. |
77 | 98 |
|
78 | 99 | ``` js |
79 | | - |
| 100 | +//import the package |
80 | 101 | var RuleEngine = require('node-rules'); |
81 | 102 |
|
| 103 | +//define the rules |
82 | 104 | var rules = [{ |
83 | | - "name": "transaction minimum", |
84 | | - "description": "blocks transactions below value x", |
85 | | - "priority": 3, |
86 | | - "on":1, |
87 | | - "condition": |
88 | | - function(fact,cb) { |
89 | | - cb(fact && (fact.transactionTotal < 500)); |
90 | | - }, |
91 | | - "consequence": |
92 | | - function(cb) { |
93 | | - console.log("Rule 1 matched for "+this.name+": blocks transactions below value 500. Rejecting payment."); |
94 | | - this.result = false; |
95 | | - this.process = true; |
96 | | - cb(); |
97 | | - } |
98 | | - }]; |
99 | | - |
| 105 | + "condition": function(R) { |
| 106 | + R.when(this && (this.transactionTotal < 500)); |
| 107 | + }, |
| 108 | + "consequence": function(R) { |
| 109 | + this.result = false; |
| 110 | + R.stop(); |
| 111 | + } |
| 112 | +}]; |
| 113 | +/*as you can see above we removed the priority and on properties for this example as they are optional.*/ |
| 114 | + |
| 115 | +//sample fact to run the rules on |
100 | 116 | var fact = { |
101 | | - "userIP": "27.3.4.5", |
102 | | - "name":"user4", |
103 | | - "eventRiskFactor":8, |
104 | | - "userCredibility":2, |
105 | | - "application":"MOB2", |
106 | | - "userLoggedIn":true, |
107 | | - "transactionTotal":400, |
108 | | - "cardType":"Credit Card", |
109 | | - "cardIssuer":"VISA", |
110 | | - |
111 | | - }; |
112 | | - |
| 117 | + "userIP": "27.3.4.5", |
| 118 | + "name":"user4", |
| 119 | + "application":"MOB2", |
| 120 | + "userLoggedIn":true, |
| 121 | + "transactionTotal":400, |
| 122 | + "cardType":"Credit Card", |
| 123 | +}; |
| 124 | + |
| 125 | +//initialize the rule engine |
113 | 126 | var R = new RuleEngine(rules); |
114 | 127 |
|
| 128 | +//Now pass the fact on to the rule engine for results |
115 | 129 | R.execute(fact,function(result){ |
116 | 130 |
|
117 | 131 | if(result.result) |
118 | | - console.log("\n-----Payment Accepted for----\n"); |
| 132 | + console.log("\n-----Payment Accepted----\n"); |
119 | 133 | else |
120 | | - console.log("\n-----Payment Rejected for----\n"); |
121 | | - |
122 | | - console.log(result); |
| 134 | + console.log("\n-----Payment Rejected----\n"); |
123 | 135 |
|
124 | 136 | }); |
125 | 137 | ``` |
126 | 138 |
|
127 | 139 |
|
128 | | - |
129 | 140 | ## Credits |
130 | 141 |
|
131 | | -Both the rules and the facts used in this module are based on the node module [jools](https://github.com/tdegrunt/jools). |
| 142 | +The JSON friendly rule formats used in this module were initially based on the node module [jools](https://github.com/tdegrunt/jools). |
132 | 143 | Its a modified version of jools with a non blocking version of applying the rule engine on the facts. |
133 | 144 | The rule engine logic was been modified sothat the rule executions on separate facts donot block each other. |
0 commit comments