Skip to content

Commit e2347d4

Browse files
author
Cache Hamm
committed
pass rule-results to success and failure events
1 parent a95417c commit e2347d4

6 files changed

Lines changed: 44 additions & 47 deletions

File tree

docs/engine.md

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -128,42 +128,22 @@ the same priority may still emit events, even though the engine has been told to
128128
engine.stop()
129129
```
130130

131-
### engine.on(String event, Function callback) -> Engine
132-
133-
Listens for events emitted as rules are being evaluated. "event" is determined by the [rule event](./rules.md#Events).
134-
135-
```js
136-
let rule = new Rule({
137-
event: {
138-
type: 'my-event',
139-
params: {
140-
customValue: 'my-custom-value'
141-
}
142-
}
143-
})
144-
145-
// whenever rule is evaluated and conditions pass, 'my-event' will trigger
146-
engine.on('my-event', function(params) {
147-
console.log(params) // { customValue: 'my-custom-value' }
148-
})
149-
```
150-
151131
There are two generic event emissions that trigger automatically:
152132

153133
#### ```engine.on('success', cb)```
154134

155-
Fires when *any* rule passes. In this case the callback will receive the entire event object.
135+
Fires when a rule passes. In this case the callback will receive the entire event object.
156136

157137
```js
158-
engine.on('success', function(event, almanac) {
138+
engine.on('success', function(event, almanac, ruleResult) {
159139
})
160140
```
161141

162142
#### ```engine.on('failure', cb)```
163143

164-
Companion to 'success', except fires when any rule fails.
144+
Companion to 'success', except fires when a rule fails.
165145

166146
```js
167-
engine.on('failure', function(rule, almanac) {
147+
engine.on('failure', function(rule, almanac, ruleResult) {
168148
})
169149
```

docs/rules.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,16 @@ See the [fact-comparison](../examples/08-fact-comparison.js) example
221221

222222
Listen for `success` and `failure` events emitted when rule is evaluated.
223223

224-
#### ```rule.on('success', Function(Object event, Almanac almanac))```
224+
#### ```rule.on('success', Function(Object event, Almanac almanac, RuleResult ruleResult))```
225225

226226
```js
227227
// whenever rule is evaluated and the conditions pass, 'success' will trigger
228-
rule.on('success', function(event, almanac) {
228+
rule.on('success', function(event, almanac, ruleResult) {
229229
console.log(event) // { type: 'my-event', params: { id: 1 }
230230
})
231231
```
232232

233-
#### ```rule.on('failure', Function(Object event, Almanac almanac))```
233+
#### ```rule.on('failure', Function(Object event, Almanac almanac, RuleResult ruleResult))```
234234

235235
Companion to `success`, except fires when the rule fails.
236236

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
},
6767
"dependencies": {
6868
"debug": "2.2.0",
69+
"lodash.clonedeep": "4.5.0",
6970
"lodash.isplainobject": "4.0.6",
7071
"object-hash": "1.1.5",
7172
"params": "0.1.1",

src/engine.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,15 @@ class Engine extends EventEmitter {
144144
debug(`engine::run status:${this.status}; skipping remaining rules`)
145145
return
146146
}
147-
return rule.evaluate(almanac).then((rulePasses) => {
148-
debug(`engine::run ruleResult:${rulePasses}`)
149-
if (rulePasses) {
150-
this.emit('success', rule.event, almanac)
151-
this.emit(rule.event.type, rule.event.params, this)
147+
return rule.evaluate(almanac).then((ruleResult) => {
148+
debug(`engine::run ruleResult:${ruleResult.result}`)
149+
if (ruleResult.result) {
150+
this.emit('success', rule.event, almanac, ruleResult)
151+
this.emit(rule.event.type, rule.event.params, this) // DEPRECATED; todo - remove from docs
152152
almanac.factValue('success-events', { event: rule.event })
153+
} else {
154+
this.emit('failure', rule, almanac, ruleResult)
153155
}
154-
if (!rulePasses) this.emit('failure', rule, almanac)
155156
})
156157
}))
157158
}

src/rule.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import params from 'params'
44
import Condition from './condition'
55
import { EventEmitter } from 'events'
6+
import deepClone from 'lodash.clonedeep'
67

78
let debug = require('debug')('json-rules-engine')
89

@@ -124,9 +125,15 @@ class Rule extends EventEmitter {
124125
/**
125126
* Evaluates the rule, starting with the root boolean operator and recursing down
126127
* All evaluation is done within the context of an almanac
127-
* @return {Promise(boolean)} rule evaluation result
128+
* @return {Promise(RuleResult)} rule evaluation result
128129
*/
129130
async evaluate (almanac) {
131+
let ruleResult = {
132+
conditions: deepClone(this.conditions),
133+
event: deepClone(this.event),
134+
priority: deepClone(this.priority)
135+
}
136+
130137
/**
131138
* Evaluates the rule conditions
132139
* @param {Condition} condition - condition to evaluate
@@ -153,11 +160,11 @@ class Rule extends EventEmitter {
153160
else throw err
154161
}
155162
}
156-
163+
condition.result = passes
157164
if (passes) {
158-
this.emit('success', this.event, almanac)
165+
this.emit('success', this.event, almanac, ruleResult)
159166
} else {
160-
this.emit('failure', this.event, almanac)
167+
this.emit('failure', this.event, almanac, ruleResult)
161168
}
162169
return passes
163170
}
@@ -238,10 +245,14 @@ class Rule extends EventEmitter {
238245
return prioritizeAndRun(conditions, 'all')
239246
}
240247

241-
if (this.conditions.any) {
242-
return await any(this.conditions.any)
248+
if (ruleResult.conditions.any) {
249+
let result = await any(ruleResult.conditions.any)
250+
ruleResult.result = result
251+
return ruleResult
243252
} else {
244-
return await all(this.conditions.all)
253+
let result = await all(ruleResult.conditions.all)
254+
ruleResult.result = result
255+
return ruleResult
245256
}
246257
}
247258
}

test/engine-event.test.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ describe('Engine: event', () => {
3030

3131
describe('engine events', () => {
3232
it('passes the event type and params', (done) => {
33-
engine.on('success', function (a, engine) {
33+
engine.on('success', function (a, almanac, ruleResult) {
3434
try {
3535
expect(a).to.eql(event)
36-
expect(engine).to.eql(engine)
36+
expect(almanac).to.be.an.instanceof(Almanac)
37+
expect(ruleResult.result).to.be.true()
38+
expect(ruleResult.conditions.any[0].result).to.be.true()
3739
} catch (e) { return done(e) }
3840
done()
3941
})
@@ -71,7 +73,7 @@ describe('Engine: event', () => {
7173
})
7274
engine.addRule(drinkOrderRule)
7375
return new Promise((resolve, reject) => {
74-
engine.on('success', function (event, almanac) {
76+
engine.on('success', function (event, almanac, ruleResult) {
7577
switch (event.type) {
7678
case 'setDrinkingFlag':
7779
almanac.addRuntimeFact('canOrderDrinks', event.params.canOrderDrinks)
@@ -91,11 +93,12 @@ describe('Engine: event', () => {
9193
it('on-success, it passes the event type and params', (done) => {
9294
let failureSpy = sinon.spy()
9395
let rule = engine.rules[0]
94-
rule.on('success', function (e, a) {
96+
rule.on('success', function (e, almanac, ruleResult) {
9597
try {
9698
expect(e).to.eql(event)
97-
expect(a).to.be.an.instanceof(Almanac)
99+
expect(almanac).to.be.an.instanceof(Almanac)
98100
expect(failureSpy.callCount).to.equal(0)
101+
expect(ruleResult.conditions.any[0].result).to.be.true()
99102
} catch (err) { return done(err) }
100103
done()
101104
})
@@ -106,11 +109,12 @@ describe('Engine: event', () => {
106109
it('on-failure, it passes the event type and params', (done) => {
107110
let successSpy = sinon.spy()
108111
let rule = engine.rules[0]
109-
rule.on('failure', function (e, a) {
112+
rule.on('failure', function (e, almanac, ruleResult) {
110113
try {
111114
expect(e).to.eql(event)
112-
expect(a).to.be.an.instanceof(Almanac)
115+
expect(almanac).to.be.an.instanceof(Almanac)
113116
expect(successSpy.callCount).to.equal(0)
117+
expect(ruleResult.conditions.any[0].result).to.be.false()
114118
} catch (err) { return done(err) }
115119
done()
116120
})

0 commit comments

Comments
 (0)