diff --git a/logic.js b/logic.js index 27670879..bbd0d6ad 100644 --- a/logic.js +++ b/logic.js @@ -37,6 +37,7 @@ http://ricostacruz.com/cheatsheets/umdjs.html } var jsonLogic = {}; + var runOperationAsControlled = {}; var operations = { "==": function(a, b) { return a == b; @@ -348,6 +349,8 @@ http://ricostacruz.com/cheatsheets/umdjs.html } } return false; // None were truthy + } else if (runOperationAsControlled[op]) { + return operations[op](values, data, jsonLogic); } // Everyone else gets immediate depth-first recursion @@ -404,12 +407,16 @@ http://ricostacruz.com/cheatsheets/umdjs.html return arrayUnique(collection); }; - jsonLogic.add_operation = function(name, code) { + jsonLogic.add_operation = function(name, code, options) { operations[name] = code; + + var controlledExecution = Boolean(options && options.controlledExecution); + runOperationAsControlled[name] = controlledExecution; }; jsonLogic.rm_operation = function(name) { delete operations[name]; + delete runOperationAsControlled[name]; }; jsonLogic.rule_like = function(rule, pattern) { diff --git a/tests/tests.js b/tests/tests.js index 96798f51..6d4e70e0 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -307,4 +307,49 @@ QUnit.module('basic', () => { jsonLogic.apply({"or": [{"push": [true]}, {"push": [true]}]}); assert.deepEqual(i, [true]); }); + + QUnit.test("Expanding functionality with add_operator - controlledExecution", function(assert) { + // assert that controlled execution doesn't do pre-evaluation + var customOp = function(values) { + return values[0]; + } + + jsonLogic.add_operation('customOp', customOp, { controlledExecution: true }); + + assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "" }, { "var": "test" }]}, { test: 123 }), { var: "" }); + assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "test" }, { "var": "" }]}, { test: 123 }), { var: "test" }); + + // assert that controlled execution custom operators can be removed as normal + jsonLogic.rm_operation('customOp'); + + assert.throws(() => jsonLogic.apply({ customOp: [] }), Error, "Unrecognized operation customOp"); + + // assert that controlled-execution custom operators have access to jsonLogic object + // and can run on external data + const externalData = { + specialReference: 'external reference' + }; + customOp = function(values, data, jsonLogic) { + return jsonLogic.apply(values[0], externalData); + } + + jsonLogic.add_operation('customOp', customOp, { controlledExecution: true }); + + assert.deepEqual(jsonLogic.apply({ customOp: [{ var: "specialReference" }] }, { specialReference: 'pre-evaluation value' }), 'external reference'); + + // assert that operators are added with normal functionality when options is omitted + jsonLogic.add_operation('customOp', customOp); + + assert.throws(() => jsonLogic.apply({ customOp: [{ var: "specialReference" }] }, { specialReference: 'pre-evaluation value' }), TypeError, "Cannot read property 'apply' of undefined"); + + // assert that adding a custom operator without controlled-execution still + // results in pre-evaluation + customOp = function(value) { + return value; + } + + jsonLogic.add_operation('customOp', customOp); + + assert.deepEqual(jsonLogic.apply({ customOp: [{ var: "specialReference" }] }, { specialReference: 'pre-evaluation value' }), 'pre-evaluation value'); + }); });