| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent fc9944e commit ca4b637
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -137,7 +137,7 @@ var Condition = function () { | |||
| 137 | 137 | key: 'evaluate', | |
| 138 | 138 | value: function () { | |
| 139 | 139 | var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(almanac, operatorMap) { | |
| 140 | - var op, rightHandSideValue, leftHandSideValue, evaluationResult; | ||
| 140 | + var op, rightHandSideValue, leftHandSideValue, result; | ||
| 141 | 141 | return regeneratorRuntime.wrap(function _callee2$(_context2) { | |
| 142 | 142 | while (1) { | |
| 143 | 143 | switch (_context2.prev = _context2.next) { | |
@@ -186,10 +186,10 @@ var Condition = function () { | |||
| 186 | 186 | ||
| 187 | 187 | case 14: | |
| 188 | 188 | leftHandSideValue = _context2.sent; | |
| 189 | - evaluationResult = op.evaluate(leftHandSideValue, rightHandSideValue); | ||
| 189 | + result = op.evaluate(leftHandSideValue, rightHandSideValue); | ||
| 190 | 190 | ||
| 191 | - debug('condition::evaluate <' + leftHandSideValue + ' ' + this.operator + ' ' + rightHandSideValue + '?> (' + evaluationResult + ')'); | ||
| 192 | - return _context2.abrupt('return', evaluationResult); | ||
| 191 | + debug('condition::evaluate <' + leftHandSideValue + ' ' + this.operator + ' ' + rightHandSideValue + '?> (' + result + ')'); | ||
| 192 | + return _context2.abrupt('return', { result: result, leftHandSideValue: leftHandSideValue, rightHandSideValue: rightHandSideValue, operator: this.operator }); | ||
| 193 | 193 | ||
| 194 | 194 | case 18: | |
| 195 | 195 | case 'end': | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -224,14 +224,14 @@ var Engine = function (_EventEmitter) { | |||
| 224 | 224 | debug('engine::run status:' + _this2.status + '; skipping remaining rules'); | |
| 225 | 225 | return; | |
| 226 | 226 | } | |
| 227 | - return rule.evaluate(almanac).then(function (rulePasses) { | ||
| 228 | - debug('engine::run ruleResult:' + rulePasses); | ||
| 229 | - if (rulePasses) { | ||
| 230 | - _this2.emit('success', rule.event, almanac); | ||
| 231 | - _this2.emit(rule.event.type, rule.event.params, _this2); | ||
| 227 | + return rule.evaluate(almanac).then(function (ruleResult) { | ||
| 228 | + debug('engine::run ruleResult:' + ruleResult.result); | ||
| 229 | + if (ruleResult.result) { | ||
| 230 | + _this2.emit('success', rule.event, almanac, ruleResult); | ||
| 232 | 231 | almanac.factValue('success-events', { event: rule.event }); | |
| 232 | + } else { | ||
| 233 | + _this2.emit('failure', rule.event, almanac, ruleResult); | ||
| 233 | 234 | } | |
| 234 | - if (!rulePasses) _this2.emit('failure', rule, almanac); | ||
| 235 | 235 | }); | |
| 236 | 236 | }))); | |
| 237 | 237 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,6 +16,10 @@ var _condition2 = _interopRequireDefault(_condition); | |||
| 16 | 16 | ||
| 17 | 17 | var _events = require('events'); | |
| 18 | 18 | ||
| 19 | + var _lodash = require('lodash.clonedeep'); | ||
| 20 | + | ||
| 21 | + var _lodash2 = _interopRequireDefault(_lodash); | ||
| 22 | + | ||
| 19 | 23 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
| 20 | 24 | ||
| 21 | 25 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | |
@@ -175,7 +179,7 @@ var Rule = function (_EventEmitter) { | |||
| 175 | 179 | /** | |
| 176 | 180 | * Evaluates the rule, starting with the root boolean operator and recursing down | |
| 177 | 181 | * All evaluation is done within the context of an almanac | |
| 178 | - * @return {Promise(boolean)} rule evaluation result | ||
| 182 | + * @return {Promise(RuleResult)} rule evaluation result | ||
| 179 | 183 | */ | |
| 180 | 184 | ||
| 181 | 185 | }, { | |
@@ -184,19 +188,27 @@ var Rule = function (_EventEmitter) { | |||
| 184 | 188 | var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee6(almanac) { | |
| 185 | 189 | var _this3 = this; | |
| 186 | 190 | ||
| 187 | - var evaluateCondition, evaluateConditions, prioritizeAndRun, any, all; | ||
| 191 | + var ruleResult, evaluateCondition, evaluateConditions, prioritizeAndRun, any, all, processResult, result, _result; | ||
| 192 | + | ||
| 188 | 193 | return regeneratorRuntime.wrap(function _callee6$(_context6) { | |
| 189 | 194 | while (1) { | |
| 190 | 195 | switch (_context6.prev = _context6.next) { | |
| 191 | 196 | case 0: | |
| 197 | + ruleResult = { | ||
| 198 | + conditions: (0, _lodash2.default)(this.conditions), | ||
| 199 | + event: (0, _lodash2.default)(this.event), | ||
| 200 | + priority: (0, _lodash2.default)(this.priority) | ||
| 201 | + }; | ||
| 202 | + | ||
| 192 | 203 | /** | |
| 193 | 204 | * Evaluates the rule conditions | |
| 194 | 205 | * @param {Condition} condition - condition to evaluate | |
| 195 | 206 | * @return {Promise(true|false)} - resolves with the result of the condition evaluation | |
| 196 | 207 | */ | |
| 208 | + | ||
| 197 | 209 | evaluateCondition = function () { | |
| 198 | 210 | var _ref2 = _asyncToGenerator(regeneratorRuntime.mark(function _callee(condition) { | |
| 199 | - var comparisonValue, passes, subConditions; | ||
| 211 | + var comparisonValue, passes, subConditions, evaluationResult; | ||
| 200 | 212 | return regeneratorRuntime.wrap(function _callee$(_context) { | |
| 201 | 213 | while (1) { | |
| 202 | 214 | switch (_context.prev = _context.next) { | |
@@ -234,7 +246,7 @@ var Rule = function (_EventEmitter) { | |||
| 234 | 246 | case 13: | |
| 235 | 247 | // for booleans, rule passing is determined by the all/any result | |
| 236 | 248 | passes = comparisonValue === true; | |
| 237 | - _context.next = 29; | ||
| 249 | + _context.next = 31; | ||
| 238 | 250 | break; | |
| 239 | 251 | ||
| 240 | 252 | case 16: | |
@@ -243,41 +255,39 @@ var Rule = function (_EventEmitter) { | |||
| 243 | 255 | return condition.evaluate(almanac, _this3.engine.operators, comparisonValue); | |
| 244 | 256 | ||
| 245 | 257 | case 19: | |
| 246 | - passes = _context.sent; | ||
| 247 | - _context.next = 29; | ||
| 258 | + evaluationResult = _context.sent; | ||
| 259 | + | ||
| 260 | + passes = evaluationResult.result; | ||
| 261 | + condition.factResult = evaluationResult.leftHandSideValue; | ||
| 262 | + _context.next = 31; | ||
| 248 | 263 | break; | |
| 249 | 264 | ||
| 250 | - case 22: | ||
| 251 | - _context.prev = 22; | ||
| 265 | + case 24: | ||
| 266 | + _context.prev = 24; | ||
| 252 | 267 | _context.t0 = _context['catch'](16); | |
| 253 | 268 | ||
| 254 | 269 | if (!(_this3.engine.allowUndefinedFacts && _context.t0.code === 'UNDEFINED_FACT')) { | |
| 255 | - _context.next = 28; | ||
| 270 | + _context.next = 30; | ||
| 256 | 271 | break; | |
| 257 | 272 | } | |
| 258 | 273 | ||
| 259 | 274 | passes = false; | |
| 260 | - _context.next = 29; | ||
| 275 | + _context.next = 31; | ||
| 261 | 276 | break; | |
| 262 | 277 | ||
| 263 | - case 28: | ||
| 278 | + case 30: | ||
| 264 | 279 | throw _context.t0; | |
| 265 | 280 | ||
| 266 | - case 29: | ||
| 267 | - | ||
| 268 | - if (passes) { | ||
| 269 | - _this3.emit('success', _this3.event, almanac); | ||
| 270 | - } else { | ||
| 271 | - _this3.emit('failure', _this3.event, almanac); | ||
| 272 | - } | ||
| 281 | + case 31: | ||
| 282 | + condition.result = passes; | ||
| 273 | 283 | return _context.abrupt('return', passes); | |
| 274 | 284 | ||
| 275 | - case 31: | ||
| 285 | + case 33: | ||
| 276 | 286 | case 'end': | |
| 277 | 287 | return _context.stop(); | |
| 278 | 288 | } | |
| 279 | 289 | } | |
| 280 | - }, _callee, _this3, [[16, 22]]); | ||
| 290 | + }, _callee, _this3, [[16, 24]]); | ||
| 281 | 291 | })); | |
| 282 | 292 | ||
| 283 | 293 | return function evaluateCondition(_x3) { | |
@@ -453,25 +463,39 @@ var Rule = function (_EventEmitter) { | |||
| 453 | 463 | }; | |
| 454 | 464 | }(); | |
| 455 | 465 | ||
| 456 | - if (!this.conditions.any) { | ||
| 457 | - _context6.next = 11; | ||
| 466 | + /** | ||
| 467 | + * Emits based on rule evaluation result, and decorates ruleResult with 'result' property | ||
| 468 | + * @param {Boolean} result | ||
| 469 | + */ | ||
| 470 | + | ||
| 471 | + | ||
| 472 | + processResult = function processResult(result) { | ||
| 473 | + ruleResult.result = result; | ||
| 474 | + if (result) _this3.emit('success', ruleResult.event, almanac, ruleResult);else _this3.emit('failure', ruleResult.event, almanac, ruleResult); | ||
| 475 | + return ruleResult; | ||
| 476 | + }; | ||
| 477 | + | ||
| 478 | + if (!ruleResult.conditions.any) { | ||
| 479 | + _context6.next = 14; | ||
| 458 | 480 | break; | |
| 459 | 481 | } | |
| 460 | 482 | ||
| 461 | - _context6.next = 8; | ||
| 462 | - return any(this.conditions.any); | ||
| 483 | + _context6.next = 10; | ||
| 484 | + return any(ruleResult.conditions.any); | ||
| 463 | 485 | ||
| 464 | - case 8: | ||
| 465 | - return _context6.abrupt('return', _context6.sent); | ||
| 486 | + case 10: | ||
| 487 | + result = _context6.sent; | ||
| 488 | + return _context6.abrupt('return', processResult(result)); | ||
| 466 | 489 | ||
| 467 | - case 11: | ||
| 468 | - _context6.next = 13; | ||
| 469 | - return all(this.conditions.all); | ||
| 490 | + case 14: | ||
| 491 | + _context6.next = 16; | ||
| 492 | + return all(ruleResult.conditions.all); | ||
| 470 | 493 | ||
| 471 | - case 13: | ||
| 472 | - return _context6.abrupt('return', _context6.sent); | ||
| 494 | + case 16: | ||
| 495 | + _result = _context6.sent; | ||
| 496 | + return _context6.abrupt('return', processResult(_result)); | ||
| 473 | 497 | ||
| 474 | - case 14: | ||
| 498 | + case 18: | ||
| 475 | 499 | case 'end': | |
| 476 | 500 | return _context6.stop(); | |
| 477 | 501 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -69,7 +69,6 @@ | |||
| 69 | 69 | "lodash.clonedeep": "4.5.0", | |
| 70 | 70 | "lodash.isplainobject": "4.0.6", | |
| 71 | 71 | "object-hash": "1.1.5", | |
| 72 | - "params": "0.1.1", | ||
| 73 | 72 | "selectn": "1.1.1" | |
| 74 | 73 | } | |
| 75 | 74 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,11 +1,11 @@ | |||
| 1 | 1 | 'use strict' | |
| 2 | 2 | ||
| 3 | - let params = require('params') | ||
| 4 | 3 | let debug = require('debug')('json-rules-engine') | |
| 5 | 4 | let isPlainObject = require('lodash.isplainobject') | |
| 6 | 5 | ||
| 7 | 6 | export default class Condition { | |
| 8 | 7 | constructor (properties) { | |
| 8 | + if (!properties) throw new Error('Condition: constructor options required') | ||
| 9 | 9 | let booleanOperator = Condition.booleanOperator(properties) | |
| 10 | 10 | Object.assign(this, properties) | |
| 11 | 11 | if (booleanOperator) { | |
@@ -20,7 +20,10 @@ export default class Condition { | |||
| 20 | 20 | return new Condition(c) | |
| 21 | 21 | }) | |
| 22 | 22 | } else { | |
| 23 | - properties = params(properties).require(['fact', 'operator', 'value']) | ||
| 23 | + if (!properties.hasOwnProperty('fact')) throw new Error('Condition: constructor "fact" property required') | ||
| 24 | + if (!properties.hasOwnProperty('operator')) throw new Error('Condition: constructor "operator" property required') | ||
| 25 | + if (!properties.hasOwnProperty('value')) throw new Error('Condition: constructor "value" property required') | ||
| 26 | + | ||
| 24 | 27 | // a non-boolean condition does not have a priority by default. this allows | |
| 25 | 28 | // priority to be dictated by the fact definition | |
| 26 | 29 | if (properties.hasOwnProperty('priority')) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,5 @@ | |||
| 1 | 1 | 'use strict' | |
| 2 | 2 | ||
| 3 | - import params from 'params' | ||
| 4 | 3 | import Fact from './fact' | |
| 5 | 4 | import Rule from './rule' | |
| 6 | 5 | import Operator from './operator' | |
@@ -41,7 +40,9 @@ class Engine extends EventEmitter { | |||
| 41 | 40 | * @param {Object} properties.conditions - conditions to evaluate when processing this rule | |
| 42 | 41 | */ | |
| 43 | 42 | addRule (properties) { | |
| 44 | - params(properties).require(['conditions', 'event']) | ||
| 43 | + if (!properties) throw new Error('Engine: addRule() requires options') | ||
| 44 | + if (!properties.hasOwnProperty('conditions')) throw new Error('Engine: addRule() argument requires "conditions" property') | ||
| 45 | + if (!properties.hasOwnProperty('event')) throw new Error('Engine: addRule() argument requires "event" property') | ||
| 45 | 46 | ||
| 46 | 47 | let rule | |
| 47 | 48 | if (properties instanceof Rule) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,5 @@ | |||
| 1 | 1 | 'use strict' | |
| 2 | 2 | ||
| 3 | - import params from 'params' | ||
| 4 | 3 | import Condition from './condition' | |
| 5 | 4 | import { EventEmitter } from 'events' | |
| 6 | 5 | import deepClone from 'lodash.clonedeep' | |
@@ -70,7 +69,12 @@ class Rule extends EventEmitter { | |||
| 70 | 69 | * @param {string} event.params - parameters to emit as the argument of the event emission | |
| 71 | 70 | */ | |
| 72 | 71 | setEvent (event) { | |
| 73 | - this.event = params(event).only(['type', 'params']) | ||
| 72 | + if (!event) throw new Error('Rule: setEvent() requires event object') | ||
| 73 | + if (!event.hasOwnProperty('type')) throw new Error('Rule: setEvent() requires event object with "type" property') | ||
| 74 | + this.event = { | ||
| 75 | + type: event.type | ||
| 76 | + } | ||
| 77 | + if (event.params) this.event.params = event.params | ||
| 74 | 78 | return this | |
| 75 | 79 | } | |
| 76 | 80 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -231,22 +231,26 @@ describe('Condition', () => { | |||
| 231 | 231 | }) | |
| 232 | 232 | ||
| 233 | 233 | describe('atomic facts', () => { | |
| 234 | + it('throws if no options are provided', () => { | ||
| 235 | + expect(() => new Condition()).to.throw(/Condition: constructor options required/) | ||
| 236 | + }) | ||
| 237 | + | ||
| 234 | 238 | it('throws for a missing "operator"', () => { | |
| 235 | 239 | let conditions = condition() | |
| 236 | 240 | delete conditions.all[0].operator | |
| 237 | - expect(() => new Condition(conditions)).to.throw(/Missing key "operator"/) | ||
| 241 | + expect(() => new Condition(conditions)).to.throw(/Condition: constructor "operator" property required/) | ||
| 238 | 242 | }) | |
| 239 | 243 | ||
| 240 | 244 | it('throws for a missing "fact"', () => { | |
| 241 | 245 | let conditions = condition() | |
| 242 | 246 | delete conditions.all[0].fact | |
| 243 | - expect(() => new Condition(conditions)).to.throw(/Missing key "fact"/) | ||
| 247 | + expect(() => new Condition(conditions)).to.throw(/Condition: constructor "fact" property required/) | ||
| 244 | 248 | }) | |
| 245 | 249 | ||
| 246 | 250 | it('throws for a missing "value"', () => { | |
| 247 | 251 | let conditions = condition() | |
| 248 | 252 | delete conditions.all[0].value | |
| 249 | - expect(() => new Condition(conditions)).to.throw(/Missing key "value"/) | ||
| 253 | + expect(() => new Condition(conditions)).to.throw(/Condition: constructor "value" property required/) | ||
| 250 | 254 | }) | |
| 251 | 255 | }) | |
| 252 | 256 | ||
@@ -288,7 +292,7 @@ describe('Condition', () => { | |||
| 288 | 292 | it('throws if a nested condition is invalid', () => { | |
| 289 | 293 | let conditions = complexCondition() | |
| 290 | 294 | delete conditions.all[2].any[0].fact | |
| 291 | - expect(() => new Condition(conditions)).to.throw(/Missing key "fact"/) | ||
| 295 | + expect(() => new Condition(conditions)).to.throw(/Condition: constructor "fact" property required/) | ||
| 292 | 296 | }) | |
| 293 | 297 | }) | |
| 294 | 298 | }) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,15 +58,15 @@ describe('Engine', () => { | |||
| 58 | 58 | delete rule.conditions | |
| 59 | 59 | expect(() => { | |
| 60 | 60 | engine.addRule(rule) | |
| 61 | - }).to.throw(/Missing key "conditions"/) | ||
| 61 | + }).to.throw(/Engine: addRule\(\) argument requires "conditions" property/) | ||
| 62 | 62 | }) | |
| 63 | 63 | ||
| 64 | 64 | it('.event', () => { | |
| 65 | 65 | let rule = factories.rule() | |
| 66 | 66 | delete rule.event | |
| 67 | 67 | expect(() => { | |
| 68 | 68 | engine.addRule(rule) | |
| 69 | - }).to.throw(/Missing key "event"/) | ||
| 69 | + }).to.throw(/Engine: addRule\(\) argument requires "event" property/) | ||
| 70 | 70 | }) | |
| 71 | 71 | }) | |
| 72 | 72 | }) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -82,6 +82,16 @@ describe('Rule', () => { | |||
| 82 | 82 | }) | |
| 83 | 83 | }) | |
| 84 | 84 | ||
| 85 | + describe('setEvent()', () => { | ||
| 86 | + it('throws if no argument provided', () => { | ||
| 87 | + expect(() => rule.setEvent()).to.throw(/Rule: setEvent\(\) requires event object/) | ||
| 88 | + }) | ||
| 89 | + | ||
| 90 | + it('throws if argument is missing "type" property', () => { | ||
| 91 | + expect(() => rule.setEvent({})).to.throw(/Rule: setEvent\(\) requires event object with "type" property/) | ||
| 92 | + }) | ||
| 93 | + }) | ||
| 94 | + | ||
| 85 | 95 | describe('setConditions()', () => { | |
| 86 | 96 | describe('validations', () => { | |
| 87 | 97 | it('throws an exception for invalid root conditions', () => { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments