From 8ceab06456dfe3e3c9b5a83360f244aa22f0a331 Mon Sep 17 00:00:00 2001 From: Sykander <26931543+Sykander@users.noreply.github.com> Date: Thu, 17 Nov 2022 14:06:18 +0000 Subject: [PATCH 1/3] Feature/ Controlled Execution - Added `controlledExecution` option on `add_operation` function - Added unit tests `controlledExecution` --- logic.js | 9 ++++++++- tests/tests.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/logic.js b/logic.js index 2767087..bbd0d6a 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 96798f5..b387695 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, data, jsonLogic) { + return jsonLogic.apply(values[0], data); + } + + jsonLogic.add_operation('customOp', customOp, { controlledExecution: true }); + + assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "" }, { "var": "test" }]}, { test: 123 }), { test: 123 }); + assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "test" }, { "var": "" }]}, { test: 123 }), 123); + + // 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'); + }); }); From 0653b36b2b15b6f89c701d2143091e31c0315233 Mon Sep 17 00:00:00 2001 From: Sykander <26931543+Sykander@users.noreply.github.com> Date: Thu, 17 Nov 2022 14:55:36 +0000 Subject: [PATCH 2/3] Feature/ Controlled Execution - Cleaned up first test case --- tests/tests.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/tests.js b/tests/tests.js index b387695..6d4e70e 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -310,14 +310,14 @@ QUnit.module('basic', () => { QUnit.test("Expanding functionality with add_operator - controlledExecution", function(assert) { // assert that controlled execution doesn't do pre-evaluation - var customOp = function(values, data, jsonLogic) { - return jsonLogic.apply(values[0], data); + var customOp = function(values) { + return values[0]; } jsonLogic.add_operation('customOp', customOp, { controlledExecution: true }); - assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "" }, { "var": "test" }]}, { test: 123 }), { test: 123 }); - assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "test" }, { "var": "" }]}, { test: 123 }), 123); + 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'); From 46670b01e2fe86228292776990302bc500243cdc Mon Sep 17 00:00:00 2001 From: "Joseph D. Purcell" Date: Sat, 21 Jan 2023 19:11:29 -0500 Subject: [PATCH 3/3] Revise to use "traverse" flag and generic opOptions object --- logic.js | 12 ++++++------ tests/tests.js | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/logic.js b/logic.js index bbd0d6a..8695e68 100644 --- a/logic.js +++ b/logic.js @@ -37,7 +37,7 @@ http://ricostacruz.com/cheatsheets/umdjs.html } var jsonLogic = {}; - var runOperationAsControlled = {}; + var opOptions = {}; var operations = { "==": function(a, b) { return a == b; @@ -349,7 +349,7 @@ http://ricostacruz.com/cheatsheets/umdjs.html } } return false; // None were truthy - } else if (runOperationAsControlled[op]) { + } else if (opOptions.hasOwnProperty(op) && !opOptions[op].traverse) { return operations[op](values, data, jsonLogic); } @@ -409,14 +409,14 @@ http://ricostacruz.com/cheatsheets/umdjs.html jsonLogic.add_operation = function(name, code, options) { operations[name] = code; - - var controlledExecution = Boolean(options && options.controlledExecution); - runOperationAsControlled[name] = controlledExecution; + opOptions[name] = { + traverse: Boolean(!options || options.traverse), + }; }; jsonLogic.rm_operation = function(name) { delete operations[name]; - delete runOperationAsControlled[name]; + delete opOptions[name]; }; jsonLogic.rule_like = function(rule, pattern) { diff --git a/tests/tests.js b/tests/tests.js index 6d4e70e..56d7b69 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -308,13 +308,13 @@ QUnit.module('basic', () => { assert.deepEqual(i, [true]); }); - QUnit.test("Expanding functionality with add_operator - controlledExecution", function(assert) { + QUnit.test("Expanding functionality with add_operator - traverse", 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 }); + jsonLogic.add_operation('customOp', customOp, { traverse: false }); assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "" }, { "var": "test" }]}, { test: 123 }), { var: "" }); assert.deepEqual(jsonLogic.apply({ customOp: [{ "var": "test" }, { "var": "" }]}, { test: 123 }), { var: "test" }); @@ -333,7 +333,7 @@ QUnit.module('basic', () => { return jsonLogic.apply(values[0], externalData); } - jsonLogic.add_operation('customOp', customOp, { controlledExecution: true }); + jsonLogic.add_operation('customOp', customOp, { traverse: false }); assert.deepEqual(jsonLogic.apply({ customOp: [{ var: "specialReference" }] }, { specialReference: 'pre-evaluation value' }), 'external reference');