Skip to content

Commit a95417c

Browse files
author
Cache Hamm
authored
Merge pull request #32 from CacheControl/cached-fact-path-bugfix
fix almanac.factValue incorrectly interpretting path
2 parents 6b295f1 + 7e96b94 commit a95417c

5 files changed

Lines changed: 69 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
1.5.1 / 2017-03-19
2+
==================
3+
4+
* Bugfix almanac.factValue skipping interpreting condition "path" for cached facts
5+
16
1.5.0 / 2017-03-12
27
==================
38

dist/almanac.js

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ var Almanac = function () {
3838
_classCallCheck(this, Almanac);
3939

4040
this.factMap = new Map(factMap);
41-
this.factResultsCache = new Map();
41+
this.factResultsCache = new Map(); // { cacheKey: Promise<factValu> }
4242

4343
for (var factId in runtimeFacts) {
4444
var fact = void 0;
@@ -131,33 +131,40 @@ var Almanac = function () {
131131
var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(factId) {
132132
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
133133
var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
134-
var fact, cacheKey, cacheVal, factValue;
134+
var factValue, fact, cacheKey, cacheVal;
135135
return regeneratorRuntime.wrap(function _callee$(_context) {
136136
while (1) {
137137
switch (_context.prev = _context.next) {
138138
case 0:
139+
factValue = void 0;
139140
fact = this._getFact(factId);
140141
cacheKey = fact.getCacheKey(params);
141142
cacheVal = cacheKey && this.factResultsCache.get(cacheKey);
142143

143144
if (!cacheVal) {
144-
_context.next = 6;
145+
_context.next = 11;
145146
break;
146147
}
147148

148-
cacheVal.then(function (val) {
149-
return debug('almanac::factValue cache hit for fact:' + factId + ' value: ' + JSON.stringify(val) + '<' + (typeof val === 'undefined' ? 'undefined' : _typeof(val)) + '>');
150-
});
151-
return _context.abrupt('return', cacheVal);
149+
_context.next = 7;
150+
return cacheVal;
152151

153-
case 6:
152+
case 7:
153+
factValue = _context.sent;
154+
155+
debug('almanac::factValue cache hit for fact:' + factId + ' value: ' + JSON.stringify(factValue) + '<' + (typeof factValue === 'undefined' ? 'undefined' : _typeof(factValue)) + '>');
156+
_context.next = 15;
157+
break;
158+
159+
case 11:
154160
verbose('almanac::factValue cache miss for fact:' + factId + '; calculating');
155-
_context.next = 9;
161+
_context.next = 14;
156162
return this._setFactValue(fact, params, fact.calculate(params, this));
157163

158-
case 9:
164+
case 14:
159165
factValue = _context.sent;
160166

167+
case 15:
161168
if (path) {
162169
if (isPlainObject(factValue) || Array.isArray(factValue)) {
163170
factValue = selectn(path)(factValue);
@@ -168,7 +175,7 @@ var Almanac = function () {
168175
}
169176
return _context.abrupt('return', factValue);
170177

171-
case 12:
178+
case 17:
172179
case 'end':
173180
return _context.stop();
174181
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"scripts": {
77
"test": "mocha && npm run lint --silent",
88
"lint": "standard --verbose | snazzy || true",
9+
"lint:fix": "standard --fix",
910
"prepublish": "npm run compile",
1011
"compile": "babel --stage 1 -d dist/ src/ && regenerator --no-cache-dir --include-runtime src/generator-runtime.js > dist/generator-runtime.js"
1112
},

src/almanac.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { UndefinedFactError } from './errors'
1717
export default class Almanac {
1818
constructor (factMap, runtimeFacts = {}) {
1919
this.factMap = new Map(factMap)
20-
this.factResultsCache = new Map()
20+
this.factResultsCache = new Map() // { cacheKey: Promise<factValu> }
2121

2222
for (let factId in runtimeFacts) {
2323
let fact
@@ -89,15 +89,17 @@ export default class Almanac {
8989
* @return {Promise} a promise which will resolve with the fact computation.
9090
*/
9191
async factValue (factId, params = {}, path = '') {
92+
let factValue
9293
let fact = this._getFact(factId)
9394
let cacheKey = fact.getCacheKey(params)
9495
let cacheVal = cacheKey && this.factResultsCache.get(cacheKey)
9596
if (cacheVal) {
96-
cacheVal.then(val => debug(`almanac::factValue cache hit for fact:${factId} value: ${JSON.stringify(val)}<${typeof val}>`))
97-
return cacheVal
97+
factValue = await cacheVal
98+
debug(`almanac::factValue cache hit for fact:${factId} value: ${JSON.stringify(factValue)}<${typeof factValue}>`)
99+
} else {
100+
verbose(`almanac::factValue cache miss for fact:${factId}; calculating`)
101+
factValue = await this._setFactValue(fact, params, fact.calculate(params, this))
98102
}
99-
verbose(`almanac::factValue cache miss for fact:${factId}; calculating`)
100-
let factValue = await this._setFactValue(fact, params, fact.calculate(params, this))
101103
if (path) {
102104
if (isPlainObject(factValue) || Array.isArray(factValue)) {
103105
factValue = selectn(path)(factValue)

test/engine-fact.test.js

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,12 @@ describe('Engine: fact evaluation', () => {
5757
}
5858
let successSpy = sinon.spy()
5959
let failureSpy = sinon.spy()
60-
function setup (conditions = baseConditions(), engineOptions = {}) {
60+
beforeEach(() => {
6161
successSpy.reset()
6262
failureSpy.reset()
63+
})
64+
65+
function setup (conditions = baseConditions(), engineOptions = {}) {
6366
engine = engineFactory([], engineOptions)
6467
let rule = factories.rule({ conditions, event })
6568
engine.addRule(rule)
@@ -157,14 +160,40 @@ describe('Engine: fact evaluation', () => {
157160
expect(successSpy).to.not.have.been.called()
158161
})
159162

160-
it('emits when complex object paths meet the conditions', async () => {
161-
let complexCondition = conditions()
162-
complexCondition.any[0].path = '.address.occupantHistory[0].year'
163-
complexCondition.any[0].value = 2011
164-
complexCondition.any[0].operator = 'equal'
165-
setup(complexCondition)
166-
await engine.run()
167-
expect(successSpy).to.have.been.calledWith(event)
163+
context('complex paths', () => {
164+
it('correctly interprets "path" when dynamic facts return objects', async () => {
165+
let complexCondition = conditions()
166+
complexCondition.any[0].path = '.address.occupantHistory[0].year'
167+
complexCondition.any[0].value = 2011
168+
complexCondition.any[0].operator = 'equal'
169+
setup(complexCondition)
170+
await engine.run()
171+
expect(successSpy).to.have.been.calledWith(event)
172+
})
173+
174+
it('correctly interprets "path" with runtime fact objects', async () => {
175+
let fact = { x: { y: 1 }, a: 2 }
176+
let conditions = {
177+
all: [{
178+
fact: 'x',
179+
path: '.y',
180+
operator: 'equal',
181+
value: 1
182+
}]
183+
}
184+
let event = {
185+
type: 'runtimeEvent'
186+
}
187+
188+
engine = engineFactory([])
189+
let rule = factories.rule({ conditions, event })
190+
engine.addRule(rule)
191+
engine.on('success', successSpy)
192+
engine.on('failure', failureSpy)
193+
await engine.run(fact)
194+
expect(successSpy).to.have.been.calledWith(event)
195+
expect(failureSpy).to.not.have.been.calledWith(event)
196+
})
168197
})
169198

170199
it('does not emit when complex object paths fail the condition', async () => {

0 commit comments

Comments
 (0)