Skip to content

Commit f18816e

Browse files
Merge pull request #7 from TotalTechGeek/feat-6/add-support-for-permissive-mode
Add a permissive mode to resolve Issue #6.
2 parents 6915876 + 78a7a93 commit f18816e

File tree

6 files changed

+110
-11
lines changed

6 files changed

+110
-11
lines changed

asyncLogic.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ class AsyncLogicEngine {
1919
/**
2020
*
2121
* @param {Object} methods An object that stores key-value pairs between the names of the commands & the functions they execute.
22-
* @param {{ yieldSupported?: Boolean, disableInline?: Boolean }} options
22+
* @param {{ yieldSupported?: Boolean, disableInline?: Boolean, permissive?: boolean }} options
2323
*/
2424
constructor (
2525
methods = defaultMethods,
26-
options = { yieldSupported: false, disableInline: false }
26+
options = { yieldSupported: false, disableInline: false, permissive: false }
2727
) {
2828
this.methods = { ...methods }
29+
/** @type {{yieldSupported?: Boolean, disableInline?: Boolean, permissive?: boolean}} */
2930
this.options = { ...options }
3031
this.disableInline = options.disableInline
3132
this.async = true
@@ -73,6 +74,8 @@ class AsyncLogicEngine {
7374
return Array.isArray(result) ? Promise.all(result) : result
7475
}
7576
}
77+
if (this.options.permissive) return { [func]: data }
78+
throw new Error(`Method '${func}' was not found in the Logic Engine.`)
7679
}
7780

7881
/**

compiler.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,11 @@ function buildString (method, buildState = {}) {
271271
buildState.useContext || (engine.methods[func] || {}).useContext
272272

273273
if (method && typeof method === 'object') {
274-
if (!engine.methods[func]) throw new Error(`Method '${func}' was not found in the Logic Engine.`)
274+
if (!engine.methods[func]) {
275+
// If we are in permissive mode, we will just return the object.
276+
if (engine.options.permissive) return pushValue(method)
277+
throw new Error(`Method '${func}' was not found in the Logic Engine.`)
278+
}
275279
functions[func] = functions[func] || 2
276280

277281
if (

defaultMethods.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ function isDeterministic (method, engine, buildState) {
1616
if (method && typeof method === 'object') {
1717
const func = Object.keys(method)[0]
1818
const lower = method[func]
19+
20+
if (!engine.methods[func] && engine.options.permissive) return true
21+
1922
if (engine.methods[func].traverse === false) {
2023
return typeof engine.methods[func].deterministic === 'function'
2124
? engine.methods[func].deterministic(lower, buildState)

general.test.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
2+
import assert from 'assert'
3+
import { LogicEngine, AsyncLogicEngine } from './index.js'
4+
5+
const normalEngines = [
6+
new LogicEngine(),
7+
new AsyncLogicEngine(),
8+
new LogicEngine(undefined, { yieldSupported: true }),
9+
new AsyncLogicEngine(undefined, { yieldSupported: true })
10+
]
11+
12+
const permissiveEngines = [
13+
new LogicEngine(undefined, { permissive: true }),
14+
new AsyncLogicEngine(undefined, { permissive: true }),
15+
new LogicEngine(undefined, { yieldSupported: true, permissive: true }),
16+
new AsyncLogicEngine(undefined, { yieldSupported: true, permissive: true })
17+
]
18+
19+
async function testEngineAsync (engine, rule, data, expected) {
20+
// run
21+
if (expected === Error) {
22+
try {
23+
await engine.run(rule, data)
24+
throw new Error('Should have failed')
25+
} catch (e) {}
26+
} else {
27+
const result = await engine.run(rule, data)
28+
assert.deepStrictEqual(result, expected)
29+
}
30+
31+
// build
32+
if (expected === Error) {
33+
try {
34+
const built = await engine.build(rule)
35+
await built(data)
36+
throw new Error('Should have failed')
37+
} catch (e) {}
38+
} else {
39+
const built = await engine.build(rule)
40+
const builtResult = await built(data)
41+
assert.deepStrictEqual(builtResult, expected)
42+
}
43+
}
44+
45+
function testEngine (engine, rule, data, expected) {
46+
if (engine instanceof AsyncLogicEngine) {
47+
return testEngineAsync(engine, rule, data, expected)
48+
}
49+
50+
// run
51+
if (expected === Error) {
52+
try {
53+
engine.run(rule, data)
54+
throw new Error('Should have failed')
55+
} catch (e) {}
56+
} else {
57+
const result = engine.run(rule, data)
58+
assert.deepStrictEqual(result, expected)
59+
}
60+
61+
// build
62+
if (expected === Error) {
63+
try {
64+
const built = engine.build(rule)
65+
built(data)
66+
throw new Error('Should have failed')
67+
} catch (e) {}
68+
} else {
69+
const built = engine.build(rule)
70+
const builtResult = built(data)
71+
assert.deepStrictEqual(builtResult, expected)
72+
}
73+
}
74+
75+
describe('Various Test Cases', () => {
76+
it('Should fail when an unrecognized method is used.', async () => {
77+
for (const engine of normalEngines) await testEngine(engine, { unknown: true }, {}, Error)
78+
})
79+
80+
it('Should return the object when an unrecognized method is used.', async () => {
81+
for (const engine of permissiveEngines) {
82+
await testEngine(engine, { unknown: true }, {}, { unknown: true })
83+
84+
await testEngine(engine, {
85+
if: [true, { unknown: true }, 5]
86+
}, {}, { unknown: true })
87+
}
88+
})
89+
})

logic.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ class LogicEngine {
1616
/**
1717
*
1818
* @param {Object} methods An object that stores key-value pairs between the names of the commands & the functions they execute.
19-
* @param {{ yieldSupported?: Boolean, disableInline?: Boolean }} options
19+
* @param {{ yieldSupported?: Boolean, disableInline?: Boolean, permissive?: boolean }} options
2020
*/
2121
constructor (
2222
methods = defaultMethods,
23-
options = { yieldSupported: false, disableInline: false }
23+
options = { yieldSupported: false, disableInline: false, permissive: false }
2424
) {
2525
this.disableInline = options.disableInline
2626
this.methods = { ...methods }
27+
/** @type {{yieldSupported?: Boolean, disableInline?: Boolean, permissive?: boolean}} */
2728
this.options = { ...options }
2829
}
2930

@@ -44,17 +45,16 @@ class LogicEngine {
4445
}
4546
if (typeof this.methods[func] === 'object') {
4647
const { method, traverse } = this.methods[func]
47-
const shouldTraverse =
48-
typeof traverse === 'undefined' ? true : traverse
48+
const shouldTraverse = typeof traverse === 'undefined' ? true : traverse
4949
const parsedData = shouldTraverse
5050
? this.run(data, context, { above })
5151
: data
52-
if (this.options.yieldSupported && checkYield(parsedData)) {
53-
return parsedData
54-
}
52+
if (this.options.yieldSupported && checkYield(parsedData)) return parsedData
5553
return method(parsedData, context, above, this)
5654
}
5755
}
56+
if (this.options.permissive) return { [func]: data }
57+
throw new Error(`Method '${func}' was not found in the Logic Engine.`)
5858
}
5959

6060
/**

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "json-logic-engine",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "Construct complex rules with JSON & process them.",
55
"main": "./dist/cjs/index.js",
66
"module": "./dist/esm/index.js",

0 commit comments

Comments
 (0)