Skip to content

Commit

Permalink
Move Almanac Initialization
Browse files Browse the repository at this point in the history
Move the Almanac Initialization into the engine class. This will allow
it to take user supplied Almanacs correctly.
  • Loading branch information
chris-pardy committed Nov 5, 2023
1 parent 597daa3 commit 25f9f7f
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 53 deletions.
16 changes: 2 additions & 14 deletions src/almanac.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,13 @@ function defaultPathResolver (value, path) {
* A new almanac is used for every engine run()
*/
export default class Almanac {
constructor (factMap, runtimeFacts = {}, options = {}) {
this.factMap = new Map(factMap)
constructor (options = {}) {
this.factMap = new Map()
this.factResultsCache = new Map() // { cacheKey: Promise<factValu> }
this.allowUndefinedFacts = Boolean(options.allowUndefinedFacts)
this.pathResolver = options.pathResolver || defaultPathResolver
this.events = { success: [], failure: [] }
this.ruleResults = []

for (const factId in runtimeFacts) {
let fact
if (runtimeFacts[factId] instanceof Fact) {
fact = runtimeFacts[factId]
} else {
fact = new Fact(factId, runtimeFacts[factId])
}

this._addConstantFact(fact)
debug(`almanac::constructor initialized runtime fact:${fact.id} with ${fact.value}<${typeof fact.value}>`)
}
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,21 @@ class Engine extends EventEmitter {
allowUndefinedFacts: this.allowUndefinedFacts,
pathResolver: this.pathResolver
}
const almanac = new Almanac(this.facts, runtimeFacts, almanacOptions)
const almanac = new Almanac(almanacOptions)
this.facts.forEach(fact => {
almanac.addFact(fact)
})
for (const factId in runtimeFacts) {
let fact
if (runtimeFacts[factId] instanceof Fact) {
fact = runtimeFacts[factId]
} else {
fact = new Fact(factId, runtimeFacts[factId])
}

almanac.addFact(fact)
debug(`engine::run initialized runtime fact:${fact.id} with ${fact.value}<${typeof fact.value}>`)
}
const orderedSets = this.prioritizeRules()
let cursor = Promise.resolve()
// for each rule set, evaluate in parallel,
Expand Down
41 changes: 12 additions & 29 deletions test/almanac.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,12 @@ describe('Almanac', () => {
})

it('adds runtime facts', () => {
almanac = new Almanac(new Map(), { modelId: 'XYZ' })
almanac = new Almanac()
almanac.addFact('modelId', 'XYZ')
expect(almanac.factMap.get('modelId').value).to.equal('XYZ')
})
})

describe('constructor', () => {
it('supports runtime facts as key => values', () => {
almanac = new Almanac(new Map(), { fact1: 3 })
return expect(almanac.factValue('fact1')).to.eventually.equal(3)
})

it('supports runtime fact instances', () => {
const fact = new Fact('fact1', 3)
almanac = new Almanac(new Map(), { fact1: fact })
return expect(almanac.factValue('fact1')).to.eventually.equal(fact.value)
})
})

describe('addFact', () => {
it('supports runtime facts as key => values', () => {
almanac = new Almanac()
Expand Down Expand Up @@ -94,9 +82,8 @@ describe('Almanac', () => {
if (params.userId) return params.userId
return 'unknown'
})
const factMap = new Map()
factMap.set(fact.id, fact)
almanac = new Almanac(factMap)
almanac = new Almanac()
almanac.addFact(fact)
})

it('allows parameters to be passed to the fact', async () => {
Expand Down Expand Up @@ -131,10 +118,9 @@ describe('Almanac', () => {

describe('_getFact', _ => {
it('retrieves the fact object', () => {
const facts = new Map()
const fact = new Fact('id', 1)
facts.set(fact.id, fact)
almanac = new Almanac(facts)
almanac = new Almanac()
almanac.addFact(fact)
expect(almanac._getFact('id')).to.equal(fact)
})
})
Expand All @@ -149,9 +135,8 @@ describe('Almanac', () => {

function setup (f = new Fact('id', 1)) {
fact = f
const facts = new Map()
facts.set(fact.id, fact)
almanac = new Almanac(facts)
almanac = new Almanac()
almanac.addFact(fact)
}
let fact
const FACT_VALUE = 2
Expand Down Expand Up @@ -179,9 +164,8 @@ describe('Almanac', () => {
name: 'Thomas'
}]
})
const factMap = new Map()
factMap.set(fact.id, fact)
almanac = new Almanac(factMap)
almanac = new Almanac()
almanac.addFact(fact)
const result = await almanac.factValue('foo', null, '$..name')
expect(result).to.deep.equal(['George', 'Thomas'])
})
Expand All @@ -192,9 +176,8 @@ describe('Almanac', () => {
factSpy()
return 'unknown'
}, factOptions)
const factMap = new Map()
factMap.set(fact.id, fact)
almanac = new Almanac(factMap)
almanac = new Almanac()
almanac.addFact(fact)
}

it('evaluates the fact every time when fact caching is off', () => {
Expand Down
21 changes: 12 additions & 9 deletions test/condition.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ describe('Condition', () => {
const properties = Object.assign({}, conditionBase, options)
condition = new Condition(properties)
const fact = new Fact(conditionBase.fact, factValue)
almanac = new Almanac(new Map([[fact.id, fact]]))
almanac = new Almanac()
almanac.addFact(fact)
}

context('validations', () => {
Expand All @@ -118,12 +119,14 @@ describe('Condition', () => {
it('evaluates "equal" to check for undefined', async () => {
condition = new Condition({ fact: 'age', operator: 'equal', value: undefined })
let fact = new Fact('age', undefined)
almanac = new Almanac(new Map([[fact.id, fact]]))
almanac = new Almanac()
almanac.addFact(fact)

expect((await condition.evaluate(almanac, operators)).result).to.equal(true)

fact = new Fact('age', 1)
almanac = new Almanac(new Map([[fact.id, fact]]))
almanac = new Almanac()
almanac.addFact(fact)
expect((await condition.evaluate(almanac, operators)).result).to.equal(false)
})

Expand Down Expand Up @@ -235,8 +238,8 @@ describe('Condition', () => {
it('extracts the object property values using its "path" property', async () => {
const condition = new Condition({ operator: 'equal', path: '$.[0].id', fact: 'age', value: 50 })
const ageFact = new Fact('age', [{ id: 50 }, { id: 60 }])
const facts = new Map([[ageFact.id, ageFact]])
const almanac = new Almanac(facts)
const almanac = new Almanac()
almanac.addFact(ageFact)
expect((await condition.evaluate(almanac, operators)).result).to.equal(true)

condition.value = 100 // negative case
Expand All @@ -245,8 +248,8 @@ describe('Condition', () => {

it('ignores "path" when non-objects are returned by the fact', async () => {
const ageFact = new Fact('age', 50)
const facts = new Map([[ageFact.id, ageFact]])
const almanac = new Almanac(facts)
const almanac = new Almanac()
almanac.addFact(ageFact)

const condition = new Condition({ operator: 'equal', path: '$.[0].id', fact: 'age', value: 50 })
expect((await condition.evaluate(almanac, operators, 50)).result).to.equal(true)
Expand All @@ -273,8 +276,8 @@ describe('Condition', () => {
}

const usersFact = new Fact('users', userData)
const facts = new Map([[usersFact.id, usersFact]])
const almanac = new Almanac(facts)
const almanac = new Almanac()
almanac.addFact(usersFact)
expect((await condition.evaluate(almanac, operators)).result).to.equal(true)

condition.value = 'work' // negative case
Expand Down

0 comments on commit 25f9f7f

Please sign in to comment.