Skip to content

Commit 1c73ff9

Browse files
author
Cache Hamm
authored
Merge pull request #12 from CacheControl/improve-docs
Improve docs
2 parents 4ef4d7e + 3c75cde commit 1c73ff9

13 files changed

Lines changed: 371 additions & 245 deletions

README.md

Lines changed: 134 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,67 +24,167 @@ A rules engine expressed in JSON
2424
$ npm install json-rules-engine
2525
```
2626

27-
## Documentation
27+
## Basic Example
2828

29-
It's best to start with the [overview](./docs/overview.md) to understand the terminology. Next, see the [walkthrough](./docs/overview.md) and try out some [examples](./examples).
29+
This example demonstrates an engine for detecting whether a basketball player has fouled out (a player who commits five personal fouls over the course of a 40-minute game, or six in a 48-minute game, fouls out).
3030

31-
To dive right in, start with the [basic example](./examples/basic.js).
31+
```js
32+
import { Engine } from 'json-rules-engine'
33+
34+
/**
35+
* Setup a new engine
36+
*/
37+
let engine = new Engine()
38+
39+
// define a rule for detecting the player has exceeded foul limits. Foul out any player who:
40+
// (has committed 5 fouls AND game is 40 minutes) OR (has committed 6 fouls AND game is 48 minutes)
41+
engine.addRule({
42+
conditions: {
43+
any: [{
44+
all: [{
45+
fact: 'gameDuration',
46+
operator: 'equal',
47+
value: 40
48+
}, {
49+
fact: 'personalFoulCount',
50+
operator: 'greaterThanInclusive',
51+
value: 5
52+
}]
53+
}, {
54+
all: [{
55+
fact: 'gameDuration',
56+
operator: 'equal',
57+
value: 48
58+
}, {
59+
fact: 'personalFoulCount',
60+
operator: 'greaterThanInclusive',
61+
value: 6
62+
}]
63+
}]
64+
},
65+
event: { // define the event to fire when the conditions evaluate truthy
66+
type: 'fouledOut',
67+
params: {
68+
message: 'Player has fouled out!'
69+
}
70+
}
71+
})
72+
73+
/**
74+
* Define facts the engine will use to evaluate the conditions above.
75+
* Facts may also be loaded asynchronously at runtime; see the advanced example below
76+
*/
77+
let facts = {
78+
personalFoulCount: 6,
79+
gameDuration: 40
80+
}
81+
82+
// Run the engine to evaluate
83+
engine
84+
.run(facts)
85+
.then(events => { // run() returns events with truthy conditions
86+
events.map(event => console.log(event.params.message))
87+
})
88+
89+
/*
90+
* Output:
91+
*
92+
* Player has fouled out!
93+
*/
94+
```
95+
96+
Run this example [here](./examples/nested-boolean-logic.js)
97+
98+
## Advanced Example
99+
100+
This example demonstates an engine for identifying employees who work for Microsoft and are taking Christmas day off.
101+
102+
This demonstrates an engine which uses asynchronous fact data.
103+
Fact information is loaded via API call during runtime, and the results are cached and recycled for all 3 conditions.
104+
It also demonstates use of the condition _path_ feature to reference properties of objects returned by facts.
32105

33-
## Hello World
34106
```js
35107
import { Engine } from 'json-rules-engine'
36-
import { Rule } from 'json-rules-engine'
108+
109+
// example client for making asynchronous requests to an api, database, etc
110+
import apiClient from './account-api-client'
37111

38112
/**
39113
* Setup a new engine
40114
*/
41115
let engine = new Engine()
42116

