Skip to content

Commit 957d577

Browse files
authored
Merge pull request #265 from Workiz/remove_update_rule_by_key
2 parents d27e402 + 0dafaab commit 957d577

File tree

4 files changed

+96
-18
lines changed

4 files changed

+96
-18
lines changed

docs/engine.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ The Engine stores and executes rules, emits events, and maintains state.
88
* [engine.addFact(String id, Function [definitionFunc], Object [options])](#engineaddfactstring-id-function-definitionfunc-object-options)
99
* [engine.removeFact(String id)](#engineremovefactstring-id)
1010
* [engine.addRule(Rule instance|Object options)](#engineaddrulerule-instanceobject-options)
11-
* [engine.removeRule(Rule instance)](#engineremoverulerule-instance)
11+
* [engine.updateRule(Rule instance|Object options)](#engineupdaterulerule-instanceobject-options)
12+
* [engine.removeRule(Rule instance | String ruleId)](#engineremoverulerule-instance)
1213
* [engine.addOperator(String operatorName, Function evaluateFunc(factValue, jsonValue))](#engineaddoperatorstring-operatorname-function-evaluatefuncfactvalue-jsonvalue)
1314
* [engine.removeOperator(String operatorName)](#engineremoveoperatorstring-operatorname)
1415
* [engine.run([Object facts], [Object options]) -> Promise ({ events: [], failureEvents: [], almanac: Almanac, results: [], failureResults: []})](#enginerunobject-facts-object-options---promise--events--failureevents--almanac-almanac-results--failureresults-)
@@ -73,6 +74,7 @@ engine.removeFact('speed-of-light')
7374
### engine.addRule(Rule instance|Object options)
7475

7576
Adds a rule to the engine. The engine will execute the rule upon the next ```run()```
77+
if the rule doesn't have value set at rule.id, a random string will be set instead.
7678

7779
```js
7880
let Rule = require('json-rules-engine').Rule
@@ -102,6 +104,24 @@ engine.addRule(rule)
102104

103105
//remove it
104106
engine.removeRule(rule)
107+
//or
108+
engine.removeRule(rule.id)
109+
```
110+
111+
### engine.updateRule(Rule instance|Object options)
112+
113+
Updates a rule in the engine.
114+
115+
```javascript
116+
// adds a rule
117+
let rule = new Rule()
118+
engine.addRule(rule)
119+
120+
// change rule condition
121+
rule.conditions.all = []
122+
123+
//update it in the engine
124+
engine.updateRule(rule)
105125
```
106126

107127
### engine.addOperator(String operatorName, Function evaluateFunc(factValue, jsonValue))

src/engine.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,27 +47,45 @@ class Engine extends EventEmitter {
4747
} else {
4848
if (!Object.prototype.hasOwnProperty.call(properties, 'event')) throw new Error('Engine: addRule() argument requires "event" property')
4949
if (!Object.prototype.hasOwnProperty.call(properties, 'conditions')) throw new Error('Engine: addRule() argument requires "conditions" property')
50-
5150
rule = new Rule(properties)
5251
}
5352
rule.setEngine(this)
54-
5553
this.rules.push(rule)
5654
this.prioritizedRules = null
5755
return this
5856
}
5957

6058
/**
61-
* Remove a rule from the engine
59+
* update a rule in the engine
6260
* @param {object|Rule} rule - rule definition. Must be a instance of Rule
6361
*/
64-
removeRule (rule) {
65-
if ((rule instanceof Rule) === false) throw new Error('Engine: removeRule() rule must be a instance of Rule')
62+
updateRule (rule) {
63+
const ruleIndex = this.rules.findIndex(ruleInEngine => (ruleInEngine.id === rule.id) && rule.id)
64+
if (ruleIndex > -1) {
65+
this.rules.splice(ruleIndex, 1)
66+
this.addRule(rule)
67+
this.prioritizedRules = null
68+
} else {
69+
throw new Error('Engine: updateRule() rule not found')
70+
}
71+
}
6672

67-
const index = this.rules.indexOf(rule)
68-
if (index === -1) return false
69-
this.prioritizedRules = null
70-
return Boolean(this.rules.splice(index, 1).length)
73+
/**
74+
* Remove a rule from the engine
75+
* @param {object|Rule|string} rule - rule definition. Must be a instance of Rule
76+
*/
77+
removeRule (rule) {
78+
if (!(rule instanceof Rule)) {
79+
this.rules = this.rules.filter(ruleInEngine => ruleInEngine.id !== rule)
80+
this.prioritizedRules = null
81+
return Boolean(this.rules.length)
82+
} else {
83+
if (!rule) return false
84+
const index = this.rules.indexOf(rule)
85+
if (index === -1) return false
86+
this.prioritizedRules = null
87+
return Boolean(this.rules.splice(index, 1).length)
88+
}
7189
}
7290

7391
/**

src/rule.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Rule extends EventEmitter {
1515
* @param {string} options.event.params - parameters to pass to the event listener
1616
* @param {Object} options.conditions - conditions to evaluate when processing this rule
1717
* @param {any} options.name - identifier for a particular rule, particularly valuable in RuleResult output
18+
* @param {any} options.id - identifier for a particular rule, particularly valuable in RuleResult output
1819
* @return {Rule} instance
1920
*/
2021
constructor (options) {
@@ -35,6 +36,8 @@ class Rule extends EventEmitter {
3536
this.setName(options.name)
3637
}
3738

39+
this.setId(options ? options.id : null)
40+
3841
const priority = (options && options.priority) || 1
3942
this.setPriority(priority)
4043

@@ -53,6 +56,19 @@ class Rule extends EventEmitter {
5356
return this
5457
}
5558

59+
/**
60+
* Sets the unique id of the rule
61+
* @param {any} id - any truthy input
62+
*/
63+
setId (id) {
64+
if (!id) {
65+
this.id = '_' + Math.random().toString(36).substr(2, 9)
66+
} else {
67+
this.id = id
68+
}
69+
return this
70+
}
71+
5672
/**
5773
* Sets the name of the rule
5874
* @param {any} name - any truthy input and zero is allowed

test/engine.test.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,30 @@ describe('Engine', () => {
8484
})
8585
})
8686

87+
describe('updateRule()', () => {
88+
it('updates rule', () => {
89+
const rule = new Rule(factories.rule())
90+
engine.addRule(rule)
91+
expect(engine.rules[0].conditions.all.length).to.equal(2)
92+
rule.conditions = { all: [] }
93+
engine.updateRule(rule)
94+
expect(engine.rules[0].conditions.all.length).to.equal(0)
95+
})
96+
it('should generate id for rule if not provided', () => {
97+
const rule = new Rule(factories.rule())
98+
expect(rule.id).to.not.equal(null)
99+
expect(rule.id).to.not.equal(undefined)
100+
})
101+
it('should throw error if rule not found', () => {
102+
const rule1 = new Rule(factories.rule())
103+
engine.addRule(rule1)
104+
const rule2 = new Rule(factories.rule())
105+
expect(() => {
106+
engine.updateRule(rule2)
107+
}).to.throw(/Engine: updateRule\(\) rule not found/)
108+
})
109+
})
110+
87111
describe('removeRule()', () => {
88112
describe('rule instance', () => {
89113
it('removes the rule', () => {
@@ -96,14 +120,6 @@ describe('Engine', () => {
96120
})
97121
})
98122

99-
describe('required fields', () => {
100-
it('.conditions', () => {
101-
expect(() => {
102-
engine.removeRule([])
103-
}).to.throw(/Engine: removeRule\(\) rule must be a instance of Rule/)
104-
})
105-
})
106-
107123
it('can only remove added rules', () => {
108124
expect(engine.rules.length).to.equal(0)
109125
const rule = new Rule(factories.rule())
@@ -118,6 +134,14 @@ describe('Engine', () => {
118134
engine.removeRule(rule)
119135
expect(engine.prioritizedRules).to.equal(null)
120136
})
137+
it('removes rule based on ruleKey', () => {
138+
const rule = new Rule(factories.rule())
139+
engine.addRule(rule)
140+
expect(engine.rules.length).to.equal(1)
141+
engine.removeRule(rule.id)
142+
expect(engine.rules.length).to.equal(0)
143+
expect(engine.prioritizedRules).to.equal(null)
144+
})
121145
})
122146

123147
describe('addOperator()', () => {

0 commit comments

Comments
 (0)