@@ -7,6 +7,8 @@ import Almanac from './almanac'
7
7
import EventEmitter from 'eventemitter2'
8
8
import defaultOperators from './engine-default-operators'
9
9
import debug from './debug'
10
+ import { interpolateDeep , needsInterpolation , defaultInterpolation } from './interpolate' ;
11
+ import { defaultPathResolver } from './resolver' ;
10
12
11
13
export const READY = 'READY'
12
14
export const RUNNING = 'RUNNING'
@@ -21,12 +23,16 @@ class Engine extends EventEmitter {
21
23
super ( )
22
24
this . rules = [ ]
23
25
this . allowUndefinedFacts = options . allowUndefinedFacts || false
24
- this . pathResolver = options . pathResolver
26
+ this . pathResolver = options . pathResolver || defaultPathResolver
27
+ const interpolation = options . interpolation || defaultInterpolation ;
28
+ this . interpolation = typeof interpolation === 'string' ? new RegExp ( interpolation ) : interpolation ;
25
29
this . operators = new Map ( )
26
30
this . facts = new Map ( )
27
31
this . status = READY
28
32
rules . map ( r => this . addRule ( r ) )
29
33
defaultOperators . map ( o => this . addOperator ( o ) )
34
+
35
+ this . interpolatableRules = new Set ( ) ;
30
36
}
31
37
32
38
/**
@@ -49,6 +55,7 @@ class Engine extends EventEmitter {
49
55
if ( ! Object . prototype . hasOwnProperty . call ( properties , 'conditions' ) ) throw new Error ( 'Engine: addRule() argument requires "conditions" property' )
50
56
rule = new Rule ( properties )
51
57
}
58
+ if ( needsInterpolation ( rule , this . interpolation ) ) this . interpolatableRules . add ( rule ) ;
52
59
rule . setEngine ( this )
53
60
this . rules . push ( rule )
54
61
this . prioritizedRules = null
@@ -62,7 +69,8 @@ class Engine extends EventEmitter {
62
69
updateRule ( rule ) {
63
70
const ruleIndex = this . rules . findIndex ( ruleInEngine => ruleInEngine . name === rule . name )
64
71
if ( ruleIndex > - 1 ) {
65
- this . rules . splice ( ruleIndex , 1 )
72
+ const [ old ] = this . rules . splice ( ruleIndex , 1 )
73
+ this . interpolatableRules . delete ( old ) ;
66
74
this . addRule ( rule )
67
75
this . prioritizedRules = null
68
76
} else {
@@ -75,21 +83,25 @@ class Engine extends EventEmitter {
75
83
* @param {object|Rule|string } rule - rule definition. Must be a instance of Rule
76
84
*/
77
85
removeRule ( rule ) {
78
- let ruleRemoved = false
86
+ let theRemovedRule ;
79
87
if ( ! ( rule instanceof Rule ) ) {
80
- const filteredRules = this . rules . filter ( ruleInEngine => ruleInEngine . name !== rule )
81
- ruleRemoved = filteredRules . length !== this . rules . length
88
+ const filteredRules = this . rules . filter ( ruleInEngine => {
89
+ const isRule = ruleInEngine . name === rule ;
90
+ if ( ! theRemovedRule && isRule ) theRemovedRule = ruleInEngine ;
91
+ return ! isRule ;
92
+ } ) ;
82
93
this . rules = filteredRules
83
94
} else {
84
95
const index = this . rules . indexOf ( rule )
85
96
if ( index > - 1 ) {
86
- ruleRemoved = Boolean ( this . rules . splice ( index , 1 ) . length )
97
+ theRemovedRule = this . rules . splice ( index , 1 ) [ 0 ] ;
87
98
}
88
99
}
89
- if ( ruleRemoved ) {
100
+ if ( theRemovedRule ) {
90
101
this . prioritizedRules = null
102
+ this . interpolatableRules . delete ( theRemovedRule ) ;
91
103
}
92
- return ruleRemoved
104
+ return Boolean ( theRemovedRule ) ;
93
105
}
94
106
95
107
/**
@@ -211,6 +223,7 @@ class Engine extends EventEmitter {
211
223
debug ( `engine::run status:${ this . status } ; skipping remaining rules` )
212
224
return Promise . resolve ( )
213
225
}
226
+
214
227
return rule . evaluate ( almanac ) . then ( ( ruleResult ) => {
215
228
debug ( `engine::run ruleResult:${ ruleResult . result } ` )
216
229
almanac . addResult ( ruleResult )
@@ -240,12 +253,28 @@ class Engine extends EventEmitter {
240
253
pathResolver : this . pathResolver
241
254
}
242
255
const almanac = new Almanac ( this . facts , runtimeFacts , almanacOptions )
243
- const orderedSets = this . prioritizeRules ( )
256
+ const orderedSets = this . prioritizeRules ( ) ;
257
+
244
258
let cursor = Promise . resolve ( )
245
259
// for each rule set, evaluate in parallel,
246
260
// before proceeding to the next priority set.
247
261
return new Promise ( ( resolve , reject ) => {
248
262
orderedSets . map ( ( set ) => {
263
+ set = set . map ( rule => {
264
+ if ( ! this . interpolatableRules . has ( rule ) ) return rule ;
265
+ rule = new Rule (
266
+ interpolateDeep (
267
+ rule . toJSON ( true ) ,
268
+ runtimeFacts ,
269
+ this . interpolation ,
270
+ this . pathResolver
271
+ )
272
+ ) ;
273
+ rule . setEngine ( this ) ;
274
+ return rule ;
275
+ } ) ;
276
+
277
+
249
278
cursor = cursor . then ( ( ) => {
250
279
return this . evaluateRules ( set , almanac )
251
280
} ) . catch ( reject )
0 commit comments