43117
/**
44-
* Create a rule
118+
* Rule for identifying microsoft employees taking pto on christmas
119+
*
120+
* the account-information fact returns:
121+
* { company: 'XYZ', status: 'ABC', ptoDaysTaken: ['YYYY-MM-DD', 'YYYY-MM-DD'] }
45122
*/
46-
let rule = new Rule()
47-
48-
// define the 'conditions' for when "hello world" should display
49-
rule.setConditions({
50-
all: [{
51-
fact: 'displayMessage',
52-
operator: 'equal',
53-
value: true
54-
}]
55-
})
56-
// define the 'event' that will fire when the condition evaluates truthy
57-
rule.setEvent({
58-
type: 'message',
59-
params: {
60-
data: 'hello-world!'
123+
let microsoftRule = {
124+
conditions: {
125+
all: [{
126+
fact: 'account-information',
127+
operator: 'equal',
128+
value: 'microsoft',
129+
path: '.company' // access the 'company' property of "account-information"
130+
}, {
131+
fact: 'account-information',
132+
operator: 'in',
133+
value: ['active', 'paid-leave'], // 'status' can be active or paid-leave
134+
path: '.status' // access the 'status' property of "account-information"
135+
}, {
136+
fact: 'account-information',
137+
operator: 'contains', // the 'ptoDaysTaken' property (an array) must contain '2016-12-25'
138+
value: '2016-12-25',
139+
path: '.ptoDaysTaken' // access the 'ptoDaysTaken' property of "account-information"
140+
}]
141+
},
142+
event: {
143+
type: 'microsoft-christmas-pto',
144+
params: {
145+
message: 'current microsoft employee taking christmas day off'
146+
}
61147
}
62-
})
63-
// add rule to engine
64-
engine.addRule(rule)
148+
}
149+
engine.addRule(microsoftRule)
65150

66151
/**
67-
* Pass initial values into the engine.
68-
* Fact values do NOT need to be known at engine runtime; see the
69-
* examples for how to pull in data asynchronously throughout a run()
152+
* 'account-information' fact executes an api call and retrieves account data, feeding the results
153+
* into the engine. The major advantage of this technique is that although there are THREE conditions
154+
* requiring this data, only ONE api call is made. This results in much more efficient runtime performance
155+
* and fewer network requests.
70156
*/
71-
let facts = { displayMessage: true }
157+
engine.addFact('account-information', function (params, almanac) {
158+
console.log('loading account information...')
159+
return almanac.factValue('accountId')
160+
.then((accountId) => {
161+
return apiClient.getAccountInformation(accountId)
162+
})
163+
})
72164

73-
// run the engine
165+
// define fact(s) known at runtime
166+
let facts = { accountId: 'lincoln' }
74167
engine
75168
.run(facts)
76-
.then(triggeredEvents => { // run() return events with truthy conditions
77-
triggeredEvents.map(event => console.log(event.params.data))
169+
.then(function (events) {
170+
console.log(facts.accountId + ' is a ' + events.map(event => event.params.message))
78171
})
79-
.catch(console.log)
172+
.catch(err => console.log(err.stack))
80173

81174
/*
82-
* hello-world!
175+
* OUTPUT:
176+
*
177+
* loading account information... // <-- API call is made ONCE and results recycled for all 3 conditions
178+
* lincoln is a current microsoft employee taking christmas day off
83179
*/
84180
```
85181

86-
[Try it out!](https://tonicdev.com/cachecontrol/json-rules-engine.hello-world)
182+
Run this example [here](./examples/nested-boolean-logic.js)
183+
184+
## Docs
87185

186+
The examples above provide a simple demonstrations of what `json-rules-engine` can do. To learn more about the advanced features and techniques,
187+
see the [docs](./docs) and read through the [examples](./examples). There is also a [walkthrough](./docs/walkthrough.md) available.
88188

89189
## Persisting Rules
90190

docs/engine.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Engine
22

3+
The Engine stores and executes rules, emits events, and maintains state.
4+
35
## Methods
46

57
### constructor([Array rules])

docs/facts.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Facts
22

3+
Facts are methods or constants registered with the engine prior to runtime and referenced within rule conditions. Each fact method should be a pure function that may return a computed value or promise.
4+
As rule conditions are evaluated during runtime, they retrieve fact values dynamically and use the condition _operator_ to compare the fact result with the condition _value_.
5+
36
## Methods
47

58
### constructor(String id, Constant|Function, [Object options]) -> instance

docs/overview.md

Lines changed: 0 additions & 56 deletions
This file was deleted.

docs/rules.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Rules
22

3+
Rules contain a set of _conditions_ and a single _event_. When the engine is run, each rule condition is evaluated. If the results are truthy, the rule's _event_ is triggered.
4+
35
## Methods
46

57
### constructor([Object options|String json])
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@
1111
*/
1212

1313
require('colors')
14-
var Engine = require('../dist').Engine
15-
var Rule = require('../dist').Rule
14+
let Engine = require('../dist').Engine
15+
let Rule = require('../dist').Rule
1616

1717
/**
1818
* Setup a new engine
1919
*/
20-
var engine = new Engine()
20+
let engine = new Engine()
2121

2222
/**
2323
* Create a rule
2424
*/
25-
var rule = new Rule()
25+
let rule = new Rule()
2626

2727
// define the 'conditions' for when "hello world" should display
2828
rule.setConditions({
@@ -47,13 +47,13 @@ engine.addRule(rule)
4747
* Fact values do NOT need to be known at engine runtime; see the
4848
* examples for how to pull in data asynchronously as the engine runs
4949
*/
50-
var facts = { displayMessage: true }
50+
let facts = { displayMessage: true }
5151

5252
// run the engine
5353
engine
5454
.run(facts)
55-
.then(function (triggeredEvents) { // engine returns a list of events with truthy conditions
56-
triggeredEvents.map(function (event) { console.log(event.params.data.green) })
55+
.then(triggeredEvents => { // engine returns a list of events with truthy conditions
56+
triggeredEvents.map(event => console.log(event.params.data.green))
5757
})
5858
.catch(console.log)
5959

0 commit comments

Comments
 (0)