Skip to content

Commit 3c75cde

Browse files
author
Cache Hamm
committed
ES6 examples; README examples; remove Overview
- Converted all examples to ES6 - Added fuller, more realistic examples to the README - Replaced the basic example with nested-boolean-logic - Ordered examples in order of complexity - Removed the Overview
1 parent cb5e0ab commit 3c75cde

13 files changed

+260
-226
lines changed

Diff for: README.md

+98-14
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,9 @@ 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).
30-
31-
To dive right in, start with the [basic example](./examples/basic.js).
32-
33-
## Example
34-
35-
In basketball, a player who commits five personal fouls over the course of a 40-minute game, or six in a 48-minute game, fouls out.
36-
37-
This example demonstrates an engine for detecting whether an individual player has fouled out.
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).
3830

3931
```js
4032
import { Engine } from 'json-rules-engine'
@@ -79,28 +71,120 @@ engine.addRule({
7971
})
8072

8173
/**
82-
* define the facts
83-
* note: facts may be loaded asynchronously at runtime; see the advanced example below
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
8476
*/
8577
let facts = {
8678
personalFoulCount: 6,
8779
gameDuration: 40
8880
}
8981

90-
// run the engine
82+
// Run the engine to evaluate
9183
engine
9284
.run(facts)
93-
.then(events => { // run() return events with truthy conditions
85+
.then(events => { // run() returns events with truthy conditions
9486
events.map(event => console.log(event.params.message))
9587
})
9688

9789
/*
90+
* Output:
91+
*
9892
* Player has fouled out!
9993
*/
10094
```
10195

10296
Run this example [here](./examples/nested-boolean-logic.js)
10397

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.
105+
106+
```js
107+
import { Engine } from 'json-rules-engine'
108+
109+
// example client for making asynchronous requests to an api, database, etc
110+
import apiClient from './account-api-client'
111+
112+
/**
113+
* Setup a new engine
114+
*/
115+
let engine = new Engine()
116+
117+
/**
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'] }
122+
*/
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+
}
147+
}
148+
}
149+
engine.addRule(microsoftRule)
150+
151+
/**
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.
156+
*/
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+
})
164+
165+
// define fact(s) known at runtime
166+
let facts = { accountId: 'lincoln' }
167+
engine
168+
.run(facts)
169+
.then(function (events) {
170+
console.log(facts.accountId + ' is a ' + events.map(event => event.params.message))
171+
})
172+
.catch(err => console.log(err.stack))
173+
174+
/*
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
179+
*/
180+
```
181+
182+
Run this example [here](./examples/nested-boolean-logic.js)
183+
184+
## Docs
185+
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.
104188

105189
## Persisting Rules
106190

Diff for: docs/engine.md

+2
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])

Diff for: docs/facts.md

+3
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

Diff for: docs/overview.md

-56
This file was deleted.

Diff for: docs/rules.md

+2
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])

Diff for: examples/hello-world.js renamed to examples/01-hello-world.js

+7-7
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

Diff for: examples/nested-boolean-logic.js renamed to examples/02-nested-boolean-logic.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

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

1616
/**
1717
* Setup a new engine

Diff for: examples/03-dynamic-facts.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use strict'
2+
3+
/*
4+
* This example demonstrates computing fact values at runtime, and leveraging the 'path' feature
5+
* to select object properties returned by facts
6+
*
7+
* Usage:
8+
* node ./examples/dynamic-facts.js
9+
*
10+
* For detailed output:
11+
* DEBUG=json-rules-engine node ./examples/dynamic-facts.js
12+
*/
13+
14+
require('colors')
15+
let Engine = require('../dist').Engine
16+
// example client for making asynchronous requests to an api, database, etc
17+
let apiClient = require('./support/account-api-client')
18+
19+
/**
20+
* Setup a new engine
21+
*/
22+
let engine = new Engine()
23+
24+
/**
25+
* Rule for identifying microsoft employees taking pto on christmas
26+
*
27+
* the account-information fact returns:
28+
* { company: 'XYZ', status: 'ABC', ptoDaysTaken: ['YYYY-MM-DD', 'YYYY-MM-DD'] }
29+
*/
30+
let microsoftRule = {
31+
conditions: {
32+
all: [{
33+
fact: 'account-information',
34+
operator: 'equal',
35+
value: 'microsoft',
36+
path: '.company' // access the 'company' property of "account-information"
37+
}, {
38+
fact: 'account-information',
39+
operator: 'in',
40+
value: ['active', 'paid-leave'], // 'status'' can be active or paid-leave
41+
path: '.status' // access the 'status' property of "account-information"
42+
}, {
43+
fact: 'account-information',
44+
operator: 'contains',
45+
value: '2016-12-25',
46+
path: '.ptoDaysTaken' // access the 'ptoDaysTaken' property of "account-information"
47+
}]
48+
},
49+
event: {
50+
type: 'microsoft-christmas-pto',
51+
params: {
52+
message: 'current microsoft employee taking christmas day off'
53+
}
54+
}
55+
}
56+
engine.addRule(microsoftRule)
57+
58+
/**
59+
* 'account-information' fact executes an api call and retrieves account data, feeding the results
60+
* into the engine. The major advantage of this technique is that although there are THREE conditions
61+
* requiring this data, only ONE api call is made. This results in much more efficient runtime performance.
62+
*/
63+
engine.addFact('account-information', function (params, almanac) {
64+
return almanac.factValue('accountId')
65+
.then(accountId => {
66+
return apiClient.getAccountInformation(accountId)
67+
})
68+
})
69+
70+
// define fact(s) known at runtime
71+
let facts = { accountId: 'lincoln' }
72+
engine
73+
.run(facts)
74+
.then(events => {
75+
if (!events.length) return
76+
console.log(facts.accountId + ' is a ' + events.map(event => event.params.message))
77+
})
78+
.catch(err => console.log(err.stack))
79+
80+
/*
81+
* OUTPUT:
82+
*
83+
* loading account information for "lincoln"
84+
* lincoln is a current microsoft employee taking christmas day off
85+
*
86+
* NOTES:
87+
*
88+
* - Notice that although all 3 conditions required data from the "account-information" fact,
89+
* the account-information api call is executed only ONCE. This is because fact results are
90+
* cached by default, increasing performance and lowering network requests.
91+
*
92+
* - See the 'fact' docs on how to disable fact caching
93+
*/

0 commit comments

Comments
 (0)