From aa69be4c52216774314c58b01032c4c60e5959b9 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 17:50:10 +0100 Subject: [PATCH 01/33] refactore: extract operations --- src/createJsonLogic.js | 5 +++++ src/index.js | 0 src/operations/add.js | 7 +++++++ src/operations/cat.js | 5 +++++ src/operations/divide.js | 5 +++++ src/operations/equal.js | 5 +++++ src/operations/falsy.js | 7 +++++++ src/operations/greater.js | 5 +++++ src/operations/greaterEqual.js | 5 +++++ src/operations/index.js | 28 +++++++++++++++++++++++++++ src/operations/indexOf.js | 6 ++++++ src/operations/log.js | 7 +++++++ src/operations/lower.js | 5 +++++ src/operations/lowerEqual.js | 5 +++++ src/operations/max.js | 5 +++++ src/operations/merge.js | 7 +++++++ src/operations/method.js | 5 +++++ src/operations/min.js | 5 +++++ src/operations/missingAccessor.js | 25 ++++++++++++++++++++++++ src/operations/missingSomeAccessor.js | 14 ++++++++++++++ src/operations/modulo.js | 5 +++++ src/operations/multiply.js | 7 +++++++ src/operations/notEqual.js | 5 +++++ src/operations/strictEqual.js | 5 +++++ src/operations/strictNotEqual.js | 5 +++++ src/operations/substr.js | 10 ++++++++++ src/operations/substract.js | 9 +++++++++ src/operations/truthy.js | 14 ++++++++++++++ src/operations/varAccessor.js | 25 ++++++++++++++++++++++++ 29 files changed, 241 insertions(+) create mode 100644 src/createJsonLogic.js create mode 100644 src/index.js create mode 100644 src/operations/add.js create mode 100644 src/operations/cat.js create mode 100644 src/operations/divide.js create mode 100644 src/operations/equal.js create mode 100644 src/operations/falsy.js create mode 100644 src/operations/greater.js create mode 100644 src/operations/greaterEqual.js create mode 100644 src/operations/index.js create mode 100644 src/operations/indexOf.js create mode 100644 src/operations/log.js create mode 100644 src/operations/lower.js create mode 100644 src/operations/lowerEqual.js create mode 100644 src/operations/max.js create mode 100644 src/operations/merge.js create mode 100644 src/operations/method.js create mode 100644 src/operations/min.js create mode 100644 src/operations/missingAccessor.js create mode 100644 src/operations/missingSomeAccessor.js create mode 100644 src/operations/modulo.js create mode 100644 src/operations/multiply.js create mode 100644 src/operations/notEqual.js create mode 100644 src/operations/strictEqual.js create mode 100644 src/operations/strictNotEqual.js create mode 100644 src/operations/substr.js create mode 100644 src/operations/substract.js create mode 100644 src/operations/truthy.js create mode 100644 src/operations/varAccessor.js diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js new file mode 100644 index 0000000..936e428 --- /dev/null +++ b/src/createJsonLogic.js @@ -0,0 +1,5 @@ +function createJsonLogic() { + +} + +export default createJsonLogic; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/operations/add.js b/src/operations/add.js new file mode 100644 index 0000000..5dc90e3 --- /dev/null +++ b/src/operations/add.js @@ -0,0 +1,7 @@ +function add(...args) { + return args.reduce(function(a, b) { + return parseFloat(a, 10) + parseFloat(b, 10); + }, 0); +} + +export default add; diff --git a/src/operations/cat.js b/src/operations/cat.js new file mode 100644 index 0000000..a630a8f --- /dev/null +++ b/src/operations/cat.js @@ -0,0 +1,5 @@ +function cat() { + return Array.prototype.join.call(arguments, ""); +} + +export default cat; diff --git a/src/operations/divide.js b/src/operations/divide.js new file mode 100644 index 0000000..93feb78 --- /dev/null +++ b/src/operations/divide.js @@ -0,0 +1,5 @@ +function divide(a, b) { + return a / b; +} + +export default divide; diff --git a/src/operations/equal.js b/src/operations/equal.js new file mode 100644 index 0000000..80ae474 --- /dev/null +++ b/src/operations/equal.js @@ -0,0 +1,5 @@ +function equal(a, b) { + return a == b; +} + +export default equal; diff --git a/src/operations/falsy.js b/src/operations/falsy.js new file mode 100644 index 0000000..37dacb3 --- /dev/null +++ b/src/operations/falsy.js @@ -0,0 +1,7 @@ +import truthy from './truthy' + +function falsy(a) { + return !truthy(a); +} + +export default falsy; diff --git a/src/operations/greater.js b/src/operations/greater.js new file mode 100644 index 0000000..06ba5c2 --- /dev/null +++ b/src/operations/greater.js @@ -0,0 +1,5 @@ +function greater(a, b) { + return a > b; +} + +export default greater; diff --git a/src/operations/greaterEqual.js b/src/operations/greaterEqual.js new file mode 100644 index 0000000..d3730e2 --- /dev/null +++ b/src/operations/greaterEqual.js @@ -0,0 +1,5 @@ +function greaterEqual(a, b) { + return a >= b; +} + +export default greaterEqual; diff --git a/src/operations/index.js b/src/operations/index.js new file mode 100644 index 0000000..b7eadcb --- /dev/null +++ b/src/operations/index.js @@ -0,0 +1,28 @@ +import missingAccessor from "./missingAccessor"; + +export { default as equal } from './equal'; +export { default as strictEqual } from './strictEqual'; +export { default as notEqual } from './notEqual'; +export { default as strictNotEqual } from './strictNotEqual'; +export { default as greater } from './greater'; +export { default as greaterEqual } from './greaterEqual'; +export { default as lower } from './lower'; +export { default as lowerEqual } from './lowerEqual'; +export { default as truthy } from './truthy'; +export { default as falsy } from './falsy'; +export { default as modulo } from './modulo'; +export { default as log } from './log'; +export { default as indexOf } from './indexOf'; +export { default as cat } from './cat'; +export { default as substr } from './substr'; +export { default as add } from './add'; +export { default as multiply } from './multiply'; +export { default as substract } from './substract'; +export { default as divide } from './divide'; +export { default as min } from './min'; +export { default as max } from './max'; +export { default as merge } from './merge'; +export { default as varAccessor } from './varAccessor'; +export { default as missingAccessor } from './missingAccessor'; +export { default as missingSomeAccessor } from './missingSomeAccessor'; +export { default as method } from './method'; diff --git a/src/operations/indexOf.js b/src/operations/indexOf.js new file mode 100644 index 0000000..9a2afee --- /dev/null +++ b/src/operations/indexOf.js @@ -0,0 +1,6 @@ +function indexOf(a, b) { + if(!b || typeof b.indexOf === "undefined") return false; + return (b.indexOf(a) !== -1); +} + +export default indexOf; diff --git a/src/operations/log.js b/src/operations/log.js new file mode 100644 index 0000000..b15c053 --- /dev/null +++ b/src/operations/log.js @@ -0,0 +1,7 @@ +function log(a) { + console.log(a); + + return a; +} + +export default log; diff --git a/src/operations/lower.js b/src/operations/lower.js new file mode 100644 index 0000000..0939e66 --- /dev/null +++ b/src/operations/lower.js @@ -0,0 +1,5 @@ +function lower(a, b, c) { + return (c === undefined) ? a < b : (a < b) && (b < c); +} + +export default lower; diff --git a/src/operations/lowerEqual.js b/src/operations/lowerEqual.js new file mode 100644 index 0000000..25a2b30 --- /dev/null +++ b/src/operations/lowerEqual.js @@ -0,0 +1,5 @@ +function lowerEqual(a, b, c) { + return (c === undefined) ? a <= b : (a <= b) && (b <= c); +} + +export default lowerEqual; diff --git a/src/operations/max.js b/src/operations/max.js new file mode 100644 index 0000000..35ebc19 --- /dev/null +++ b/src/operations/max.js @@ -0,0 +1,5 @@ +function max() { + return Math.max.apply(this, arguments); +} + +export default max; diff --git a/src/operations/merge.js b/src/operations/merge.js new file mode 100644 index 0000000..0d31ec8 --- /dev/null +++ b/src/operations/merge.js @@ -0,0 +1,7 @@ +function merge(...args) { + return args.reduce(function(a, b) { + return a.concat(b); + }, []); +} + +export default merge; diff --git a/src/operations/method.js b/src/operations/method.js new file mode 100644 index 0000000..fe66035 --- /dev/null +++ b/src/operations/method.js @@ -0,0 +1,5 @@ +function method(obj, method, args) { + return obj[method].apply(obj, args); +} + +export default method; diff --git a/src/operations/min.js b/src/operations/min.js new file mode 100644 index 0000000..3885aa0 --- /dev/null +++ b/src/operations/min.js @@ -0,0 +1,5 @@ +function min() { + return Math.min.apply(this, arguments); +} + +export default min; diff --git a/src/operations/missingAccessor.js b/src/operations/missingAccessor.js new file mode 100644 index 0000000..13fcf45 --- /dev/null +++ b/src/operations/missingAccessor.js @@ -0,0 +1,25 @@ +import varAccessor from './varAccessor' + +function missingAccessor() { + /* + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ + + var missing = []; + var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments; + + for(var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = varAccessor.call(this, {"var": key}); + if(value === null || value === "") { + missing.push(key); + } + } + + return missing; +}; + +export default missingAccessor; diff --git a/src/operations/missingSomeAccessor.js b/src/operations/missingSomeAccessor.js new file mode 100644 index 0000000..e82721b --- /dev/null +++ b/src/operations/missingSomeAccessor.js @@ -0,0 +1,14 @@ +import missingAccessor from './missingAccessor'; + +function missingSomeAccessor(need_count, options) { + // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. + var are_missing = missingAccessor.call(this, {"missing": options}); + + if(options.length - are_missing.length >= need_count) { + return []; + }else{ + return are_missing; + } +} + +export default missingSomeAccessor; diff --git a/src/operations/modulo.js b/src/operations/modulo.js new file mode 100644 index 0000000..642bd6d --- /dev/null +++ b/src/operations/modulo.js @@ -0,0 +1,5 @@ +function modulo(a, b) { + return a % b; +} + +export default modulo; diff --git a/src/operations/multiply.js b/src/operations/multiply.js new file mode 100644 index 0000000..59ddd2a --- /dev/null +++ b/src/operations/multiply.js @@ -0,0 +1,7 @@ +function multiply(...args) { + return args.reduce(function(a, b) { + return parseFloat(a, 10) * parseFloat(b, 10); + }, 1); +} + +export default multiply; diff --git a/src/operations/notEqual.js b/src/operations/notEqual.js new file mode 100644 index 0000000..3d3ed0f --- /dev/null +++ b/src/operations/notEqual.js @@ -0,0 +1,5 @@ +function notEqual(a, b) { + return a != b; +} + +export default notEqual; diff --git a/src/operations/strictEqual.js b/src/operations/strictEqual.js new file mode 100644 index 0000000..f9517a0 --- /dev/null +++ b/src/operations/strictEqual.js @@ -0,0 +1,5 @@ +function strictEqual(a, b) { + return a === b; +} + +export default strictEqual; diff --git a/src/operations/strictNotEqual.js b/src/operations/strictNotEqual.js new file mode 100644 index 0000000..31f255d --- /dev/null +++ b/src/operations/strictNotEqual.js @@ -0,0 +1,5 @@ +function strictNotEqual(a, b) { + return a !== b; +} + +export default strictNotEqual; diff --git a/src/operations/substr.js b/src/operations/substr.js new file mode 100644 index 0000000..24da4bf --- /dev/null +++ b/src/operations/substr.js @@ -0,0 +1,10 @@ +function substr(source, start, end) { + if (end < 0) { + // JavaScript doesn't support negative end, this emulates PHP behavior + const temp = String(source).substr(start); + return temp.substr(0, temp.length + end); + } + return String(source).substr(start, end); +}; + +export default substr; diff --git a/src/operations/substract.js b/src/operations/substract.js new file mode 100644 index 0000000..94caeaf --- /dev/null +++ b/src/operations/substract.js @@ -0,0 +1,9 @@ +function substract(a, b) { + if(b === undefined) { + return -a; + }else{ + return a - b; + } +} + +export default substract; diff --git a/src/operations/truthy.js b/src/operations/truthy.js new file mode 100644 index 0000000..6cf16f2 --- /dev/null +++ b/src/operations/truthy.js @@ -0,0 +1,14 @@ +/* + This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. + + Spec and rationale here: http://jsonlogic.com/truthy + */ +function truthy(a) { + if(Array.isArray(value) && value.length === 0) { + return false; + } + + return !!value; +} + +export default truthy; diff --git a/src/operations/varAccessor.js b/src/operations/varAccessor.js new file mode 100644 index 0000000..00075cd --- /dev/null +++ b/src/operations/varAccessor.js @@ -0,0 +1,25 @@ +function varAccessor(a, b) { + const not_found = (b === undefined) ? null : b; + let data = this; + + if(typeof a === "undefined" || a==="" || a===null) { + return data; + } + + const sub_props = String(a).split("."); + + for(let i = 0; i < sub_props.length; i++) { + if(data === null) { + return not_found; + } + // Descending into data + data = data[sub_props[i]]; + if(data === undefined) { + return not_found; + } + } + + return data; +} + +export default varAccessor; From d11373c1f6fb6974022262b0f5f9fb9f91385606 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 18:16:25 +0100 Subject: [PATCH 02/33] refactore: extracted helpers --- src/helpers/apply.js | 167 +++++++++++++++++++++++++++++++++++++ src/helpers/arrayUnique.js | 16 ++++ src/helpers/getOperator.js | 5 ++ src/helpers/getValues.js | 7 ++ src/helpers/isLogic.js | 10 +++ src/helpers/ruleLike.js | 64 ++++++++++++++ src/helpers/truthy.js | 14 ++++ src/helpers/usesData.js | 28 +++++++ 8 files changed, 311 insertions(+) create mode 100644 src/helpers/apply.js create mode 100644 src/helpers/arrayUnique.js create mode 100644 src/helpers/getOperator.js create mode 100644 src/helpers/getValues.js create mode 100644 src/helpers/isLogic.js create mode 100644 src/helpers/ruleLike.js create mode 100644 src/helpers/truthy.js create mode 100644 src/helpers/usesData.js diff --git a/src/helpers/apply.js b/src/helpers/apply.js new file mode 100644 index 0000000..5e7ee6c --- /dev/null +++ b/src/helpers/apply.js @@ -0,0 +1,167 @@ +import isLogic from './isLogic'; +import getOperator from './getOperator'; +import truthy from './truthy'; + +function apply(logic, data) { + // Does this array contain logic? Only one way to find out. + if(Array.isArray(logic)) { + return logic.map(function(l) { + return apply(l, data); + }); + } + // You've recursed to a primitive, stop! + if( ! isLogic(logic) ) { + return logic; + } + + data = data || {}; + + var op = getOperator(logic); + var values = logic[op]; + var i; + var current; + var scopedLogic, scopedData, filtered, initial; + + // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} + if( ! Array.isArray(values)) { + values = [values]; + } + + // 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed. + if(op === "if" || op == "?:") { + /* 'if' should be called with a odd number of parameters, 3 or greater + This works on the pattern: + if( 0 ){ 1 }else{ 2 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; + + The implementation is: + For pairs of values (0,1 then 2,3 then 4,5 etc) + If the first evaluates truthy, evaluate and return the second + If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) + given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) + given 0 parameters, return NULL (not great practice, but there was no Else) + */ + for(i = 0; i < values.length - 1; i += 2) { + if( truthy( apply(values[i], data) ) ) { + return apply(values[i+1], data); + } + } + if(values.length === i+1) return apply(values[i], data); + return null; + }else if(op === "and") { // Return first falsy, or last + for(i=0; i < values.length; i+=1) { + current = apply(values[i], data); + if( ! truthy(current)) { + return current; + } + } + return current; // Last + }else if(op === "or") {// Return first truthy, or last + for(i=0; i < values.length; i+=1) { + current = apply(values[i], data); + if( truthy(current) ) { + return current; + } + } + return current; // Last + + + + + }else if(op === 'filter'){ + scopedData = apply(values[0], data); + scopedLogic = values[1]; + + if ( ! Array.isArray(scopedData)) { + return []; + } + // Return only the elements from the array in the first argument, + // that return truthy when passed to the logic in the second argument. + // For parity with JavaScript, reindex the returned array + return scopedData.filter(function(datum){ + return truthy( apply(scopedLogic, datum)); + }); + }else if(op === 'map'){ + scopedData = apply(values[0], data); + scopedLogic = values[1]; + + if ( ! Array.isArray(scopedData)) { + return []; + } + + return scopedData.map(function(datum){ + return apply(scopedLogic, datum); + }); + + }else if(op === 'reduce'){ + scopedData = apply(values[0], data); + scopedLogic = values[1]; + initial = typeof values[2] !== 'undefined' ? values[2] : null; + + if ( ! Array.isArray(scopedData)) { + return initial; + } + + return scopedData.reduce( + function(accumulator, current){ + return apply( + scopedLogic, + {'current':current, 'accumulator':accumulator} + ); + }, + initial + ); + + }else if(op === "all") { + scopedData = apply(values[0], data); + scopedLogic = values[1]; + // All of an empty set is false. Note, some and none have correct fallback after the for loop + if( ! scopedData.length) { + return false; + } + for(i=0; i < scopedData.length; i+=1) { + if( ! truthy( apply(scopedLogic, scopedData[i]) )) { + return false; // First falsy, short circuit + } + } + return true; // All were truthy + }else if(op === "none") { + filtered = apply({'filter' : values}, data); + return filtered.length === 0; + + }else if(op === "some") { + filtered = apply({'filter' : values}, data); + return filtered.length > 0; + } + + // Everyone else gets immediate depth-first recursion + values = values.map(function(val) { + return apply(val, data); + }); + + + // The operation is called with "data" bound to its "this" and "values" passed as arguments. + // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments + if(typeof operations[op] === "function") { + return operations[op].apply(data, values); + }else if(op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position + var sub_ops = String(op).split("."); + var operation = operations; + for(i = 0; i < sub_ops.length; i++) { + // Descending into operations + operation = operation[sub_ops[i]]; + if(operation === undefined) { + throw new Error("Unrecognized operation " + op + + " (failed at " + sub_ops.slice(0, i+1).join(".") + ")"); + } + } + + return operation.apply(data, values); + } + + throw new Error("Unrecognized operation " + op ); +} + +export default apply; diff --git a/src/helpers/arrayUnique.js b/src/helpers/arrayUnique.js new file mode 100644 index 0000000..1cde3a7 --- /dev/null +++ b/src/helpers/arrayUnique.js @@ -0,0 +1,16 @@ +/** + * Return an array that contains no duplicates (original not modified) + * @param {array} array Original reference array + * @return {array} New array with no duplicates + */ +function arrayUnique(array) { + const a = []; + for (let i=0, l=array.length; i Date: Thu, 21 Feb 2019 18:16:47 +0100 Subject: [PATCH 03/33] refactore: truthy and falsy operations --- src/operations/falsy.js | 2 +- src/operations/truthy.js | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/operations/falsy.js b/src/operations/falsy.js index 37dacb3..3662ffd 100644 --- a/src/operations/falsy.js +++ b/src/operations/falsy.js @@ -1,4 +1,4 @@ -import truthy from './truthy' +import truthy from '../helpers/truthy' function falsy(a) { return !truthy(a); diff --git a/src/operations/truthy.js b/src/operations/truthy.js index 6cf16f2..cb575fd 100644 --- a/src/operations/truthy.js +++ b/src/operations/truthy.js @@ -1,14 +1 @@ -/* - This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. - - Spec and rationale here: http://jsonlogic.com/truthy - */ -function truthy(a) { - if(Array.isArray(value) && value.length === 0) { - return false; - } - - return !!value; -} - -export default truthy; +export '../helpers/truthy'; From e6e627f95bea0c9bd58408834c79d2e3ec5b9b2a Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 18:36:21 +0100 Subject: [PATCH 04/33] refactore: start extracting visitors --- src/visitors/allVisitor.js | 18 ++++++++++++++++++ src/visitors/andVisitor.js | 13 +++++++++++++ src/visitors/filterVisitor.js | 18 ++++++++++++++++++ src/visitors/ifVisitor.js | 30 ++++++++++++++++++++++++++++++ src/visitors/index.js | 9 +++++++++ src/visitors/mapVisitor.js | 14 ++++++++++++++ src/visitors/noneVisitor.js | 7 +++++++ src/visitors/orVisitor.js | 13 +++++++++++++ src/visitors/reduceVisitor.js | 21 +++++++++++++++++++++ src/visitors/someVisitor.js | 7 +++++++ 10 files changed, 150 insertions(+) create mode 100644 src/visitors/allVisitor.js create mode 100644 src/visitors/andVisitor.js create mode 100644 src/visitors/filterVisitor.js create mode 100644 src/visitors/ifVisitor.js create mode 100644 src/visitors/index.js create mode 100644 src/visitors/mapVisitor.js create mode 100644 src/visitors/noneVisitor.js create mode 100644 src/visitors/orVisitor.js create mode 100644 src/visitors/reduceVisitor.js create mode 100644 src/visitors/someVisitor.js diff --git a/src/visitors/allVisitor.js b/src/visitors/allVisitor.js new file mode 100644 index 0000000..7f8ebac --- /dev/null +++ b/src/visitors/allVisitor.js @@ -0,0 +1,18 @@ +import truthy from "../helpers/truthy"; + +function allVisitor(data, values) { + const scopedData = apply(values[0], data); + const scopedLogic = values[1]; + // All of an empty set is false. Note, some and none have correct fallback after the for loop + if( ! scopedData.length) { + return false; + } + for(let i=0; i < scopedData.length; i+=1) { + if( ! truthy( apply(scopedLogic, scopedData[i]) )) { + return false; // First falsy, short circuit + } + } + return true; // All were truthy +} + +export default allVisitor; diff --git a/src/visitors/andVisitor.js b/src/visitors/andVisitor.js new file mode 100644 index 0000000..db6d3f3 --- /dev/null +++ b/src/visitors/andVisitor.js @@ -0,0 +1,13 @@ +import truthy from "../helpers/truthy"; + +function andVisitor(data, values, current) { + for(let i=0; i < values.length; i++) { + current = apply(values[i], data); + if( ! truthy(current)) { + return current; + } + } + return current; // Last +} + +export default andVisitor; diff --git a/src/visitors/filterVisitor.js b/src/visitors/filterVisitor.js new file mode 100644 index 0000000..38cf79d --- /dev/null +++ b/src/visitors/filterVisitor.js @@ -0,0 +1,18 @@ +import truthy from "../helpers/truthy"; + +function filterVisitor(data, values) { + const scopedData = apply(values[0], data); + const scopedLogic = values[1]; + + if ( ! Array.isArray(scopedData)) { + return []; + } + // Return only the elements from the array in the first argument, + // that return truthy when passed to the logic in the second argument. + // For parity with JavaScript, reindex the returned array + return scopedData.filter(function(datum){ + return truthy( apply(scopedLogic, datum)); + }); +} + +export default filterVisitor diff --git a/src/visitors/ifVisitor.js b/src/visitors/ifVisitor.js new file mode 100644 index 0000000..e7ca6b3 --- /dev/null +++ b/src/visitors/ifVisitor.js @@ -0,0 +1,30 @@ +import truthy from '../helpers/truthy'; + +function ifVisitor(data, values) { + /* 'if' should be called with a odd number of parameters, 3 or greater + This works on the pattern: + if( 0 ){ 1 }else{ 2 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; + + The implementation is: + For pairs of values (0,1 then 2,3 then 4,5 etc) + If the first evaluates truthy, evaluate and return the second + If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) + given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) + given 0 parameters, return NULL (not great practice, but there was no Else) + */ + for(let i = 0; i < values.length - 1; i += 2) { + if( truthy( apply(values[i], data) ) ) { + return apply(values[i+1], data); + } + } + + if(values.length === i+1) { + return apply(values[i], data); + } + + return null; +}; + +export default ifVisitor; diff --git a/src/visitors/index.js b/src/visitors/index.js new file mode 100644 index 0000000..7e5a50b --- /dev/null +++ b/src/visitors/index.js @@ -0,0 +1,9 @@ +export { default as allVisitor } from './allVisitor' +export { default as andVisito } from './andVisito' +export { default as filterVisitor } from './filterVisitor' +export { default as ifVisitor } from './ifVisitor' +export { default as mapVisitor } from './mapVisitor' +export { default as noneVisitor } from './noneVisitor' +export { default as orVisitor } from './orVisitor' +export { default as reduceVisitor } from './reduceVisitor' +export { default as someVisitor } from './someVisitor' diff --git a/src/visitors/mapVisitor.js b/src/visitors/mapVisitor.js new file mode 100644 index 0000000..2ab9131 --- /dev/null +++ b/src/visitors/mapVisitor.js @@ -0,0 +1,14 @@ +function mapVisitor(data, values) { + const scopedData = apply(values[0], data); + const scopedLogic = values[1]; + + if ( ! Array.isArray(scopedData)) { + return []; + } + + return scopedData.map(function(datum){ + return apply(scopedLogic, datum); + }); +} + +export default mapVisitor; diff --git a/src/visitors/noneVisitor.js b/src/visitors/noneVisitor.js new file mode 100644 index 0000000..6aded28 --- /dev/null +++ b/src/visitors/noneVisitor.js @@ -0,0 +1,7 @@ +function noneVisitor(data, values) { + const filtered = apply({'filter' : values}, data); + + return filtered.length === 0; +} + +export default noneVisitor; diff --git a/src/visitors/orVisitor.js b/src/visitors/orVisitor.js new file mode 100644 index 0000000..11c4d0e --- /dev/null +++ b/src/visitors/orVisitor.js @@ -0,0 +1,13 @@ +import truthy from "../helpers/truthy"; + +function orVisitor(data, values, current) { + for(let i=0; i < values.length; i++) { + current = apply(values[i], data); + if( truthy(current) ) { + return current; + } + } + return current; // Last +} + +export default orVisitor; diff --git a/src/visitors/reduceVisitor.js b/src/visitors/reduceVisitor.js new file mode 100644 index 0000000..749b499 --- /dev/null +++ b/src/visitors/reduceVisitor.js @@ -0,0 +1,21 @@ +function reduceVisitor(data, values) { + const scopedData = apply(values[0], data); + const scopedLogic = values[1]; + const initial = typeof values[2] !== 'undefined' ? values[2] : null; + + if ( ! Array.isArray(scopedData)) { + return initial; + } + + return scopedData.reduce( + function(accumulator, current){ + return apply( + scopedLogic, + {'current':current, 'accumulator':accumulator} + ); + }, + initial + ); +} + +export default reduceVisitor; diff --git a/src/visitors/someVisitor.js b/src/visitors/someVisitor.js new file mode 100644 index 0000000..c1b9dc0 --- /dev/null +++ b/src/visitors/someVisitor.js @@ -0,0 +1,7 @@ +function someVisitor(data, values) { + const filtered = apply({'filter' : values}, data); + + return filtered.length > 0; +} + +export default someVisitor; From 8ae7689393866e8d3aaca43eaec81f83acdb41ca Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 18:57:56 +0100 Subject: [PATCH 05/33] fix: added missing operator code --- src/operations/add.js | 2 ++ src/operations/divide.js | 2 ++ src/operations/equal.js | 2 ++ src/operations/falsy.js | 2 ++ src/operations/greater.js | 2 ++ src/operations/greaterEqual.js | 2 ++ src/operations/indexOf.js | 2 ++ src/operations/lower.js | 2 ++ src/operations/lowerEqual.js | 2 ++ src/operations/missingAccessor.js | 2 ++ src/operations/missingSomeAccessor.js | 2 ++ src/operations/modulo.js | 2 ++ src/operations/multiply.js | 2 ++ src/operations/notEqual.js | 2 ++ src/operations/strictEqual.js | 2 ++ src/operations/strictNotEqual.js | 2 ++ src/operations/varAccessor.js | 2 ++ 17 files changed, 34 insertions(+) diff --git a/src/operations/add.js b/src/operations/add.js index 5dc90e3..d314e20 100644 --- a/src/operations/add.js +++ b/src/operations/add.js @@ -4,4 +4,6 @@ function add(...args) { }, 0); } +add.code = '+'; + export default add; diff --git a/src/operations/divide.js b/src/operations/divide.js index 93feb78..953faf6 100644 --- a/src/operations/divide.js +++ b/src/operations/divide.js @@ -2,4 +2,6 @@ function divide(a, b) { return a / b; } +divide.code = '/'; + export default divide; diff --git a/src/operations/equal.js b/src/operations/equal.js index 80ae474..31e0743 100644 --- a/src/operations/equal.js +++ b/src/operations/equal.js @@ -2,4 +2,6 @@ function equal(a, b) { return a == b; } +equal.code = '=='; + export default equal; diff --git a/src/operations/falsy.js b/src/operations/falsy.js index 3662ffd..7555401 100644 --- a/src/operations/falsy.js +++ b/src/operations/falsy.js @@ -4,4 +4,6 @@ function falsy(a) { return !truthy(a); } +falsy.code = '!'; + export default falsy; diff --git a/src/operations/greater.js b/src/operations/greater.js index 06ba5c2..5a71ead 100644 --- a/src/operations/greater.js +++ b/src/operations/greater.js @@ -2,4 +2,6 @@ function greater(a, b) { return a > b; } +greater.code = '>'; + export default greater; diff --git a/src/operations/greaterEqual.js b/src/operations/greaterEqual.js index d3730e2..d59668a 100644 --- a/src/operations/greaterEqual.js +++ b/src/operations/greaterEqual.js @@ -2,4 +2,6 @@ function greaterEqual(a, b) { return a >= b; } +greaterEqual.code = '>='; + export default greaterEqual; diff --git a/src/operations/indexOf.js b/src/operations/indexOf.js index 9a2afee..37ba9f2 100644 --- a/src/operations/indexOf.js +++ b/src/operations/indexOf.js @@ -3,4 +3,6 @@ function indexOf(a, b) { return (b.indexOf(a) !== -1); } +indexOf.code = 'in'; + export default indexOf; diff --git a/src/operations/lower.js b/src/operations/lower.js index 0939e66..8d31a7d 100644 --- a/src/operations/lower.js +++ b/src/operations/lower.js @@ -2,4 +2,6 @@ function lower(a, b, c) { return (c === undefined) ? a < b : (a < b) && (b < c); } +lower.code = '<'; + export default lower; diff --git a/src/operations/lowerEqual.js b/src/operations/lowerEqual.js index 25a2b30..a49bf31 100644 --- a/src/operations/lowerEqual.js +++ b/src/operations/lowerEqual.js @@ -2,4 +2,6 @@ function lowerEqual(a, b, c) { return (c === undefined) ? a <= b : (a <= b) && (b <= c); } +lowerEqual.code = '<='; + export default lowerEqual; diff --git a/src/operations/missingAccessor.js b/src/operations/missingAccessor.js index 13fcf45..fefc9ed 100644 --- a/src/operations/missingAccessor.js +++ b/src/operations/missingAccessor.js @@ -22,4 +22,6 @@ function missingAccessor() { return missing; }; +missingAccessor.code = 'missing'; + export default missingAccessor; diff --git a/src/operations/missingSomeAccessor.js b/src/operations/missingSomeAccessor.js index e82721b..86ab8fc 100644 --- a/src/operations/missingSomeAccessor.js +++ b/src/operations/missingSomeAccessor.js @@ -11,4 +11,6 @@ function missingSomeAccessor(need_count, options) { } } +missingSomeAccessor.code = 'missing_some'; + export default missingSomeAccessor; diff --git a/src/operations/modulo.js b/src/operations/modulo.js index 642bd6d..8395bd2 100644 --- a/src/operations/modulo.js +++ b/src/operations/modulo.js @@ -2,4 +2,6 @@ function modulo(a, b) { return a % b; } +modulo.code = '%'; + export default modulo; diff --git a/src/operations/multiply.js b/src/operations/multiply.js index 59ddd2a..2e72479 100644 --- a/src/operations/multiply.js +++ b/src/operations/multiply.js @@ -4,4 +4,6 @@ function multiply(...args) { }, 1); } +multiply.code = '*'; + export default multiply; diff --git a/src/operations/notEqual.js b/src/operations/notEqual.js index 3d3ed0f..98c7b72 100644 --- a/src/operations/notEqual.js +++ b/src/operations/notEqual.js @@ -2,4 +2,6 @@ function notEqual(a, b) { return a != b; } +notEqual.code = '!='; + export default notEqual; diff --git a/src/operations/strictEqual.js b/src/operations/strictEqual.js index f9517a0..dcda4fa 100644 --- a/src/operations/strictEqual.js +++ b/src/operations/strictEqual.js @@ -2,4 +2,6 @@ function strictEqual(a, b) { return a === b; } +strictEqual.code = '==='; + export default strictEqual; diff --git a/src/operations/strictNotEqual.js b/src/operations/strictNotEqual.js index 31f255d..5d9cbcc 100644 --- a/src/operations/strictNotEqual.js +++ b/src/operations/strictNotEqual.js @@ -2,4 +2,6 @@ function strictNotEqual(a, b) { return a !== b; } +strictNotEqual.code = '1=='; + export default strictNotEqual; diff --git a/src/operations/varAccessor.js b/src/operations/varAccessor.js index 00075cd..38e24cf 100644 --- a/src/operations/varAccessor.js +++ b/src/operations/varAccessor.js @@ -22,4 +22,6 @@ function varAccessor(a, b) { return data; } +varAccessor.code = 'var'; + export default varAccessor; From e84b72420fe2e8151c19fae09f868dad7f7c0556 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 18:59:59 +0100 Subject: [PATCH 06/33] refactore: naming of accessors --- src/operations/index.js | 2 +- src/operations/{missingAccessor.js => missing.js} | 10 ++++------ .../{missingSomeAccessor.js => missingSome.js} | 10 +++++----- src/operations/{varAccessor.js => variable.js} | 6 +++--- 4 files changed, 13 insertions(+), 15 deletions(-) rename src/operations/{missingAccessor.js => missing.js} (73%) rename src/operations/{missingSomeAccessor.js => missingSome.js} (51%) rename src/operations/{varAccessor.js => variable.js} (83%) diff --git a/src/operations/index.js b/src/operations/index.js index b7eadcb..a82b22e 100644 --- a/src/operations/index.js +++ b/src/operations/index.js @@ -1,4 +1,4 @@ -import missingAccessor from "./missingAccessor"; +import missing from "./missingAccessor"; export { default as equal } from './equal'; export { default as strictEqual } from './strictEqual'; diff --git a/src/operations/missingAccessor.js b/src/operations/missing.js similarity index 73% rename from src/operations/missingAccessor.js rename to src/operations/missing.js index fefc9ed..92ec924 100644 --- a/src/operations/missingAccessor.js +++ b/src/operations/missing.js @@ -1,6 +1,6 @@ -import varAccessor from './varAccessor' +import variable from './varAccessor' -function missingAccessor() { +function missing() { /* Missing can receive many keys as many arguments, like {"missing:[1,2]} Missing can also receive *one* argument that is an array of keys, @@ -13,7 +13,7 @@ function missingAccessor() { for(var i = 0; i < keys.length; i++) { var key = keys[i]; - var value = varAccessor.call(this, {"var": key}); + var value = variable.call(this, {"var": key}); if(value === null || value === "") { missing.push(key); } @@ -22,6 +22,4 @@ function missingAccessor() { return missing; }; -missingAccessor.code = 'missing'; - -export default missingAccessor; +export default missing; diff --git a/src/operations/missingSomeAccessor.js b/src/operations/missingSome.js similarity index 51% rename from src/operations/missingSomeAccessor.js rename to src/operations/missingSome.js index 86ab8fc..668e7b9 100644 --- a/src/operations/missingSomeAccessor.js +++ b/src/operations/missingSome.js @@ -1,8 +1,8 @@ -import missingAccessor from './missingAccessor'; +import missing from './missingAccessor'; -function missingSomeAccessor(need_count, options) { +function missingSome(need_count, options) { // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. - var are_missing = missingAccessor.call(this, {"missing": options}); + var are_missing = missing.call(this, {"missing": options}); if(options.length - are_missing.length >= need_count) { return []; @@ -11,6 +11,6 @@ function missingSomeAccessor(need_count, options) { } } -missingSomeAccessor.code = 'missing_some'; +missingSome.code = 'missing_some'; -export default missingSomeAccessor; +export default missingSome; diff --git a/src/operations/varAccessor.js b/src/operations/variable.js similarity index 83% rename from src/operations/varAccessor.js rename to src/operations/variable.js index 38e24cf..3f3825b 100644 --- a/src/operations/varAccessor.js +++ b/src/operations/variable.js @@ -1,4 +1,4 @@ -function varAccessor(a, b) { +function variable(a, b) { const not_found = (b === undefined) ? null : b; let data = this; @@ -22,6 +22,6 @@ function varAccessor(a, b) { return data; } -varAccessor.code = 'var'; +variable.code = 'var'; -export default varAccessor; +export default variable; From 90da72372bf8154df917db65bdbcf3902c39249d Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 19:02:46 +0100 Subject: [PATCH 07/33] refactore: visitor names --- src/visitors/{allVisitor.js => all.js} | 4 ++-- src/visitors/{andVisitor.js => and.js} | 4 ++-- src/visitors/{ifVisitor.js => condition.js} | 8 +++++--- src/visitors/{filterVisitor.js => filter.js} | 4 ++-- src/visitors/{mapVisitor.js => map.js} | 4 ++-- src/visitors/{noneVisitor.js => none.js} | 4 ++-- src/visitors/{orVisitor.js => or.js} | 4 ++-- src/visitors/{reduceVisitor.js => reduce.js} | 4 ++-- src/visitors/{someVisitor.js => some.js} | 4 ++-- 9 files changed, 21 insertions(+), 19 deletions(-) rename src/visitors/{allVisitor.js => all.js} (88%) rename src/visitors/{andVisitor.js => and.js} (74%) rename src/visitors/{ifVisitor.js => condition.js} (91%) rename src/visitors/{filterVisitor.js => filter.js} (87%) rename src/visitors/{mapVisitor.js => map.js} (78%) rename src/visitors/{noneVisitor.js => none.js} (57%) rename src/visitors/{orVisitor.js => or.js} (74%) rename src/visitors/{reduceVisitor.js => reduce.js} (85%) rename src/visitors/{someVisitor.js => some.js} (57%) diff --git a/src/visitors/allVisitor.js b/src/visitors/all.js similarity index 88% rename from src/visitors/allVisitor.js rename to src/visitors/all.js index 7f8ebac..3d4499c 100644 --- a/src/visitors/allVisitor.js +++ b/src/visitors/all.js @@ -1,6 +1,6 @@ import truthy from "../helpers/truthy"; -function allVisitor(data, values) { +function all(data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; // All of an empty set is false. Note, some and none have correct fallback after the for loop @@ -15,4 +15,4 @@ function allVisitor(data, values) { return true; // All were truthy } -export default allVisitor; +export default all; diff --git a/src/visitors/andVisitor.js b/src/visitors/and.js similarity index 74% rename from src/visitors/andVisitor.js rename to src/visitors/and.js index db6d3f3..8c441c9 100644 --- a/src/visitors/andVisitor.js +++ b/src/visitors/and.js @@ -1,6 +1,6 @@ import truthy from "../helpers/truthy"; -function andVisitor(data, values, current) { +function and(data, values, current) { for(let i=0; i < values.length; i++) { current = apply(values[i], data); if( ! truthy(current)) { @@ -10,4 +10,4 @@ function andVisitor(data, values, current) { return current; // Last } -export default andVisitor; +export default and; diff --git a/src/visitors/ifVisitor.js b/src/visitors/condition.js similarity index 91% rename from src/visitors/ifVisitor.js rename to src/visitors/condition.js index e7ca6b3..26981b6 100644 --- a/src/visitors/ifVisitor.js +++ b/src/visitors/condition.js @@ -1,6 +1,6 @@ import truthy from '../helpers/truthy'; -function ifVisitor(data, values) { +function condition(data, values) { /* 'if' should be called with a odd number of parameters, 3 or greater This works on the pattern: if( 0 ){ 1 }else{ 2 }; @@ -25,6 +25,8 @@ function ifVisitor(data, values) { } return null; -}; +} -export default ifVisitor; +condition.code = 'if'; + +export default condition; diff --git a/src/visitors/filterVisitor.js b/src/visitors/filter.js similarity index 87% rename from src/visitors/filterVisitor.js rename to src/visitors/filter.js index 38cf79d..589bdba 100644 --- a/src/visitors/filterVisitor.js +++ b/src/visitors/filter.js @@ -1,6 +1,6 @@ import truthy from "../helpers/truthy"; -function filterVisitor(data, values) { +function filter(data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; @@ -15,4 +15,4 @@ function filterVisitor(data, values) { }); } -export default filterVisitor +export default filter diff --git a/src/visitors/mapVisitor.js b/src/visitors/map.js similarity index 78% rename from src/visitors/mapVisitor.js rename to src/visitors/map.js index 2ab9131..2639617 100644 --- a/src/visitors/mapVisitor.js +++ b/src/visitors/map.js @@ -1,4 +1,4 @@ -function mapVisitor(data, values) { +function map(data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; @@ -11,4 +11,4 @@ function mapVisitor(data, values) { }); } -export default mapVisitor; +export default map; diff --git a/src/visitors/noneVisitor.js b/src/visitors/none.js similarity index 57% rename from src/visitors/noneVisitor.js rename to src/visitors/none.js index 6aded28..a28b0d3 100644 --- a/src/visitors/noneVisitor.js +++ b/src/visitors/none.js @@ -1,7 +1,7 @@ -function noneVisitor(data, values) { +function none(data, values) { const filtered = apply({'filter' : values}, data); return filtered.length === 0; } -export default noneVisitor; +export default none; diff --git a/src/visitors/orVisitor.js b/src/visitors/or.js similarity index 74% rename from src/visitors/orVisitor.js rename to src/visitors/or.js index 11c4d0e..3756972 100644 --- a/src/visitors/orVisitor.js +++ b/src/visitors/or.js @@ -1,6 +1,6 @@ import truthy from "../helpers/truthy"; -function orVisitor(data, values, current) { +function or(data, values, current) { for(let i=0; i < values.length; i++) { current = apply(values[i], data); if( truthy(current) ) { @@ -10,4 +10,4 @@ function orVisitor(data, values, current) { return current; // Last } -export default orVisitor; +export default or; diff --git a/src/visitors/reduceVisitor.js b/src/visitors/reduce.js similarity index 85% rename from src/visitors/reduceVisitor.js rename to src/visitors/reduce.js index 749b499..ef1b014 100644 --- a/src/visitors/reduceVisitor.js +++ b/src/visitors/reduce.js @@ -1,4 +1,4 @@ -function reduceVisitor(data, values) { +function reduce(data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; const initial = typeof values[2] !== 'undefined' ? values[2] : null; @@ -18,4 +18,4 @@ function reduceVisitor(data, values) { ); } -export default reduceVisitor; +export default reduce; diff --git a/src/visitors/someVisitor.js b/src/visitors/some.js similarity index 57% rename from src/visitors/someVisitor.js rename to src/visitors/some.js index c1b9dc0..d8e4271 100644 --- a/src/visitors/someVisitor.js +++ b/src/visitors/some.js @@ -1,7 +1,7 @@ -function someVisitor(data, values) { +function some(data, values) { const filtered = apply({'filter' : values}, data); return filtered.length > 0; } -export default someVisitor; +export default some; From e68d84292f7fef174f3746c5e8a467c73176c061 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 19:12:59 +0100 Subject: [PATCH 08/33] refactore: start applying visitors --- src/createJsonLogic.js | 91 +++++++++++++++++++++ src/helpers/apply.js | 167 -------------------------------------- src/index.js | 19 +++++ src/visitors/all.js | 2 +- src/visitors/and.js | 4 +- src/visitors/condition.js | 4 +- src/visitors/filter.js | 2 +- src/visitors/map.js | 2 +- src/visitors/none.js | 2 +- src/visitors/or.js | 4 +- src/visitors/reduce.js | 2 +- src/visitors/some.js | 2 +- 12 files changed, 124 insertions(+), 177 deletions(-) delete mode 100644 src/helpers/apply.js diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js index 936e428..4b8038d 100644 --- a/src/createJsonLogic.js +++ b/src/createJsonLogic.js @@ -1,5 +1,96 @@ +import isLogic from './isLogic'; +import getOperator from './getOperator'; + function createJsonLogic() { + const operations = {} + const visitors = {} + + function addOperation(name, code) { + operations[name] = code; + } + + function removeOperation(name) { + delete operations[name]; + } + + function addVisitor(name, code) { + if (Array.isArray(name)) { + name.forEach(addVisitor); + } + + visitors[name] = code; + } + + function removeVisitor(name) { + if (Array.isArray(name)) { + name.forEach(removeVisitor); + } + + delete visitors[name]; + } + + function apply(logic, data) { + // Does this array contain logic? Only one way to find out. + if(Array.isArray(logic)) { + return logic.map(function(l) { + return apply(l, data); + }); + } + // You've recursed to a primitive, stop! + if( ! isLogic(logic) ) { + return logic; + } + + data = data || {}; + + const op = getOperator(logic); + let values = logic[op]; + let i; + + // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} + if( ! Array.isArray(values)) { + values = [values]; + } + + // apply matching visitors first + if (typeof visitors[op] === 'function') { + return visitors[op](apply, data, values); + } + + // Everyone else gets immediate depth-first recursion + values = values.map(function(val) { + return apply(val, data); + }); + + // The operation is called with "data" bound to its "this" and "values" passed as arguments. + // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments + if(typeof operations[op] === "function") { + return operations[op].apply(data, values); + }else if(op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position + var sub_ops = String(op).split("."); + var operation = operations; + for(i = 0; i < sub_ops.length; i++) { + // Descending into operations + operation = operation[sub_ops[i]]; + if(operation === undefined) { + throw new Error("Unrecognized operation " + op + + " (failed at " + sub_ops.slice(0, i+1).join(".") + ")"); + } + } + + return operation.apply(data, values); + } + + throw new Error("Unrecognized operation " + op ); + } + return { + add_operation: addOperation, + rm_operation: removeOperation, + add_visitor: addVisitor, + rm_visitor: removeVisitor, + } } export default createJsonLogic; diff --git a/src/helpers/apply.js b/src/helpers/apply.js deleted file mode 100644 index 5e7ee6c..0000000 --- a/src/helpers/apply.js +++ /dev/null @@ -1,167 +0,0 @@ -import isLogic from './isLogic'; -import getOperator from './getOperator'; -import truthy from './truthy'; - -function apply(logic, data) { - // Does this array contain logic? Only one way to find out. - if(Array.isArray(logic)) { - return logic.map(function(l) { - return apply(l, data); - }); - } - // You've recursed to a primitive, stop! - if( ! isLogic(logic) ) { - return logic; - } - - data = data || {}; - - var op = getOperator(logic); - var values = logic[op]; - var i; - var current; - var scopedLogic, scopedData, filtered, initial; - - // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} - if( ! Array.isArray(values)) { - values = [values]; - } - - // 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed. - if(op === "if" || op == "?:") { - /* 'if' should be called with a odd number of parameters, 3 or greater - This works on the pattern: - if( 0 ){ 1 }else{ 2 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; - - The implementation is: - For pairs of values (0,1 then 2,3 then 4,5 etc) - If the first evaluates truthy, evaluate and return the second - If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) - given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) - given 0 parameters, return NULL (not great practice, but there was no Else) - */ - for(i = 0; i < values.length - 1; i += 2) { - if( truthy( apply(values[i], data) ) ) { - return apply(values[i+1], data); - } - } - if(values.length === i+1) return apply(values[i], data); - return null; - }else if(op === "and") { // Return first falsy, or last - for(i=0; i < values.length; i+=1) { - current = apply(values[i], data); - if( ! truthy(current)) { - return current; - } - } - return current; // Last - }else if(op === "or") {// Return first truthy, or last - for(i=0; i < values.length; i+=1) { - current = apply(values[i], data); - if( truthy(current) ) { - return current; - } - } - return current; // Last - - - - - }else if(op === 'filter'){ - scopedData = apply(values[0], data); - scopedLogic = values[1]; - - if ( ! Array.isArray(scopedData)) { - return []; - } - // Return only the elements from the array in the first argument, - // that return truthy when passed to the logic in the second argument. - // For parity with JavaScript, reindex the returned array - return scopedData.filter(function(datum){ - return truthy( apply(scopedLogic, datum)); - }); - }else if(op === 'map'){ - scopedData = apply(values[0], data); - scopedLogic = values[1]; - - if ( ! Array.isArray(scopedData)) { - return []; - } - - return scopedData.map(function(datum){ - return apply(scopedLogic, datum); - }); - - }else if(op === 'reduce'){ - scopedData = apply(values[0], data); - scopedLogic = values[1]; - initial = typeof values[2] !== 'undefined' ? values[2] : null; - - if ( ! Array.isArray(scopedData)) { - return initial; - } - - return scopedData.reduce( - function(accumulator, current){ - return apply( - scopedLogic, - {'current':current, 'accumulator':accumulator} - ); - }, - initial - ); - - }else if(op === "all") { - scopedData = apply(values[0], data); - scopedLogic = values[1]; - // All of an empty set is false. Note, some and none have correct fallback after the for loop - if( ! scopedData.length) { - return false; - } - for(i=0; i < scopedData.length; i+=1) { - if( ! truthy( apply(scopedLogic, scopedData[i]) )) { - return false; // First falsy, short circuit - } - } - return true; // All were truthy - }else if(op === "none") { - filtered = apply({'filter' : values}, data); - return filtered.length === 0; - - }else if(op === "some") { - filtered = apply({'filter' : values}, data); - return filtered.length > 0; - } - - // Everyone else gets immediate depth-first recursion - values = values.map(function(val) { - return apply(val, data); - }); - - - // The operation is called with "data" bound to its "this" and "values" passed as arguments. - // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments - if(typeof operations[op] === "function") { - return operations[op].apply(data, values); - }else if(op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position - var sub_ops = String(op).split("."); - var operation = operations; - for(i = 0; i < sub_ops.length; i++) { - // Descending into operations - operation = operation[sub_ops[i]]; - if(operation === undefined) { - throw new Error("Unrecognized operation " + op + - " (failed at " + sub_ops.slice(0, i+1).join(".") + ")"); - } - } - - return operation.apply(data, values); - } - - throw new Error("Unrecognized operation " + op ); -} - -export default apply; diff --git a/src/index.js b/src/index.js index e69de29..7ab2977 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,19 @@ +import createJsonLogic from './createJsonLogic'; +import * as operations from './operations'; +import * as visitors from './visitors'; + +const jsonLogic = createJsonLogic(); + +Object.keys(operations).forEach(function(name) { + const operation = operations[name]; + + jsonLogic.add_operation(operation.code || name , operation); +}); + +Object.keys(visitors).forEach(function(name) { + const visitor = visitors[name]; + + jsonLogic.add_visitor(visitor.code || name , visitor); +}); + +export default jsonLogic; diff --git a/src/visitors/all.js b/src/visitors/all.js index 3d4499c..efdcff8 100644 --- a/src/visitors/all.js +++ b/src/visitors/all.js @@ -1,6 +1,6 @@ import truthy from "../helpers/truthy"; -function all(data, values) { +function all(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; // All of an empty set is false. Note, some and none have correct fallback after the for loop diff --git a/src/visitors/and.js b/src/visitors/and.js index 8c441c9..194e90f 100644 --- a/src/visitors/and.js +++ b/src/visitors/and.js @@ -1,6 +1,8 @@ import truthy from "../helpers/truthy"; -function and(data, values, current) { +function and(apply, data, values) { + let current; + for(let i=0; i < values.length; i++) { current = apply(values[i], data); if( ! truthy(current)) { diff --git a/src/visitors/condition.js b/src/visitors/condition.js index 26981b6..8361e3e 100644 --- a/src/visitors/condition.js +++ b/src/visitors/condition.js @@ -1,6 +1,6 @@ import truthy from '../helpers/truthy'; -function condition(data, values) { +function condition(apply, data, values) { /* 'if' should be called with a odd number of parameters, 3 or greater This works on the pattern: if( 0 ){ 1 }else{ 2 }; @@ -27,6 +27,6 @@ function condition(data, values) { return null; } -condition.code = 'if'; +condition.code = ['if', '?:']; export default condition; diff --git a/src/visitors/filter.js b/src/visitors/filter.js index 589bdba..533b0c4 100644 --- a/src/visitors/filter.js +++ b/src/visitors/filter.js @@ -1,6 +1,6 @@ import truthy from "../helpers/truthy"; -function filter(data, values) { +function filter(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; diff --git a/src/visitors/map.js b/src/visitors/map.js index 2639617..28b4b8b 100644 --- a/src/visitors/map.js +++ b/src/visitors/map.js @@ -1,4 +1,4 @@ -function map(data, values) { +function map(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; diff --git a/src/visitors/none.js b/src/visitors/none.js index a28b0d3..35ecea4 100644 --- a/src/visitors/none.js +++ b/src/visitors/none.js @@ -1,4 +1,4 @@ -function none(data, values) { +function none(apply, data, values) { const filtered = apply({'filter' : values}, data); return filtered.length === 0; diff --git a/src/visitors/or.js b/src/visitors/or.js index 3756972..d3a6acc 100644 --- a/src/visitors/or.js +++ b/src/visitors/or.js @@ -1,6 +1,8 @@ import truthy from "../helpers/truthy"; -function or(data, values, current) { +function or(apply, data, values) { + let current; + for(let i=0; i < values.length; i++) { current = apply(values[i], data); if( truthy(current) ) { diff --git a/src/visitors/reduce.js b/src/visitors/reduce.js index ef1b014..ace8611 100644 --- a/src/visitors/reduce.js +++ b/src/visitors/reduce.js @@ -1,4 +1,4 @@ -function reduce(data, values) { +function reduce(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; const initial = typeof values[2] !== 'undefined' ? values[2] : null; diff --git a/src/visitors/some.js b/src/visitors/some.js index d8e4271..700110c 100644 --- a/src/visitors/some.js +++ b/src/visitors/some.js @@ -1,4 +1,4 @@ -function some(data, values) { +function some(apply, data, values) { const filtered = apply({'filter' : values}, data); return filtered.length > 0; From b3c2426182b19048b7e52944fe3abce107879133 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 19:25:38 +0100 Subject: [PATCH 09/33] fix: restored original API --- src/createJsonLogic.js | 1 + src/helpers/usesData.js | 2 ++ src/index.js | 14 ++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js index 4b8038d..c3e8521 100644 --- a/src/createJsonLogic.js +++ b/src/createJsonLogic.js @@ -86,6 +86,7 @@ function createJsonLogic() { } return { + apply, add_operation: addOperation, rm_operation: removeOperation, add_visitor: addVisitor, diff --git a/src/helpers/usesData.js b/src/helpers/usesData.js index f6a2075..bcdcf4b 100644 --- a/src/helpers/usesData.js +++ b/src/helpers/usesData.js @@ -26,3 +26,5 @@ function usesData(logic) { return arrayUnique(collection); }; + +export default usesData; diff --git a/src/index.js b/src/index.js index 7ab2977..5d0b1a5 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,12 @@ import createJsonLogic from './createJsonLogic'; import * as operations from './operations'; import * as visitors from './visitors'; +import isLogic from "./helpers/isLogic"; +import truthy from "./helpers/truthy"; +import getOperator from "./helpers/getOperator"; +import getValues from "./helpers/getValues"; +import usesData from "./helpers/usesData"; +import ruleLike from "./helpers/ruleLike"; const jsonLogic = createJsonLogic(); @@ -16,4 +22,12 @@ Object.keys(visitors).forEach(function(name) { jsonLogic.add_visitor(visitor.code || name , visitor); }); +// restore original public API +jsonLogic.is_logic = isLogic; +jsonLogic.truthy = truthy; +jsonLogic.get_operator = getOperator; +jsonLogic.get_values = getValues; +jsonLogic.uses_data = usesData; +jsonLogic.rule_like = ruleLike; + export default jsonLogic; From 65f754971517399b54378d27d7d02fb630697729 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Thu, 21 Feb 2019 19:27:40 +0100 Subject: [PATCH 10/33] feat: also export jsonLogic creator --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 5d0b1a5..a02e2c6 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -import createJsonLogic from './createJsonLogic'; +export createJsonLogic from './createJsonLogic'; import * as operations from './operations'; import * as visitors from './visitors'; import isLogic from "./helpers/isLogic"; From 46560282292cf92198b2f00785d400a6418a177d Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 09:10:58 +0100 Subject: [PATCH 11/33] refactore: api --- src/createJsonLogic.js | 17 +++++++++++++---- src/index.js | 14 +------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js index c3e8521..da8a361 100644 --- a/src/createJsonLogic.js +++ b/src/createJsonLogic.js @@ -1,9 +1,18 @@ import isLogic from './isLogic'; import getOperator from './getOperator'; -function createJsonLogic() { - const operations = {} - const visitors = {} +function createJsonLogic(operations = {}, visitors = {}) { + Object.keys(operations).forEach(function(name) { + const operation = operations[name]; + + addOperation(operation.code || name , operation); + }); + + Object.keys(visitors).forEach(function(name) { + const visitor = visitors[name]; + + addVisitor(visitor.code || name , visitor); + }); function addOperation(name, code) { operations[name] = code; @@ -91,7 +100,7 @@ function createJsonLogic() { rm_operation: removeOperation, add_visitor: addVisitor, rm_visitor: removeVisitor, - } + }; } export default createJsonLogic; diff --git a/src/index.js b/src/index.js index a02e2c6..0aa21d3 100644 --- a/src/index.js +++ b/src/index.js @@ -8,19 +8,7 @@ import getValues from "./helpers/getValues"; import usesData from "./helpers/usesData"; import ruleLike from "./helpers/ruleLike"; -const jsonLogic = createJsonLogic(); - -Object.keys(operations).forEach(function(name) { - const operation = operations[name]; - - jsonLogic.add_operation(operation.code || name , operation); -}); - -Object.keys(visitors).forEach(function(name) { - const visitor = visitors[name]; - - jsonLogic.add_visitor(visitor.code || name , visitor); -}); +const jsonLogic = createJsonLogic(operations, visitors); // restore original public API jsonLogic.is_logic = isLogic; From 02e84a66d3a1154796d0714d8e2a20aa489f844e Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 09:36:13 +0100 Subject: [PATCH 12/33] fix: grouped operations and visitors by kind --- src/operations/accessor/index.js | 3 ++ src/operations/{ => accessor}/missing.js | 0 src/operations/{ => accessor}/missingSome.js | 0 src/operations/{ => accessor}/variable.js | 0 src/operations/{ => arithmetic}/add.js | 0 src/operations/{ => arithmetic}/divide.js | 0 src/operations/arithmetic/index.js | 5 +++ src/operations/{ => arithmetic}/modulo.js | 0 src/operations/{ => arithmetic}/multiply.js | 0 src/operations/{ => arithmetic}/substract.js | 0 src/operations/array/index.js | 1 + src/operations/{ => array}/merge.js | 0 src/operations/index.js | 35 ++++---------------- src/operations/{ => logic}/equal.js | 0 src/operations/{ => logic}/falsy.js | 2 +- src/operations/logic/index.js | 6 ++++ src/operations/{ => logic}/notEqual.js | 0 src/operations/{ => logic}/strictEqual.js | 0 src/operations/{ => logic}/strictNotEqual.js | 0 src/operations/logic/truthy.js | 1 + src/operations/misc/index.js | 3 ++ src/operations/{ => misc}/indexOf.js | 0 src/operations/{ => misc}/log.js | 0 src/operations/{ => misc}/method.js | 0 src/operations/{ => numeric}/greater.js | 0 src/operations/{ => numeric}/greaterEqual.js | 0 src/operations/numeric/index.js | 6 ++++ src/operations/{ => numeric}/lower.js | 0 src/operations/{ => numeric}/lowerEqual.js | 0 src/operations/{ => numeric}/max.js | 0 src/operations/{ => numeric}/min.js | 0 src/operations/{ => string}/cat.js | 0 src/operations/string/index.js | 2 ++ src/operations/{ => string}/substr.js | 0 src/operations/truthy.js | 1 - src/visitors/{ => array}/all.js | 2 +- src/visitors/{ => array}/filter.js | 2 +- src/visitors/array/index.js | 6 ++++ src/visitors/{ => array}/map.js | 0 src/visitors/{ => array}/none.js | 0 src/visitors/{ => array}/reduce.js | 0 src/visitors/{ => array}/some.js | 0 src/visitors/index.js | 11 ++---- src/visitors/{ => logic}/and.js | 2 +- src/visitors/{ => logic}/condition.js | 2 +- src/visitors/logic/index.js | 3 ++ src/visitors/{ => logic}/or.js | 2 +- 47 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 src/operations/accessor/index.js rename src/operations/{ => accessor}/missing.js (100%) rename src/operations/{ => accessor}/missingSome.js (100%) rename src/operations/{ => accessor}/variable.js (100%) rename src/operations/{ => arithmetic}/add.js (100%) rename src/operations/{ => arithmetic}/divide.js (100%) create mode 100644 src/operations/arithmetic/index.js rename src/operations/{ => arithmetic}/modulo.js (100%) rename src/operations/{ => arithmetic}/multiply.js (100%) rename src/operations/{ => arithmetic}/substract.js (100%) create mode 100644 src/operations/array/index.js rename src/operations/{ => array}/merge.js (100%) rename src/operations/{ => logic}/equal.js (100%) rename src/operations/{ => logic}/falsy.js (67%) create mode 100644 src/operations/logic/index.js rename src/operations/{ => logic}/notEqual.js (100%) rename src/operations/{ => logic}/strictEqual.js (100%) rename src/operations/{ => logic}/strictNotEqual.js (100%) create mode 100644 src/operations/logic/truthy.js create mode 100644 src/operations/misc/index.js rename src/operations/{ => misc}/indexOf.js (100%) rename src/operations/{ => misc}/log.js (100%) rename src/operations/{ => misc}/method.js (100%) rename src/operations/{ => numeric}/greater.js (100%) rename src/operations/{ => numeric}/greaterEqual.js (100%) create mode 100644 src/operations/numeric/index.js rename src/operations/{ => numeric}/lower.js (100%) rename src/operations/{ => numeric}/lowerEqual.js (100%) rename src/operations/{ => numeric}/max.js (100%) rename src/operations/{ => numeric}/min.js (100%) rename src/operations/{ => string}/cat.js (100%) create mode 100644 src/operations/string/index.js rename src/operations/{ => string}/substr.js (100%) delete mode 100644 src/operations/truthy.js rename src/visitors/{ => array}/all.js (91%) rename src/visitors/{ => array}/filter.js (92%) create mode 100644 src/visitors/array/index.js rename src/visitors/{ => array}/map.js (100%) rename src/visitors/{ => array}/none.js (100%) rename src/visitors/{ => array}/reduce.js (100%) rename src/visitors/{ => array}/some.js (100%) rename src/visitors/{ => logic}/and.js (84%) rename src/visitors/{ => logic}/condition.js (95%) create mode 100644 src/visitors/logic/index.js rename src/visitors/{ => logic}/or.js (84%) diff --git a/src/operations/accessor/index.js b/src/operations/accessor/index.js new file mode 100644 index 0000000..0539a3e --- /dev/null +++ b/src/operations/accessor/index.js @@ -0,0 +1,3 @@ +export { default as variable } from './variable'; +export { default as missing } from './missing'; +export { default as missingSome } from './missingSome'; diff --git a/src/operations/missing.js b/src/operations/accessor/missing.js similarity index 100% rename from src/operations/missing.js rename to src/operations/accessor/missing.js diff --git a/src/operations/missingSome.js b/src/operations/accessor/missingSome.js similarity index 100% rename from src/operations/missingSome.js rename to src/operations/accessor/missingSome.js diff --git a/src/operations/variable.js b/src/operations/accessor/variable.js similarity index 100% rename from src/operations/variable.js rename to src/operations/accessor/variable.js diff --git a/src/operations/add.js b/src/operations/arithmetic/add.js similarity index 100% rename from src/operations/add.js rename to src/operations/arithmetic/add.js diff --git a/src/operations/divide.js b/src/operations/arithmetic/divide.js similarity index 100% rename from src/operations/divide.js rename to src/operations/arithmetic/divide.js diff --git a/src/operations/arithmetic/index.js b/src/operations/arithmetic/index.js new file mode 100644 index 0000000..78be23f --- /dev/null +++ b/src/operations/arithmetic/index.js @@ -0,0 +1,5 @@ +export { default as add } from './add'; +export { default as divide } from './divide'; +export { default as modulo } from './modulo'; +export { default as multiply } from './multiply'; +export { default as substract } from './substract'; diff --git a/src/operations/modulo.js b/src/operations/arithmetic/modulo.js similarity index 100% rename from src/operations/modulo.js rename to src/operations/arithmetic/modulo.js diff --git a/src/operations/multiply.js b/src/operations/arithmetic/multiply.js similarity index 100% rename from src/operations/multiply.js rename to src/operations/arithmetic/multiply.js diff --git a/src/operations/substract.js b/src/operations/arithmetic/substract.js similarity index 100% rename from src/operations/substract.js rename to src/operations/arithmetic/substract.js diff --git a/src/operations/array/index.js b/src/operations/array/index.js new file mode 100644 index 0000000..f2e5c7a --- /dev/null +++ b/src/operations/array/index.js @@ -0,0 +1 @@ +export { default as merge } from './merge'; diff --git a/src/operations/merge.js b/src/operations/array/merge.js similarity index 100% rename from src/operations/merge.js rename to src/operations/array/merge.js diff --git a/src/operations/index.js b/src/operations/index.js index a82b22e..b125bf9 100644 --- a/src/operations/index.js +++ b/src/operations/index.js @@ -1,28 +1,7 @@ -import missing from "./missingAccessor"; - -export { default as equal } from './equal'; -export { default as strictEqual } from './strictEqual'; -export { default as notEqual } from './notEqual'; -export { default as strictNotEqual } from './strictNotEqual'; -export { default as greater } from './greater'; -export { default as greaterEqual } from './greaterEqual'; -export { default as lower } from './lower'; -export { default as lowerEqual } from './lowerEqual'; -export { default as truthy } from './truthy'; -export { default as falsy } from './falsy'; -export { default as modulo } from './modulo'; -export { default as log } from './log'; -export { default as indexOf } from './indexOf'; -export { default as cat } from './cat'; -export { default as substr } from './substr'; -export { default as add } from './add'; -export { default as multiply } from './multiply'; -export { default as substract } from './substract'; -export { default as divide } from './divide'; -export { default as min } from './min'; -export { default as max } from './max'; -export { default as merge } from './merge'; -export { default as varAccessor } from './varAccessor'; -export { default as missingAccessor } from './missingAccessor'; -export { default as missingSomeAccessor } from './missingSomeAccessor'; -export { default as method } from './method'; +export * from './accessor'; +export * from './arithmetic'; +export * from './array'; +export * from './logic'; +export * from './misc'; +export * from './numeric'; +export * from './string'; diff --git a/src/operations/equal.js b/src/operations/logic/equal.js similarity index 100% rename from src/operations/equal.js rename to src/operations/logic/equal.js diff --git a/src/operations/falsy.js b/src/operations/logic/falsy.js similarity index 67% rename from src/operations/falsy.js rename to src/operations/logic/falsy.js index 7555401..bda904c 100644 --- a/src/operations/falsy.js +++ b/src/operations/logic/falsy.js @@ -1,4 +1,4 @@ -import truthy from '../helpers/truthy' +import truthy from '../../helpers/truthy' function falsy(a) { return !truthy(a); diff --git a/src/operations/logic/index.js b/src/operations/logic/index.js new file mode 100644 index 0000000..ae5ee99 --- /dev/null +++ b/src/operations/logic/index.js @@ -0,0 +1,6 @@ +export { default as equal } from './equal'; +export { default as falsy } from './falsy'; +export { default as notEqual } from './notEqual'; +export { default as strictEqual } from './strictEqual'; +export { default as strictNotEqual } from './strictNotEqual'; +export { default as truthy } from './truthy'; diff --git a/src/operations/notEqual.js b/src/operations/logic/notEqual.js similarity index 100% rename from src/operations/notEqual.js rename to src/operations/logic/notEqual.js diff --git a/src/operations/strictEqual.js b/src/operations/logic/strictEqual.js similarity index 100% rename from src/operations/strictEqual.js rename to src/operations/logic/strictEqual.js diff --git a/src/operations/strictNotEqual.js b/src/operations/logic/strictNotEqual.js similarity index 100% rename from src/operations/strictNotEqual.js rename to src/operations/logic/strictNotEqual.js diff --git a/src/operations/logic/truthy.js b/src/operations/logic/truthy.js new file mode 100644 index 0000000..9b23fc0 --- /dev/null +++ b/src/operations/logic/truthy.js @@ -0,0 +1 @@ +export '../../helpers/truthy'; diff --git a/src/operations/misc/index.js b/src/operations/misc/index.js new file mode 100644 index 0000000..3e72c79 --- /dev/null +++ b/src/operations/misc/index.js @@ -0,0 +1,3 @@ +export { default as indexOf } from './indexOf'; +export { default as log } from './log'; +export { default as method } from './method'; diff --git a/src/operations/indexOf.js b/src/operations/misc/indexOf.js similarity index 100% rename from src/operations/indexOf.js rename to src/operations/misc/indexOf.js diff --git a/src/operations/log.js b/src/operations/misc/log.js similarity index 100% rename from src/operations/log.js rename to src/operations/misc/log.js diff --git a/src/operations/method.js b/src/operations/misc/method.js similarity index 100% rename from src/operations/method.js rename to src/operations/misc/method.js diff --git a/src/operations/greater.js b/src/operations/numeric/greater.js similarity index 100% rename from src/operations/greater.js rename to src/operations/numeric/greater.js diff --git a/src/operations/greaterEqual.js b/src/operations/numeric/greaterEqual.js similarity index 100% rename from src/operations/greaterEqual.js rename to src/operations/numeric/greaterEqual.js diff --git a/src/operations/numeric/index.js b/src/operations/numeric/index.js new file mode 100644 index 0000000..8e66afc --- /dev/null +++ b/src/operations/numeric/index.js @@ -0,0 +1,6 @@ +export { default as greater } from './greater'; +export { default as greaterEqual } from './greaterEqual'; +export { default as lower } from './lower'; +export { default as lowerEqual } from './lowerEqual'; +export { default as max } from './numeric/max'; +export { default as min } from './numeric/min'; diff --git a/src/operations/lower.js b/src/operations/numeric/lower.js similarity index 100% rename from src/operations/lower.js rename to src/operations/numeric/lower.js diff --git a/src/operations/lowerEqual.js b/src/operations/numeric/lowerEqual.js similarity index 100% rename from src/operations/lowerEqual.js rename to src/operations/numeric/lowerEqual.js diff --git a/src/operations/max.js b/src/operations/numeric/max.js similarity index 100% rename from src/operations/max.js rename to src/operations/numeric/max.js diff --git a/src/operations/min.js b/src/operations/numeric/min.js similarity index 100% rename from src/operations/min.js rename to src/operations/numeric/min.js diff --git a/src/operations/cat.js b/src/operations/string/cat.js similarity index 100% rename from src/operations/cat.js rename to src/operations/string/cat.js diff --git a/src/operations/string/index.js b/src/operations/string/index.js new file mode 100644 index 0000000..d2b9410 --- /dev/null +++ b/src/operations/string/index.js @@ -0,0 +1,2 @@ +export { default as cat } from './cat'; +export { default as substr } from './substr'; diff --git a/src/operations/substr.js b/src/operations/string/substr.js similarity index 100% rename from src/operations/substr.js rename to src/operations/string/substr.js diff --git a/src/operations/truthy.js b/src/operations/truthy.js deleted file mode 100644 index cb575fd..0000000 --- a/src/operations/truthy.js +++ /dev/null @@ -1 +0,0 @@ -export '../helpers/truthy'; diff --git a/src/visitors/all.js b/src/visitors/array/all.js similarity index 91% rename from src/visitors/all.js rename to src/visitors/array/all.js index efdcff8..c0ce1ec 100644 --- a/src/visitors/all.js +++ b/src/visitors/array/all.js @@ -1,4 +1,4 @@ -import truthy from "../helpers/truthy"; +import truthy from "../../helpers/truthy"; function all(apply, data, values) { const scopedData = apply(values[0], data); diff --git a/src/visitors/filter.js b/src/visitors/array/filter.js similarity index 92% rename from src/visitors/filter.js rename to src/visitors/array/filter.js index 533b0c4..a7db461 100644 --- a/src/visitors/filter.js +++ b/src/visitors/array/filter.js @@ -1,4 +1,4 @@ -import truthy from "../helpers/truthy"; +import truthy from "../../helpers/truthy"; function filter(apply, data, values) { const scopedData = apply(values[0], data); diff --git a/src/visitors/array/index.js b/src/visitors/array/index.js new file mode 100644 index 0000000..8b11ac8 --- /dev/null +++ b/src/visitors/array/index.js @@ -0,0 +1,6 @@ +export { default as all } from './all' +export { default as filter } from './filter' +export { default as map } from './map' +export { default as none } from './none' +export { default as reduce } from './reduce' +export { default as some } from './some' diff --git a/src/visitors/map.js b/src/visitors/array/map.js similarity index 100% rename from src/visitors/map.js rename to src/visitors/array/map.js diff --git a/src/visitors/none.js b/src/visitors/array/none.js similarity index 100% rename from src/visitors/none.js rename to src/visitors/array/none.js diff --git a/src/visitors/reduce.js b/src/visitors/array/reduce.js similarity index 100% rename from src/visitors/reduce.js rename to src/visitors/array/reduce.js diff --git a/src/visitors/some.js b/src/visitors/array/some.js similarity index 100% rename from src/visitors/some.js rename to src/visitors/array/some.js diff --git a/src/visitors/index.js b/src/visitors/index.js index 7e5a50b..3876f22 100644 --- a/src/visitors/index.js +++ b/src/visitors/index.js @@ -1,9 +1,2 @@ -export { default as allVisitor } from './allVisitor' -export { default as andVisito } from './andVisito' -export { default as filterVisitor } from './filterVisitor' -export { default as ifVisitor } from './ifVisitor' -export { default as mapVisitor } from './mapVisitor' -export { default as noneVisitor } from './noneVisitor' -export { default as orVisitor } from './orVisitor' -export { default as reduceVisitor } from './reduceVisitor' -export { default as someVisitor } from './someVisitor' +export * from './array'; +export * from './logic'; diff --git a/src/visitors/and.js b/src/visitors/logic/and.js similarity index 84% rename from src/visitors/and.js rename to src/visitors/logic/and.js index 194e90f..709b40e 100644 --- a/src/visitors/and.js +++ b/src/visitors/logic/and.js @@ -1,4 +1,4 @@ -import truthy from "../helpers/truthy"; +import truthy from "../../helpers/truthy"; function and(apply, data, values) { let current; diff --git a/src/visitors/condition.js b/src/visitors/logic/condition.js similarity index 95% rename from src/visitors/condition.js rename to src/visitors/logic/condition.js index 8361e3e..43bc980 100644 --- a/src/visitors/condition.js +++ b/src/visitors/logic/condition.js @@ -1,4 +1,4 @@ -import truthy from '../helpers/truthy'; +import truthy from '../../helpers/truthy'; function condition(apply, data, values) { /* 'if' should be called with a odd number of parameters, 3 or greater diff --git a/src/visitors/logic/index.js b/src/visitors/logic/index.js new file mode 100644 index 0000000..eeeff26 --- /dev/null +++ b/src/visitors/logic/index.js @@ -0,0 +1,3 @@ +export { default as and } from './and' +export { default as condition } from './condition' +export { default as or } from './or' diff --git a/src/visitors/or.js b/src/visitors/logic/or.js similarity index 84% rename from src/visitors/or.js rename to src/visitors/logic/or.js index d3a6acc..9b633ab 100644 --- a/src/visitors/or.js +++ b/src/visitors/logic/or.js @@ -1,4 +1,4 @@ -import truthy from "../helpers/truthy"; +import truthy from "../../helpers/truthy"; function or(apply, data, values) { let current; From 8bebfc1d573fbc1586246a82abcd20d533627541 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 10:40:01 +0100 Subject: [PATCH 13/33] fix: imports and exports --- src/createJsonLogic.js | 4 ++-- src/operations/accessor/missing.js | 2 +- src/operations/accessor/missingSome.js | 2 +- src/operations/logic/truthy.js | 2 +- src/operations/numeric/index.js | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js index da8a361..7d9c648 100644 --- a/src/createJsonLogic.js +++ b/src/createJsonLogic.js @@ -1,5 +1,5 @@ -import isLogic from './isLogic'; -import getOperator from './getOperator'; +import isLogic from './helpers/isLogic'; +import getOperator from './helpers/getOperator'; function createJsonLogic(operations = {}, visitors = {}) { Object.keys(operations).forEach(function(name) { diff --git a/src/operations/accessor/missing.js b/src/operations/accessor/missing.js index 92ec924..faa2e76 100644 --- a/src/operations/accessor/missing.js +++ b/src/operations/accessor/missing.js @@ -1,4 +1,4 @@ -import variable from './varAccessor' +import variable from './variable' function missing() { /* diff --git a/src/operations/accessor/missingSome.js b/src/operations/accessor/missingSome.js index 668e7b9..d139925 100644 --- a/src/operations/accessor/missingSome.js +++ b/src/operations/accessor/missingSome.js @@ -1,4 +1,4 @@ -import missing from './missingAccessor'; +import missing from './missing'; function missingSome(need_count, options) { // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. diff --git a/src/operations/logic/truthy.js b/src/operations/logic/truthy.js index 9b23fc0..3ee7149 100644 --- a/src/operations/logic/truthy.js +++ b/src/operations/logic/truthy.js @@ -1 +1 @@ -export '../../helpers/truthy'; +export default from '../../helpers/truthy'; diff --git a/src/operations/numeric/index.js b/src/operations/numeric/index.js index 8e66afc..4cb644d 100644 --- a/src/operations/numeric/index.js +++ b/src/operations/numeric/index.js @@ -2,5 +2,5 @@ export { default as greater } from './greater'; export { default as greaterEqual } from './greaterEqual'; export { default as lower } from './lower'; export { default as lowerEqual } from './lowerEqual'; -export { default as max } from './numeric/max'; -export { default as min } from './numeric/min'; +export { default as max } from './max'; +export { default as min } from './min'; From 21bb46d568025c510486b712fc86be22fda42dfa Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 11:45:57 +0100 Subject: [PATCH 14/33] fix: cached isArray --- src/createJsonLogic.js | 9 +++++---- src/helpers/isArray.js | 1 + src/helpers/isLogic.js | 4 +++- src/helpers/ruleLike.js | 7 ++++--- src/helpers/truthy.js | 4 +++- src/helpers/usesData.js | 3 ++- src/index.js | 2 +- src/operations/accessor/missing.js | 3 ++- src/visitors/array/filter.js | 3 ++- src/visitors/array/map.js | 4 +++- src/visitors/array/reduce.js | 4 +++- 11 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 src/helpers/isArray.js diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js index 7d9c648..ceb6ef8 100644 --- a/src/createJsonLogic.js +++ b/src/createJsonLogic.js @@ -1,3 +1,4 @@ +import isArray from './helpers/isArray'; import isLogic from './helpers/isLogic'; import getOperator from './helpers/getOperator'; @@ -23,7 +24,7 @@ function createJsonLogic(operations = {}, visitors = {}) { } function addVisitor(name, code) { - if (Array.isArray(name)) { + if (isArray(name)) { name.forEach(addVisitor); } @@ -31,7 +32,7 @@ function createJsonLogic(operations = {}, visitors = {}) { } function removeVisitor(name) { - if (Array.isArray(name)) { + if (isArray(name)) { name.forEach(removeVisitor); } @@ -40,7 +41,7 @@ function createJsonLogic(operations = {}, visitors = {}) { function apply(logic, data) { // Does this array contain logic? Only one way to find out. - if(Array.isArray(logic)) { + if(isArray(logic)) { return logic.map(function(l) { return apply(l, data); }); @@ -57,7 +58,7 @@ function createJsonLogic(operations = {}, visitors = {}) { let i; // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} - if( ! Array.isArray(values)) { + if( ! isArray(values)) { values = [values]; } diff --git a/src/helpers/isArray.js b/src/helpers/isArray.js new file mode 100644 index 0000000..cc2cea0 --- /dev/null +++ b/src/helpers/isArray.js @@ -0,0 +1 @@ +export default Array.isArray; diff --git a/src/helpers/isLogic.js b/src/helpers/isLogic.js index 06e227b..05a0db9 100644 --- a/src/helpers/isLogic.js +++ b/src/helpers/isLogic.js @@ -1,8 +1,10 @@ +import isArray from './isArray'; + function isLogic(logic) { return ( typeof logic === "object" && // An object logic !== null && // but not null - !Array.isArray(logic) && // and not an array + !isArray(logic) && // and not an array Object.keys(logic).length === 1 // with exactly one key ); } diff --git a/src/helpers/ruleLike.js b/src/helpers/ruleLike.js index 525f85c..0c856f9 100644 --- a/src/helpers/ruleLike.js +++ b/src/helpers/ruleLike.js @@ -1,3 +1,4 @@ +import isArray from './isArray'; import isLogic from './isLogic'; import getOperator from './getOperator'; import getValues from './getValues'; @@ -18,7 +19,7 @@ function ruleLike(rule, pattern) { } if(pattern === "array") { // !logic test might be superfluous in JavaScript - return Array.isArray(rule) && ! isLogic(rule); + return isArray(rule) && ! isLogic(rule); } if(isLogic(pattern)) { @@ -37,8 +38,8 @@ function ruleLike(rule, pattern) { return false; // pattern is logic, rule isn't, can't be eq } - if(Array.isArray(pattern)) { - if(Array.isArray(rule)) { + if(isArray(pattern)) { + if(isArray(rule)) { if(pattern.length !== rule.length) { return false; } diff --git a/src/helpers/truthy.js b/src/helpers/truthy.js index 6cf16f2..d748384 100644 --- a/src/helpers/truthy.js +++ b/src/helpers/truthy.js @@ -1,10 +1,12 @@ +import isArray from './isArray'; + /* This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. Spec and rationale here: http://jsonlogic.com/truthy */ function truthy(a) { - if(Array.isArray(value) && value.length === 0) { + if(isArray(value) && value.length === 0) { return false; } diff --git a/src/helpers/usesData.js b/src/helpers/usesData.js index bcdcf4b..3956672 100644 --- a/src/helpers/usesData.js +++ b/src/helpers/usesData.js @@ -1,3 +1,4 @@ +import isArray from './isArray'; import isLogic from './isLogic'; import getOperator from './getOperator'; import arrayUnique from './arrayUnique'; @@ -9,7 +10,7 @@ function usesData(logic) { var op = getOperator(logic); var values = logic[op]; - if( ! Array.isArray(values)) { + if( ! isArray(values)) { values = [values]; } diff --git a/src/index.js b/src/index.js index 0aa21d3..f26a1c0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -export createJsonLogic from './createJsonLogic'; +import createJsonLogic from './createJsonLogic'; import * as operations from './operations'; import * as visitors from './visitors'; import isLogic from "./helpers/isLogic"; diff --git a/src/operations/accessor/missing.js b/src/operations/accessor/missing.js index faa2e76..a55a7c9 100644 --- a/src/operations/accessor/missing.js +++ b/src/operations/accessor/missing.js @@ -1,3 +1,4 @@ +import isArray from '../../helpers/isArray'; import variable from './variable' function missing() { @@ -9,7 +10,7 @@ function missing() { */ var missing = []; - var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments; + var keys = isArray(arguments[0]) ? arguments[0] : arguments; for(var i = 0; i < keys.length; i++) { var key = keys[i]; diff --git a/src/visitors/array/filter.js b/src/visitors/array/filter.js index a7db461..c85fe43 100644 --- a/src/visitors/array/filter.js +++ b/src/visitors/array/filter.js @@ -1,10 +1,11 @@ +import isArray from '../../helpers/isArray'; import truthy from "../../helpers/truthy"; function filter(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; - if ( ! Array.isArray(scopedData)) { + if ( ! isArray(scopedData)) { return []; } // Return only the elements from the array in the first argument, diff --git a/src/visitors/array/map.js b/src/visitors/array/map.js index 28b4b8b..edab4aa 100644 --- a/src/visitors/array/map.js +++ b/src/visitors/array/map.js @@ -1,8 +1,10 @@ +import isArray from '../../helpers/isArray'; + function map(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; - if ( ! Array.isArray(scopedData)) { + if ( ! isArray(scopedData)) { return []; } diff --git a/src/visitors/array/reduce.js b/src/visitors/array/reduce.js index ace8611..3c61ae6 100644 --- a/src/visitors/array/reduce.js +++ b/src/visitors/array/reduce.js @@ -1,9 +1,11 @@ +import isArray from '../../helpers/isArray'; + function reduce(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; const initial = typeof values[2] !== 'undefined' ? values[2] : null; - if ( ! Array.isArray(scopedData)) { + if ( ! isArray(scopedData)) { return initial; } From 7566f8ae2a1469ca907f5c1476e505f1600b3d20 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 13:01:13 +0100 Subject: [PATCH 15/33] fix: broken tests --- src/createJsonLogic.js | 38 ++++++++++++++++++-------- src/helpers/truthy.js | 2 +- src/operations/accessor/missing.js | 8 ++++-- src/operations/accessor/missingSome.js | 5 ++-- src/operations/arithmetic/substract.js | 2 ++ src/operations/logic/strictNotEqual.js | 2 +- src/operations/logic/truthy.js | 6 +++- src/visitors/accessor/missing.js | 27 ++++++++++++++++++ src/visitors/accessor/missingSome.js | 16 +++++++++++ src/visitors/logic/condition.js | 4 ++- 10 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 src/visitors/accessor/missing.js create mode 100644 src/visitors/accessor/missingSome.js diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js index ceb6ef8..a375fb9 100644 --- a/src/createJsonLogic.js +++ b/src/createJsonLogic.js @@ -2,18 +2,25 @@ import isArray from './helpers/isArray'; import isLogic from './helpers/isLogic'; import getOperator from './helpers/getOperator'; -function createJsonLogic(operations = {}, visitors = {}) { - Object.keys(operations).forEach(function(name) { - const operation = operations[name]; +function createJsonLogic(_operations, _visitors) { + const operations = {}; + const visitors = {}; - addOperation(operation.code || name , operation); - }); + if (_operations) { + Object.keys(_operations).forEach(function(name) { + const operation = _operations[name]; - Object.keys(visitors).forEach(function(name) { - const visitor = visitors[name]; + addOperation(operation.code || name , operation); + }); + } - addVisitor(visitor.code || name , visitor); - }); + if (_visitors) { + Object.keys(_visitors).forEach(function (name) { + const visitor = _visitors[name]; + + addVisitor(visitor.code || name, visitor); + }); + } function addOperation(name, code) { operations[name] = code; @@ -25,7 +32,8 @@ function createJsonLogic(operations = {}, visitors = {}) { function addVisitor(name, code) { if (isArray(name)) { - name.forEach(addVisitor); + name.forEach((key) => addVisitor(key, code)); + return; } visitors[name] = code; @@ -34,6 +42,7 @@ function createJsonLogic(operations = {}, visitors = {}) { function removeVisitor(name) { if (isArray(name)) { name.forEach(removeVisitor); + return; } delete visitors[name]; @@ -75,8 +84,13 @@ function createJsonLogic(operations = {}, visitors = {}) { // The operation is called with "data" bound to its "this" and "values" passed as arguments. // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments - if(typeof operations[op] === "function") { - return operations[op].apply(data, values); + const operator = operations[op]; + if(typeof operator === "function") { + if (operator.withApply) { + values.unshift(apply); + } + + return operator.apply(data, values); }else if(op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position var sub_ops = String(op).split("."); var operation = operations; diff --git a/src/helpers/truthy.js b/src/helpers/truthy.js index d748384..ab07d6b 100644 --- a/src/helpers/truthy.js +++ b/src/helpers/truthy.js @@ -5,7 +5,7 @@ import isArray from './isArray'; Spec and rationale here: http://jsonlogic.com/truthy */ -function truthy(a) { +function truthy(value) { if(isArray(value) && value.length === 0) { return false; } diff --git a/src/operations/accessor/missing.js b/src/operations/accessor/missing.js index a55a7c9..0af0618 100644 --- a/src/operations/accessor/missing.js +++ b/src/operations/accessor/missing.js @@ -1,7 +1,7 @@ import isArray from '../../helpers/isArray'; import variable from './variable' -function missing() { +function missing(apply, ...args) { /* Missing can receive many keys as many arguments, like {"missing:[1,2]} Missing can also receive *one* argument that is an array of keys, @@ -10,11 +10,11 @@ function missing() { */ var missing = []; - var keys = isArray(arguments[0]) ? arguments[0] : arguments; + var keys = isArray(args[0]) ? args[0] : args; for(var i = 0; i < keys.length; i++) { var key = keys[i]; - var value = variable.call(this, {"var": key}); + var value = apply({"var": key}, this); if(value === null || value === "") { missing.push(key); } @@ -23,4 +23,6 @@ function missing() { return missing; }; +missing.withApply = true; + export default missing; diff --git a/src/operations/accessor/missingSome.js b/src/operations/accessor/missingSome.js index d139925..2889cf1 100644 --- a/src/operations/accessor/missingSome.js +++ b/src/operations/accessor/missingSome.js @@ -1,8 +1,8 @@ import missing from './missing'; -function missingSome(need_count, options) { +function missingSome(apply, need_count, options) { // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. - var are_missing = missing.call(this, {"missing": options}); + const are_missing = apply({"missing": options}, this); if(options.length - are_missing.length >= need_count) { return []; @@ -12,5 +12,6 @@ function missingSome(need_count, options) { } missingSome.code = 'missing_some'; +missingSome.withApply = true; export default missingSome; diff --git a/src/operations/arithmetic/substract.js b/src/operations/arithmetic/substract.js index 94caeaf..24c2d95 100644 --- a/src/operations/arithmetic/substract.js +++ b/src/operations/arithmetic/substract.js @@ -6,4 +6,6 @@ function substract(a, b) { } } +substract.code = '-'; + export default substract; diff --git a/src/operations/logic/strictNotEqual.js b/src/operations/logic/strictNotEqual.js index 5d9cbcc..09b3666 100644 --- a/src/operations/logic/strictNotEqual.js +++ b/src/operations/logic/strictNotEqual.js @@ -2,6 +2,6 @@ function strictNotEqual(a, b) { return a !== b; } -strictNotEqual.code = '1=='; +strictNotEqual.code = '!=='; export default strictNotEqual; diff --git a/src/operations/logic/truthy.js b/src/operations/logic/truthy.js index 3ee7149..60afc96 100644 --- a/src/operations/logic/truthy.js +++ b/src/operations/logic/truthy.js @@ -1 +1,5 @@ -export default from '../../helpers/truthy'; +import truthy from '../../helpers/truthy' + +truthy.code = '!!'; + +export default truthy; diff --git a/src/visitors/accessor/missing.js b/src/visitors/accessor/missing.js new file mode 100644 index 0000000..8dee315 --- /dev/null +++ b/src/visitors/accessor/missing.js @@ -0,0 +1,27 @@ +import isArray from '../../helpers/isArray'; +import variable from '../../operations/accessor/variable' + +function missing() { + /* + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ + + const missing = []; + const keys = isArray(arguments[0]) ? arguments[0] : arguments; + + for(let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = variable.call(this, {"var": key}, arguments); + + if(value === null || value === "") { + missing.push(key); + } + } + + return missing; +}; + +export default missing; diff --git a/src/visitors/accessor/missingSome.js b/src/visitors/accessor/missingSome.js new file mode 100644 index 0000000..d139925 --- /dev/null +++ b/src/visitors/accessor/missingSome.js @@ -0,0 +1,16 @@ +import missing from './missing'; + +function missingSome(need_count, options) { + // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. + var are_missing = missing.call(this, {"missing": options}); + + if(options.length - are_missing.length >= need_count) { + return []; + }else{ + return are_missing; + } +} + +missingSome.code = 'missing_some'; + +export default missingSome; diff --git a/src/visitors/logic/condition.js b/src/visitors/logic/condition.js index 43bc980..dfc5883 100644 --- a/src/visitors/logic/condition.js +++ b/src/visitors/logic/condition.js @@ -1,6 +1,8 @@ import truthy from '../../helpers/truthy'; function condition(apply, data, values) { + let i; + /* 'if' should be called with a odd number of parameters, 3 or greater This works on the pattern: if( 0 ){ 1 }else{ 2 }; @@ -14,7 +16,7 @@ function condition(apply, data, values) { given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) given 0 parameters, return NULL (not great practice, but there was no Else) */ - for(let i = 0; i < values.length - 1; i += 2) { + for(i = 0; i < values.length - 1; i += 2) { if( truthy( apply(values[i], data) ) ) { return apply(values[i+1], data); } From 859945b228425da27aed79056ef6d28905cb5b70 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 13:18:22 +0100 Subject: [PATCH 16/33] refactore: enabled babel, rollup, tests,... --- .babelrc | 14 ++++++++++++++ .gitignore | 2 ++ gulpfile.js | 18 ------------------ package.json | 27 +++++++++++++++++++++++---- rollup.config.js | 22 ++++++++++++++++++++++ tests/testrunner.js | 4 ++-- tests/tests.js | 6 +++--- 7 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 .babelrc delete mode 100755 gulpfile.js create mode 100644 rollup.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..5ca31e5 --- /dev/null +++ b/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "modules": false, + "useBuiltIns": false + } + ] + ], + "plugins": [ + "@babel/plugin-proposal-export-default-from" + ] +} diff --git a/.gitignore b/.gitignore index ab3a2e7..bc9ffb1 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ node_modules tests/*.json +package-lock.json + # editor and IDE remnants *~ .idea/ diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100755 index 75c7add..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,18 +0,0 @@ -var gulp = require("gulp"); -var exec = require("child_process").exec; - -gulp.task("test", function(cb) { - exec( - "node testrunner.js", - {cwd: "tests"}, - function(err, stdout, stderr) { - console.log(stdout); - console.log(stderr); - cb(err); - } - ); -}); - -gulp.task("default", function() { - gulp.watch(["**/*.js", "tests/tests.json"], ["test"]); -}); diff --git a/package.json b/package.json index 201ef3b..b9ba761 100644 --- a/package.json +++ b/package.json @@ -6,16 +6,35 @@ "directories": { "test": "tests" }, - "dependencies": {}, + "dependencies": { + "@babel/polyfill": "^7.2.5" + }, "devDependencies": { + "@babel/cli": "^7.2.3", + "@babel/core": "^7.3.3", + "@babel/plugin-proposal-export-default-from": "^7.2.0", + "@babel/preset-env": "^7.3.1", "eslint": "^3.9.1", "eslint-config-google": "^0.7.0", - "gulp": "^3.9.0", "qunit": "^0.7.7", - "request": "^2.65.0" + "request": "^2.65.0", + "rimraf": "^2.6.3", + "rollup": "^1.2.2", + "rollup-plugin-babel": "^4.3.2", + "rollup-plugin-filesize": "^6.0.1", + "rollup-plugin-node-resolve": "^4.0.1", + "rollup-plugin-optimize-js": "0.0.4", + "rollup-plugin-uglify": "^6.0.2" }, "scripts": { - "test": "gulp test" + "test": "node tests/testrunner.js", + "pretest": "npm run build-package", + "build": "npm run build-lib", + "build-lib": "babel src --out-dir lib", + "prebuild-lib": "rimraf lib", + "postbuild-lib": "npm run build-package", + "build-package": "rollup --config", + "prebuild-package": "rimraf dist" }, "repository": { "type": "git", diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..91703ae --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,22 @@ +const babel = require('rollup-plugin-babel'); +const resolve = require('rollup-plugin-node-resolve'); +const uglify = require('rollup-plugin-uglify'); +const optimize = require('rollup-plugin-optimize-js'); +const filesize = require('rollup-plugin-filesize'); + +module.exports = { + input: 'src/index.js', + plugins: [ + resolve(), + babel(), + // uglify.uglify(), + // optimize(), + filesize({ showMinifiedSize: false }), + ], + output: { + file: 'dist/jsonLogic.js', + format: 'umd', + name: 'jsonLogic', + exports: 'default', + } +}; diff --git a/tests/testrunner.js b/tests/testrunner.js index e1dbb08..2e9f55d 100644 --- a/tests/testrunner.js +++ b/tests/testrunner.js @@ -9,8 +9,8 @@ testrunner.setup({ // specify dependency testrunner.run({ - code: "../logic.js", - tests: "tests.js" + code: "./dist/jsonLogic.js", + tests: "./tests/tests.js" }, function(err, report) { if(err) console.dir(err); // console.dir(report); diff --git a/tests/tests.js b/tests/tests.js index b201bf6..8b05760 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,4 +1,4 @@ -var jsonLogic = require("../logic.js"); +var jsonLogic = require("../dist/jsonLogic.js"); var http = require("http"); var fs = require("fs"); @@ -58,7 +58,7 @@ var remote_or_cache = function (remote_url, local_file, description, runner){ remote_or_cache( "http://jsonlogic.com/tests.json", - "tests.json", + "tests/tests.json", "applies() tests", function(test){ var rule = test[0]; @@ -77,7 +77,7 @@ remote_or_cache( remote_or_cache( "http://jsonlogic.com/rule_like.json", - "rule_like.json", + "tests/rule_like.json", "rule_like() tests", function(test){ var rule = test[0]; From f68c1653bb5fac0eddb7e5857ba8bd1056e12f11 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 15:39:36 +0100 Subject: [PATCH 17/33] fix: upgraded qunit --- package.json | 4 +- tests/testrunner.js | 17 ------- tests/tests.js | 107 ++++++++++++++++++++++---------------------- 3 files changed, 55 insertions(+), 73 deletions(-) delete mode 100644 tests/testrunner.js diff --git a/package.json b/package.json index b9ba761..c41f135 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@babel/preset-env": "^7.3.1", "eslint": "^3.9.1", "eslint-config-google": "^0.7.0", - "qunit": "^0.7.7", + "qunit": "^2.9.2", "request": "^2.65.0", "rimraf": "^2.6.3", "rollup": "^1.2.2", @@ -27,7 +27,7 @@ "rollup-plugin-uglify": "^6.0.2" }, "scripts": { - "test": "node tests/testrunner.js", + "test": "qunit 'tests/**/*.js' -r tap", "pretest": "npm run build-package", "build": "npm run build-lib", "build-lib": "babel src --out-dir lib", diff --git a/tests/testrunner.js b/tests/testrunner.js deleted file mode 100644 index 2e9f55d..0000000 --- a/tests/testrunner.js +++ /dev/null @@ -1,17 +0,0 @@ -var testrunner = require("qunit"); - -testrunner.setup({ - log: { - summary: true, - errors: true - } -}); - -// specify dependency -testrunner.run({ - code: "./dist/jsonLogic.js", - tests: "./tests/tests.js" -}, function(err, report) { - if(err) console.dir(err); - // console.dir(report); -}); diff --git a/tests/tests.js b/tests/tests.js index 8b05760..15afe2d 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,6 +1,6 @@ -var jsonLogic = require("../dist/jsonLogic.js"); var http = require("http"); var fs = require("fs"); +var jsonLogic = require("../dist/jsonLogic.js"); var download = function(url, dest, cb) { var file = fs.createWriteStream(dest); @@ -15,52 +15,57 @@ var download = function(url, dest, cb) { }); }; -var remote_or_cache = function (remote_url, local_file, description, runner){ - var parse_and_iterate = function(local_file, description, runner){ - fs.readFile(local_file, "utf8", function(error, body) { - var tests; - try{ - tests = JSON.parse(body); - }catch(e) { - throw new Error("Trouble parsing " + description + ": " + e.message); - } - - // Remove comments - tests = tests.filter(function(test){ return typeof test !== 'string'; }); - - console.log("Including "+tests.length+" "+description); - - QUnit.test(description, function(assert) { - tests.map(runner); +var remote_or_cache = function (remote_url, local_file, description, runner) { + QUnit.test('Load and run remote tests form: ' + remote_url, function(assert) { + assert.expect(0); + // Only waiting on the request() is async + var done = assert.async(); + + var parse_and_iterate = function (local_file, description, runner) { + fs.readFile(local_file, "utf8", function (error, body) { + var tests; + try { + tests = JSON.parse(body); + } catch (e) { + throw new Error("Trouble parsing " + description + ": " + e.message); + } + + // Remove comments + tests = tests.filter(function (test) { + return typeof test !== 'string'; + }); + + console.log("Including " + tests.length + " " + description); + + QUnit.test(description, function (assert) { + tests.map(function(test) { + runner(test, assert); + }); + }); + + done(); }); - - start(); - }); - - }; - - // Only waiting on the request() is async - stop(); - - fs.stat(local_file, function(err, stats) { - if(err) { - console.log("Downloading " + description + " from JsonLogic.com"); - download(remote_url, local_file, function() { + }; + + fs.stat(local_file, function (err, stats) { + if (err) { + console.log("Downloading " + description + " from JsonLogic.com"); + download(remote_url, local_file, function () { + parse_and_iterate(local_file, description, runner); + }); + } else { + console.log("Using cached " + description); parse_and_iterate(local_file, description, runner); - }); - }else{ - console.log("Using cached " + description); - parse_and_iterate(local_file, description, runner); - } + } + }); }); - }; remote_or_cache( "http://jsonlogic.com/tests.json", "tests/tests.json", "applies() tests", - function(test){ + function(test, assert){ var rule = test[0]; var data = test[1]; var expected = test[2]; @@ -69,8 +74,8 @@ remote_or_cache( jsonLogic.apply(rule, data), expected, "jsonLogic.apply("+ JSON.stringify(rule) +"," + - JSON.stringify(data) +") === " + - JSON.stringify(expected) + JSON.stringify(data) +") === " + + JSON.stringify(expected) ); } ); @@ -79,7 +84,7 @@ remote_or_cache( "http://jsonlogic.com/rule_like.json", "tests/rule_like.json", "rule_like() tests", - function(test){ + function(test, assert){ var rule = test[0]; var pattern = test[1]; var expected = test[2]; @@ -88,17 +93,12 @@ remote_or_cache( jsonLogic.rule_like(rule, pattern), expected, "jsonLogic.rule_like("+ JSON.stringify(rule) +"," + - JSON.stringify(pattern) +") === " + - JSON.stringify(expected) + JSON.stringify(pattern) +") === " + + JSON.stringify(expected) ); } ); - - - - - QUnit.test( "Bad operator", function( assert ) { assert.throws( function() { @@ -108,14 +108,16 @@ QUnit.test( "Bad operator", function( assert ) { ); }); - QUnit.test( "logging", function( assert ) { var last_console; + var log = console.log; console.log = function(logged) { last_console = logged; }; assert.equal( jsonLogic.apply({"log": [1]}), 1 ); assert.equal( last_console, 1 ); + + delete console.log; }); QUnit.test( "edge cases", function( assert ) { @@ -172,7 +174,7 @@ QUnit.test( "Expanding functionality with add_operator", function( assert) { 42 ); - //Remove operation: + // Remove operation: jsonLogic.rm_operation("times"); assert.throws( @@ -193,9 +195,6 @@ QUnit.test( "Expanding functionality with add_operator", function( assert) { ), 42 ); - - - }); QUnit.test( "Expanding functionality with method", function( assert) { @@ -232,7 +231,7 @@ QUnit.test( "Expanding functionality with method", function( assert) { }); -QUnit.test("Control structures don't eval depth-first", function(assert) { +QUnit.test( "Control structures don't eval depth-first", function(assert) { // Depth-first recursion was wasteful but not harmful until we added custom operations that could have side-effects. // If operations run the condition, if truthy, it runs and returns that consequent. From 1518cb3f33c024f5475904196e4cc84418678806 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 15:55:14 +0100 Subject: [PATCH 18/33] refactore: build --- package.json | 5 +++-- rollup.config.js | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index c41f135..fbf6299 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/core": "^7.3.3", "@babel/plugin-proposal-export-default-from": "^7.2.0", "@babel/preset-env": "^7.3.1", + "cross-env": "^5.2.0", "eslint": "^3.9.1", "eslint-config-google": "^0.7.0", "qunit": "^2.9.2", @@ -27,9 +28,9 @@ "rollup-plugin-uglify": "^6.0.2" }, "scripts": { - "test": "qunit 'tests/**/*.js' -r tap", + "test": "cross-env NODE_ENV=test qunit 'tests/**/*.js' -r tap", "pretest": "npm run build-package", - "build": "npm run build-lib", + "build": "cross-env NODE_ENV=production npm run build-lib", "build-lib": "babel src --out-dir lib", "prebuild-lib": "rimraf lib", "postbuild-lib": "npm run build-package", diff --git a/rollup.config.js b/rollup.config.js index 91703ae..10d92eb 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,13 +4,11 @@ const uglify = require('rollup-plugin-uglify'); const optimize = require('rollup-plugin-optimize-js'); const filesize = require('rollup-plugin-filesize'); -module.exports = { +module.exports = [{ input: 'src/index.js', plugins: [ resolve(), babel(), - // uglify.uglify(), - // optimize(), filesize({ showMinifiedSize: false }), ], output: { @@ -19,4 +17,19 @@ module.exports = { name: 'jsonLogic', exports: 'default', } -}; +}, { + input: 'src/index.js', + plugins: [ + resolve(), + babel(), + uglify.uglify(), + optimize(), + filesize({ showMinifiedSize: false }), + ], + output: { + file: 'dist/jsonLogic.min.js', + format: 'umd', + name: 'jsonLogic', + exports: 'default', + } +}]; From bb9cb75f9eff99ff693e4d7a9d86beb60aa78dd0 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 15:58:00 +0100 Subject: [PATCH 19/33] feat: added module, browser and main field --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index fbf6299..30b33c1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,9 @@ "name": "json-logic-js", "version": "1.2.2", "description": "Build complex rules, serialize them as JSON, and execute them in JavaScript", - "main": "logic.js", + "main": "dist/jsonLogic.js", + "browser": "dist/jsonLogic.min.js", + "module": "lib/index.js", "directories": { "test": "tests" }, From 4279063d2c7c7e1db64fc06fd79bd539cf5ef2d0 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 16:01:46 +0100 Subject: [PATCH 20/33] feat: enable source maps --- rollup.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rollup.config.js b/rollup.config.js index 10d92eb..0ba2de3 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -16,6 +16,7 @@ module.exports = [{ format: 'umd', name: 'jsonLogic', exports: 'default', + sourcemap: true, } }, { input: 'src/index.js', @@ -31,5 +32,6 @@ module.exports = [{ format: 'umd', name: 'jsonLogic', exports: 'default', + sourcemap: true, } }]; From f605f04f9b0f20da6cc0372b58bca31be3007153 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 16:03:28 +0100 Subject: [PATCH 21/33] chore: rebuild --- dist/jsonLogic.js | 694 +++++++++++++++++++++++++ dist/jsonLogic.js.map | 1 + dist/jsonLogic.min.js | 2 + dist/jsonLogic.min.js.map | 1 + lib/createJsonLogic.js | 122 +++++ lib/helpers/arrayUnique.js | 18 + lib/helpers/getOperator.js | 5 + lib/helpers/getValues.js | 7 + lib/helpers/isArray.js | 1 + lib/helpers/isLogic.js | 13 + lib/helpers/ruleLike.js | 72 +++ lib/helpers/truthy.js | 16 + lib/helpers/usesData.js | 32 ++ lib/index.js | 18 + lib/operations/accessor/index.js | 3 + lib/operations/accessor/missing.js | 35 ++ lib/operations/accessor/missingSome.js | 18 + lib/operations/accessor/variable.js | 28 + lib/operations/arithmetic/add.js | 12 + lib/operations/arithmetic/divide.js | 6 + lib/operations/arithmetic/index.js | 5 + lib/operations/arithmetic/modulo.js | 6 + lib/operations/arithmetic/multiply.js | 12 + lib/operations/arithmetic/substract.js | 10 + lib/operations/array/index.js | 1 + lib/operations/array/merge.js | 11 + lib/operations/index.js | 7 + lib/operations/logic/equal.js | 6 + lib/operations/logic/falsy.js | 8 + lib/operations/logic/index.js | 6 + lib/operations/logic/notEqual.js | 6 + lib/operations/logic/strictEqual.js | 6 + lib/operations/logic/strictNotEqual.js | 6 + lib/operations/logic/truthy.js | 3 + lib/operations/misc/index.js | 3 + lib/operations/misc/indexOf.js | 7 + lib/operations/misc/log.js | 6 + lib/operations/misc/method.js | 5 + lib/operations/numeric/greater.js | 6 + lib/operations/numeric/greaterEqual.js | 6 + lib/operations/numeric/index.js | 6 + lib/operations/numeric/lower.js | 6 + lib/operations/numeric/lowerEqual.js | 6 + lib/operations/numeric/max.js | 5 + lib/operations/numeric/min.js | 5 + lib/operations/string/cat.js | 5 + lib/operations/string/index.js | 2 + lib/operations/string/substr.js | 12 + lib/visitors/accessor/missing.js | 29 ++ lib/visitors/accessor/missingSome.js | 17 + lib/visitors/array/all.js | 20 + lib/visitors/array/filter.js | 20 + lib/visitors/array/index.js | 6 + lib/visitors/array/map.js | 16 + lib/visitors/array/none.js | 8 + lib/visitors/array/reduce.js | 20 + lib/visitors/array/some.js | 8 + lib/visitors/index.js | 2 + lib/visitors/logic/and.js | 17 + lib/visitors/logic/condition.js | 32 ++ lib/visitors/logic/index.js | 3 + lib/visitors/logic/or.js | 17 + 62 files changed, 1492 insertions(+) create mode 100644 dist/jsonLogic.js create mode 100644 dist/jsonLogic.js.map create mode 100644 dist/jsonLogic.min.js create mode 100644 dist/jsonLogic.min.js.map create mode 100644 lib/createJsonLogic.js create mode 100644 lib/helpers/arrayUnique.js create mode 100644 lib/helpers/getOperator.js create mode 100644 lib/helpers/getValues.js create mode 100644 lib/helpers/isArray.js create mode 100644 lib/helpers/isLogic.js create mode 100644 lib/helpers/ruleLike.js create mode 100644 lib/helpers/truthy.js create mode 100644 lib/helpers/usesData.js create mode 100644 lib/index.js create mode 100644 lib/operations/accessor/index.js create mode 100644 lib/operations/accessor/missing.js create mode 100644 lib/operations/accessor/missingSome.js create mode 100644 lib/operations/accessor/variable.js create mode 100644 lib/operations/arithmetic/add.js create mode 100644 lib/operations/arithmetic/divide.js create mode 100644 lib/operations/arithmetic/index.js create mode 100644 lib/operations/arithmetic/modulo.js create mode 100644 lib/operations/arithmetic/multiply.js create mode 100644 lib/operations/arithmetic/substract.js create mode 100644 lib/operations/array/index.js create mode 100644 lib/operations/array/merge.js create mode 100644 lib/operations/index.js create mode 100644 lib/operations/logic/equal.js create mode 100644 lib/operations/logic/falsy.js create mode 100644 lib/operations/logic/index.js create mode 100644 lib/operations/logic/notEqual.js create mode 100644 lib/operations/logic/strictEqual.js create mode 100644 lib/operations/logic/strictNotEqual.js create mode 100644 lib/operations/logic/truthy.js create mode 100644 lib/operations/misc/index.js create mode 100644 lib/operations/misc/indexOf.js create mode 100644 lib/operations/misc/log.js create mode 100644 lib/operations/misc/method.js create mode 100644 lib/operations/numeric/greater.js create mode 100644 lib/operations/numeric/greaterEqual.js create mode 100644 lib/operations/numeric/index.js create mode 100644 lib/operations/numeric/lower.js create mode 100644 lib/operations/numeric/lowerEqual.js create mode 100644 lib/operations/numeric/max.js create mode 100644 lib/operations/numeric/min.js create mode 100644 lib/operations/string/cat.js create mode 100644 lib/operations/string/index.js create mode 100644 lib/operations/string/substr.js create mode 100644 lib/visitors/accessor/missing.js create mode 100644 lib/visitors/accessor/missingSome.js create mode 100644 lib/visitors/array/all.js create mode 100644 lib/visitors/array/filter.js create mode 100644 lib/visitors/array/index.js create mode 100644 lib/visitors/array/map.js create mode 100644 lib/visitors/array/none.js create mode 100644 lib/visitors/array/reduce.js create mode 100644 lib/visitors/array/some.js create mode 100644 lib/visitors/index.js create mode 100644 lib/visitors/logic/and.js create mode 100644 lib/visitors/logic/condition.js create mode 100644 lib/visitors/logic/index.js create mode 100644 lib/visitors/logic/or.js diff --git a/dist/jsonLogic.js b/dist/jsonLogic.js new file mode 100644 index 0000000..9653c76 --- /dev/null +++ b/dist/jsonLogic.js @@ -0,0 +1,694 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.jsonLogic = factory()); +}(this, function () { 'use strict'; + + var isArray = Array.isArray; + + function _typeof(obj) { + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function isLogic(logic) { + return _typeof(logic) === "object" && // An object + logic !== null && // but not null + !isArray(logic) && // and not an array + Object.keys(logic).length === 1 // with exactly one key + ; + } + + function getOperator(logic) { + return Object.keys(logic)[0]; + } + + function createJsonLogic(_operations, _visitors) { + var operations = {}; + var visitors = {}; + + if (_operations) { + Object.keys(_operations).forEach(function (name) { + var operation = _operations[name]; + addOperation(operation.code || name, operation); + }); + } + + if (_visitors) { + Object.keys(_visitors).forEach(function (name) { + var visitor = _visitors[name]; + addVisitor(visitor.code || name, visitor); + }); + } + + function addOperation(name, code) { + operations[name] = code; + } + + function removeOperation(name) { + delete operations[name]; + } + + function addVisitor(name, code) { + if (isArray(name)) { + name.forEach(function (key) { + return addVisitor(key, code); + }); + return; + } + + visitors[name] = code; + } + + function removeVisitor(name) { + if (isArray(name)) { + name.forEach(removeVisitor); + return; + } + + delete visitors[name]; + } + + function apply(logic, data) { + // Does this array contain logic? Only one way to find out. + if (isArray(logic)) { + return logic.map(function (l) { + return apply(l, data); + }); + } // You've recursed to a primitive, stop! + + + if (!isLogic(logic)) { + return logic; + } + + data = data || {}; + var op = getOperator(logic); + var values = logic[op]; + var i; // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} + + if (!isArray(values)) { + values = [values]; + } // apply matching visitors first + + + if (typeof visitors[op] === 'function') { + return visitors[op](apply, data, values); + } // Everyone else gets immediate depth-first recursion + + + values = values.map(function (val) { + return apply(val, data); + }); // The operation is called with "data" bound to its "this" and "values" passed as arguments. + // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments + + var operator = operations[op]; + + if (typeof operator === "function") { + if (operator.withApply) { + values.unshift(apply); + } + + return operator.apply(data, values); + } else if (op.indexOf(".") > 0) { + // Contains a dot, and not in the 0th position + var sub_ops = String(op).split("."); + var operation = operations; + + for (i = 0; i < sub_ops.length; i++) { + // Descending into operations + operation = operation[sub_ops[i]]; + + if (operation === undefined) { + throw new Error("Unrecognized operation " + op + " (failed at " + sub_ops.slice(0, i + 1).join(".") + ")"); + } + } + + return operation.apply(data, values); + } + + throw new Error("Unrecognized operation " + op); + } + + return { + apply: apply, + add_operation: addOperation, + rm_operation: removeOperation, + add_visitor: addVisitor, + rm_visitor: removeVisitor + }; + } + + function variable(a, b) { + var not_found = b === undefined ? null : b; + var data = this; + + if (typeof a === "undefined" || a === "" || a === null) { + return data; + } + + var sub_props = String(a).split("."); + + for (var i = 0; i < sub_props.length; i++) { + if (data === null) { + return not_found; + } // Descending into data + + + data = data[sub_props[i]]; + + if (data === undefined) { + return not_found; + } + } + + return data; + } + + variable.code = 'var'; + + function missing(apply) { + /* + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ + var missing = []; + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + var keys = isArray(args[0]) ? args[0] : args; + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = apply({ + "var": key + }, this); + + if (value === null || value === "") { + missing.push(key); + } + } + + return missing; + } + missing.withApply = true; + + function missingSome(apply, need_count, options) { + // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. + var are_missing = apply({ + "missing": options + }, this); + + if (options.length - are_missing.length >= need_count) { + return []; + } else { + return are_missing; + } + } + + missingSome.code = 'missing_some'; + missingSome.withApply = true; + + function add() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) + parseFloat(b, 10); + }, 0); + } + + add.code = '+'; + + function divide(a, b) { + return a / b; + } + + divide.code = '/'; + + function modulo(a, b) { + return a % b; + } + + modulo.code = '%'; + + function multiply() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) * parseFloat(b, 10); + }, 1); + } + + multiply.code = '*'; + + function substract(a, b) { + if (b === undefined) { + return -a; + } else { + return a - b; + } + } + + substract.code = '-'; + + function merge() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return a.concat(b); + }, []); + } + + function equal(a, b) { + return a == b; + } + + equal.code = '=='; + + /* + This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. + + Spec and rationale here: http://jsonlogic.com/truthy + */ + + function truthy(value) { + if (isArray(value) && value.length === 0) { + return false; + } + + return !!value; + } + + function falsy(a) { + return !truthy(a); + } + + falsy.code = '!'; + + function notEqual(a, b) { + return a != b; + } + + notEqual.code = '!='; + + function strictEqual(a, b) { + return a === b; + } + + strictEqual.code = '==='; + + function strictNotEqual(a, b) { + return a !== b; + } + + strictNotEqual.code = '!=='; + + truthy.code = '!!'; + + function indexOf(a, b) { + if (!b || typeof b.indexOf === "undefined") return false; + return b.indexOf(a) !== -1; + } + + indexOf.code = 'in'; + + function log(a) { + console.log(a); + return a; + } + + function method(obj, method, args) { + return obj[method].apply(obj, args); + } + + function greater(a, b) { + return a > b; + } + + greater.code = '>'; + + function greaterEqual(a, b) { + return a >= b; + } + + greaterEqual.code = '>='; + + function lower(a, b, c) { + return c === undefined ? a < b : a < b && b < c; + } + + lower.code = '<'; + + function lowerEqual(a, b, c) { + return c === undefined ? a <= b : a <= b && b <= c; + } + + lowerEqual.code = '<='; + + function max() { + return Math.max.apply(this, arguments); + } + + function min() { + return Math.min.apply(this, arguments); + } + + function cat() { + return Array.prototype.join.call(arguments, ""); + } + + function substr(source, start, end) { + if (end < 0) { + // JavaScript doesn't support negative end, this emulates PHP behavior + var temp = String(source).substr(start); + return temp.substr(0, temp.length + end); + } + + return String(source).substr(start, end); + } + + + + var operations = /*#__PURE__*/Object.freeze({ + variable: variable, + missing: missing, + missingSome: missingSome, + add: add, + divide: divide, + modulo: modulo, + multiply: multiply, + substract: substract, + merge: merge, + equal: equal, + falsy: falsy, + notEqual: notEqual, + strictEqual: strictEqual, + strictNotEqual: strictNotEqual, + truthy: truthy, + indexOf: indexOf, + log: log, + method: method, + greater: greater, + greaterEqual: greaterEqual, + lower: lower, + lowerEqual: lowerEqual, + max: max, + min: min, + cat: cat, + substr: substr + }); + + function all(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; // All of an empty set is false. Note, some and none have correct fallback after the for loop + + if (!scopedData.length) { + return false; + } + + for (var i = 0; i < scopedData.length; i += 1) { + if (!truthy(apply(scopedLogic, scopedData[i]))) { + return false; // First falsy, short circuit + } + } + + return true; // All were truthy + } + + function filter(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } // Return only the elements from the array in the first argument, + // that return truthy when passed to the logic in the second argument. + // For parity with JavaScript, reindex the returned array + + + return scopedData.filter(function (datum) { + return truthy(apply(scopedLogic, datum)); + }); + } + + function map(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } + + return scopedData.map(function (datum) { + return apply(scopedLogic, datum); + }); + } + + function none(apply, data, values) { + var filtered = apply({ + 'filter': values + }, data); + return filtered.length === 0; + } + + function reduce(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + var initial = typeof values[2] !== 'undefined' ? values[2] : null; + + if (!isArray(scopedData)) { + return initial; + } + + return scopedData.reduce(function (accumulator, current) { + return apply(scopedLogic, { + 'current': current, + 'accumulator': accumulator + }); + }, initial); + } + + function some(apply, data, values) { + var filtered = apply({ + 'filter': values + }, data); + return filtered.length > 0; + } + + function and(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (!truthy(current)) { + return current; + } + } + + return current; // Last + } + + function condition(apply, data, values) { + var i; + /* 'if' should be called with a odd number of parameters, 3 or greater + This works on the pattern: + if( 0 ){ 1 }else{ 2 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; + The implementation is: + For pairs of values (0,1 then 2,3 then 4,5 etc) + If the first evaluates truthy, evaluate and return the second + If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) + given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) + given 0 parameters, return NULL (not great practice, but there was no Else) + */ + + for (i = 0; i < values.length - 1; i += 2) { + if (truthy(apply(values[i], data))) { + return apply(values[i + 1], data); + } + } + + if (values.length === i + 1) { + return apply(values[i], data); + } + + return null; + } + + condition.code = ['if', '?:']; + + function or(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (truthy(current)) { + return current; + } + } + + return current; // Last + } + + + + var visitors = /*#__PURE__*/Object.freeze({ + all: all, + filter: filter, + map: map, + none: none, + reduce: reduce, + some: some, + and: and, + condition: condition, + or: or + }); + + function getValues(logic) { + return logic[getOperator(logic)]; + } + + /** + * Return an array that contains no duplicates (original not modified) + * @param {array} array Original reference array + * @return {array} New array with no duplicates + */ + function arrayUnique(array) { + var a = []; + + for (var i = 0, l = array.length; i < l; i++) { + if (a.indexOf(array[i]) === -1) { + a.push(array[i]); + } + } + + return a; + } + + function usesData(logic) { + var collection = []; + + if (isLogic(logic)) { + var op = getOperator(logic); + var values = logic[op]; + + if (!isArray(values)) { + values = [values]; + } + + if (op === "var") { + // This doesn't cover the case where the arg to var is itself a rule. + collection.push(values[0]); + } else { + // Recursion! + values.map(function (val) { + collection.push.apply(collection, usesData(val)); + }); + } + } + + return arrayUnique(collection); + } + + function ruleLike(rule, pattern) { + // console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?"); + if (pattern === rule) { + return true; + } // TODO : Deep object equivalency? + + + if (pattern === "@") { + return true; + } // Wildcard! + + + if (pattern === "number") { + return typeof rule === "number"; + } + + if (pattern === "string") { + return typeof rule === "string"; + } + + if (pattern === "array") { + // !logic test might be superfluous in JavaScript + return isArray(rule) && !isLogic(rule); + } + + if (isLogic(pattern)) { + if (isLogic(rule)) { + var pattern_op = getOperator(pattern); + var rule_op = getOperator(rule); + + if (pattern_op === "@" || pattern_op === rule_op) { + // echo "\nOperators match, go deeper\n"; + return ruleLike(getValues(rule, false), getValues(pattern, false)); + } + } + + return false; // pattern is logic, rule isn't, can't be eq + } + + if (isArray(pattern)) { + if (isArray(rule)) { + if (pattern.length !== rule.length) { + return false; + } + /* + Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) + */ + + + for (var i = 0; i < pattern.length; i += 1) { + // If any fail, we fail + if (!ruleLike(rule[i], pattern[i])) { + return false; + } + } + + return true; // If they *all* passed, we pass + } else { + return false; // Pattern is array, rule isn't + } + } // Not logic, not array, not a === match for rule. + + + return false; + } + + var jsonLogic = createJsonLogic(operations, visitors); // restore original public API + + jsonLogic.is_logic = isLogic; + jsonLogic.truthy = truthy; + jsonLogic.get_operator = getOperator; + jsonLogic.get_values = getValues; + jsonLogic.uses_data = usesData; + jsonLogic.rule_like = ruleLike; + + return jsonLogic; + +})); +//# sourceMappingURL=jsonLogic.js.map diff --git a/dist/jsonLogic.js.map b/dist/jsonLogic.js.map new file mode 100644 index 0000000..dee07aa --- /dev/null +++ b/dist/jsonLogic.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jsonLogic.js","sources":["../src/helpers/isArray.js","../src/helpers/isLogic.js","../src/helpers/getOperator.js","../src/createJsonLogic.js","../src/operations/accessor/variable.js","../src/operations/accessor/missing.js","../src/operations/accessor/missingSome.js","../src/operations/arithmetic/add.js","../src/operations/arithmetic/divide.js","../src/operations/arithmetic/modulo.js","../src/operations/arithmetic/multiply.js","../src/operations/arithmetic/substract.js","../src/operations/array/merge.js","../src/operations/logic/equal.js","../src/helpers/truthy.js","../src/operations/logic/falsy.js","../src/operations/logic/notEqual.js","../src/operations/logic/strictEqual.js","../src/operations/logic/strictNotEqual.js","../src/operations/logic/truthy.js","../src/operations/misc/indexOf.js","../src/operations/misc/log.js","../src/operations/misc/method.js","../src/operations/numeric/greater.js","../src/operations/numeric/greaterEqual.js","../src/operations/numeric/lower.js","../src/operations/numeric/lowerEqual.js","../src/operations/numeric/max.js","../src/operations/numeric/min.js","../src/operations/string/cat.js","../src/operations/string/substr.js","../src/visitors/array/all.js","../src/visitors/array/filter.js","../src/visitors/array/map.js","../src/visitors/array/none.js","../src/visitors/array/reduce.js","../src/visitors/array/some.js","../src/visitors/logic/and.js","../src/visitors/logic/condition.js","../src/visitors/logic/or.js","../src/helpers/getValues.js","../src/helpers/arrayUnique.js","../src/helpers/usesData.js","../src/helpers/ruleLike.js","../src/index.js"],"sourcesContent":["export default Array.isArray;\n","import isArray from './isArray';\n\nfunction isLogic(logic) {\n return (\n typeof logic === \"object\" && // An object\n logic !== null && // but not null\n !isArray(logic) && // and not an array\n Object.keys(logic).length === 1 // with exactly one key\n );\n}\n\nexport default isLogic;\n","function getOperator(logic) {\n return Object.keys(logic)[0];\n}\n\nexport default getOperator;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(function(name) {\n const operation = _operations[name];\n\n addOperation(operation.code || name , operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(function (name) {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach((key) => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data) {\n // Does this array contain logic? Only one way to find out.\n if(isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if( ! isLogic(logic) ) {\n return logic;\n }\n\n data = data || {};\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if( ! isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if(typeof operator === \"function\") {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }else if(op.indexOf(\".\") > 0) { // Contains a dot, and not in the 0th position\n var sub_ops = String(op).split(\".\");\n var operation = operations;\n for(i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if(operation === undefined) {\n throw new Error(\"Unrecognized operation \" + op +\n \" (failed at \" + sub_ops.slice(0, i+1).join(\".\") + \")\");\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(\"Unrecognized operation \" + op );\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","function variable(a, b) {\n const not_found = (b === undefined) ? null : b;\n let data = this;\n\n if(typeof a === \"undefined\" || a===\"\" || a===null) {\n return data;\n }\n\n const sub_props = String(a).split(\".\");\n\n for(let i = 0; i < sub_props.length; i++) {\n if(data === null) {\n return not_found;\n }\n // Descending into data\n data = data[sub_props[i]];\n if(data === undefined) {\n return not_found;\n }\n }\n\n return data;\n}\n\nvariable.code = 'var';\n\nexport default variable;\n","import isArray from '../../helpers/isArray';\nimport variable from './variable'\n\nfunction missing(apply, ...args) {\n /*\n Missing can receive many keys as many arguments, like {\"missing:[1,2]}\n Missing can also receive *one* argument that is an array of keys,\n which typically happens if it's actually acting on the output of another command\n (like 'if' or 'merge')\n */\n\n var missing = [];\n var keys = isArray(args[0]) ? args[0] : args;\n\n for(var i = 0; i < keys.length; i++) {\n var key = keys[i];\n var value = apply({\"var\": key}, this);\n if(value === null || value === \"\") {\n missing.push(key);\n }\n }\n\n return missing;\n};\n\nmissing.withApply = true;\n\nexport default missing;\n","import missing from './missing';\n\nfunction missingSome(apply, need_count, options) {\n // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.\n const are_missing = apply({\"missing\": options}, this);\n\n if(options.length - are_missing.length >= need_count) {\n return [];\n }else{\n return are_missing;\n }\n}\n\nmissingSome.code = 'missing_some';\nmissingSome.withApply = true;\n\nexport default missingSome;\n","function add(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) + parseFloat(b, 10);\n }, 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function divide(a, b) {\n return a / b;\n}\n\ndivide.code = '/';\n\nexport default divide;\n","function modulo(a, b) {\n return a % b;\n}\n\nmodulo.code = '%';\n\nexport default modulo;\n","function multiply(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) * parseFloat(b, 10);\n }, 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function substract(a, b) {\n if(b === undefined) {\n return -a;\n }else{\n return a - b;\n }\n}\n\nsubstract.code = '-';\n\nexport default substract;\n","function merge(...args) {\n return args.reduce(function(a, b) {\n return a.concat(b);\n }, []);\n}\n\nexport default merge;\n","function equal(a, b) {\n return a == b;\n}\n\nequal.code = '==';\n\nexport default equal;\n","import isArray from './isArray';\n\n/*\n This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.\n\n Spec and rationale here: http://jsonlogic.com/truthy\n */\nfunction truthy(value) {\n if(isArray(value) && value.length === 0) {\n return false;\n }\n\n return !!value;\n}\n\nexport default truthy;\n","import truthy from '../../helpers/truthy'\n\nfunction falsy(a) {\n return !truthy(a);\n}\n\nfalsy.code = '!';\n\nexport default falsy;\n","function notEqual(a, b) {\n return a != b;\n}\n\nnotEqual.code = '!=';\n\nexport default notEqual;\n","function strictEqual(a, b) {\n return a === b;\n}\n\nstrictEqual.code = '===';\n\nexport default strictEqual;\n","function strictNotEqual(a, b) {\n return a !== b;\n}\n\nstrictNotEqual.code = '!==';\n\nexport default strictNotEqual;\n","import truthy from '../../helpers/truthy'\n\ntruthy.code = '!!';\n\nexport default truthy;\n","function indexOf(a, b) {\n if(!b || typeof b.indexOf === \"undefined\") return false;\n return (b.indexOf(a) !== -1);\n}\n\nindexOf.code = 'in';\n\nexport default indexOf;\n","function log(a) {\n console.log(a);\n\n return a;\n}\n\nexport default log;\n","function method(obj, method, args) {\n return obj[method].apply(obj, args);\n}\n\nexport default method;\n","function greater(a, b) {\n return a > b;\n}\n\ngreater.code = '>';\n\nexport default greater;\n","function greaterEqual(a, b) {\n return a >= b;\n}\n\ngreaterEqual.code = '>=';\n\nexport default greaterEqual;\n","function lower(a, b, c) {\n return (c === undefined) ? a < b : (a < b) && (b < c);\n}\n\nlower.code = '<';\n\nexport default lower;\n","function lowerEqual(a, b, c) {\n return (c === undefined) ? a <= b : (a <= b) && (b <= c);\n}\n\nlowerEqual.code = '<=';\n\nexport default lowerEqual;\n","function max() {\n return Math.max.apply(this, arguments);\n}\n\nexport default max;\n","function min() {\n return Math.min.apply(this, arguments);\n}\n\nexport default min;\n","function cat() {\n return Array.prototype.join.call(arguments, \"\");\n}\n\nexport default cat;\n","function substr(source, start, end) {\n if (end < 0) {\n // JavaScript doesn't support negative end, this emulates PHP behavior\n const temp = String(source).substr(start);\n return temp.substr(0, temp.length + end);\n }\n return String(source).substr(start, end);\n};\n\nexport default substr;\n","import truthy from \"../../helpers/truthy\";\n\nfunction all(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n // All of an empty set is false. Note, some and none have correct fallback after the for loop\n if( ! scopedData.length) {\n return false;\n }\n for(let i=0; i < scopedData.length; i+=1) {\n if( ! truthy( apply(scopedLogic, scopedData[i]) )) {\n return false; // First falsy, short circuit\n }\n }\n return true; // All were truthy\n}\n\nexport default all;\n","import isArray from '../../helpers/isArray';\nimport truthy from \"../../helpers/truthy\";\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum){\n return truthy( apply(scopedLogic, datum));\n });\n}\n\nexport default filter\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum){\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","function none(apply, data, values) {\n const filtered = apply({'filter' : values}, data);\n\n return filtered.length === 0;\n}\n\nexport default none;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if ( ! isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n function(accumulator, current){\n return apply(\n scopedLogic,\n {'current':current, 'accumulator':accumulator}\n );\n },\n initial\n );\n}\n\nexport default reduce;\n","function some(apply, data, values) {\n const filtered = apply({'filter' : values}, data);\n\n return filtered.length > 0;\n}\n\nexport default some;\n","import truthy from \"../../helpers/truthy\";\n\nfunction and(apply, data, values) {\n let current;\n\n for(let i=0; i < values.length; i++) {\n current = apply(values[i], data);\n if( ! truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default and;\n","import truthy from '../../helpers/truthy';\n\nfunction condition(apply, data, values) {\n let i;\n\n /* 'if' should be called with a odd number of parameters, 3 or greater\n This works on the pattern:\n if( 0 ){ 1 }else{ 2 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };\n\n The implementation is:\n For pairs of values (0,1 then 2,3 then 4,5 etc)\n If the first evaluates truthy, evaluate and return the second\n If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)\n given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)\n given 0 parameters, return NULL (not great practice, but there was no Else)\n */\n for(i = 0; i < values.length - 1; i += 2) {\n if( truthy( apply(values[i], data) ) ) {\n return apply(values[i+1], data);\n }\n }\n\n if(values.length === i+1) {\n return apply(values[i], data);\n }\n\n return null;\n}\n\ncondition.code = ['if', '?:'];\n\nexport default condition;\n","import truthy from \"../../helpers/truthy\";\n\nfunction or(apply, data, values) {\n let current;\n\n for(let i=0; i < values.length; i++) {\n current = apply(values[i], data);\n if( truthy(current) ) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default or;\n","import getOperator from './getOperator'\n\nfunction getValues(logic) {\n return logic[getOperator(logic)];\n}\n\nexport default getValues;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i=0, l=array.length; i=r?[]:e}function o(){for(var n=arguments.length,r=new Array(n),t=0;t",w.code=">=",j.code="<",E.code="<=";var O=Object.freeze({variable:n,missing:t,missingSome:e,add:o,divide:u,modulo:i,multiply:f,substract:c,merge:function(){for(var n=arguments.length,r=new Array(n),t=0;t addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data) {\n // Does this array contain logic? Only one way to find out.\n if(isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if( ! isLogic(logic) ) {\n return logic;\n }\n\n data = data || {};\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if( ! isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if(typeof operator === \"function\") {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }else if(op.indexOf(\".\") > 0) { // Contains a dot, and not in the 0th position\n var sub_ops = String(op).split(\".\");\n var operation = operations;\n for(i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if(operation === undefined) {\n throw new Error(\"Unrecognized operation \" + op +\n \" (failed at \" + sub_ops.slice(0, i+1).join(\".\") + \")\");\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(\"Unrecognized operation \" + op );\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from \"./helpers/isLogic\";\nimport truthy from \"./helpers/truthy\";\nimport getOperator from \"./helpers/getOperator\";\nimport getValues from \"./helpers/getValues\";\nimport usesData from \"./helpers/usesData\";\nimport ruleLike from \"./helpers/ruleLike\";\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n","import isArray from '../../helpers/isArray';\nimport truthy from \"../../helpers/truthy\";\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum){\n return truthy( apply(scopedLogic, datum));\n });\n}\n\nexport default filter\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum){\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if ( ! isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n function(accumulator, current){\n return apply(\n scopedLogic,\n {'current':current, 'accumulator':accumulator}\n );\n },\n initial\n );\n}\n\nexport default reduce;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n var collection = [];\n\n if(isLogic(logic) ) {\n var op = getOperator(logic);\n var values = logic[op];\n\n if( ! isArray(values)) {\n values = [values];\n }\n\n if(op === \"var\") {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n }else{\n // Recursion!\n values.map(function(val) {\n collection.push.apply(collection, usesData(val) );\n });\n }\n }\n\n return arrayUnique(collection);\n};\n\nexport default usesData;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i=0, l=array.length; i can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments + + var operator = operations[op]; + + if (typeof operator === "function") { + if (operator.withApply) { + values.unshift(apply); + } + + return operator.apply(data, values); + } else if (op.indexOf(".") > 0) { + // Contains a dot, and not in the 0th position + var sub_ops = String(op).split("."); + var operation = operations; + + for (i = 0; i < sub_ops.length; i++) { + // Descending into operations + operation = operation[sub_ops[i]]; + + if (operation === undefined) { + throw new Error("Unrecognized operation " + op + " (failed at " + sub_ops.slice(0, i + 1).join(".") + ")"); + } + } + + return operation.apply(data, values); + } + + throw new Error("Unrecognized operation " + op); + } + + return { + apply: apply, + add_operation: addOperation, + rm_operation: removeOperation, + add_visitor: addVisitor, + rm_visitor: removeVisitor + }; +} + +export default createJsonLogic; \ No newline at end of file diff --git a/lib/helpers/arrayUnique.js b/lib/helpers/arrayUnique.js new file mode 100644 index 0000000..49bc327 --- /dev/null +++ b/lib/helpers/arrayUnique.js @@ -0,0 +1,18 @@ +/** + * Return an array that contains no duplicates (original not modified) + * @param {array} array Original reference array + * @return {array} New array with no duplicates + */ +function arrayUnique(array) { + var a = []; + + for (var i = 0, l = array.length; i < l; i++) { + if (a.indexOf(array[i]) === -1) { + a.push(array[i]); + } + } + + return a; +} + +export default arrayUnique; \ No newline at end of file diff --git a/lib/helpers/getOperator.js b/lib/helpers/getOperator.js new file mode 100644 index 0000000..994698f --- /dev/null +++ b/lib/helpers/getOperator.js @@ -0,0 +1,5 @@ +function getOperator(logic) { + return Object.keys(logic)[0]; +} + +export default getOperator; \ No newline at end of file diff --git a/lib/helpers/getValues.js b/lib/helpers/getValues.js new file mode 100644 index 0000000..c6d9040 --- /dev/null +++ b/lib/helpers/getValues.js @@ -0,0 +1,7 @@ +import getOperator from './getOperator'; + +function getValues(logic) { + return logic[getOperator(logic)]; +} + +export default getValues; \ No newline at end of file diff --git a/lib/helpers/isArray.js b/lib/helpers/isArray.js new file mode 100644 index 0000000..088ca26 --- /dev/null +++ b/lib/helpers/isArray.js @@ -0,0 +1 @@ +export default Array.isArray; \ No newline at end of file diff --git a/lib/helpers/isLogic.js b/lib/helpers/isLogic.js new file mode 100644 index 0000000..aafe1c8 --- /dev/null +++ b/lib/helpers/isLogic.js @@ -0,0 +1,13 @@ +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +import isArray from './isArray'; + +function isLogic(logic) { + return _typeof(logic) === "object" && // An object + logic !== null && // but not null + !isArray(logic) && // and not an array + Object.keys(logic).length === 1 // with exactly one key + ; +} + +export default isLogic; \ No newline at end of file diff --git a/lib/helpers/ruleLike.js b/lib/helpers/ruleLike.js new file mode 100644 index 0000000..dbdf86e --- /dev/null +++ b/lib/helpers/ruleLike.js @@ -0,0 +1,72 @@ +import isArray from './isArray'; +import isLogic from './isLogic'; +import getOperator from './getOperator'; +import getValues from './getValues'; + +function ruleLike(rule, pattern) { + // console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?"); + if (pattern === rule) { + return true; + } // TODO : Deep object equivalency? + + + if (pattern === "@") { + return true; + } // Wildcard! + + + if (pattern === "number") { + return typeof rule === "number"; + } + + if (pattern === "string") { + return typeof rule === "string"; + } + + if (pattern === "array") { + // !logic test might be superfluous in JavaScript + return isArray(rule) && !isLogic(rule); + } + + if (isLogic(pattern)) { + if (isLogic(rule)) { + var pattern_op = getOperator(pattern); + var rule_op = getOperator(rule); + + if (pattern_op === "@" || pattern_op === rule_op) { + // echo "\nOperators match, go deeper\n"; + return ruleLike(getValues(rule, false), getValues(pattern, false)); + } + } + + return false; // pattern is logic, rule isn't, can't be eq + } + + if (isArray(pattern)) { + if (isArray(rule)) { + if (pattern.length !== rule.length) { + return false; + } + /* + Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) + */ + + + for (var i = 0; i < pattern.length; i += 1) { + // If any fail, we fail + if (!ruleLike(rule[i], pattern[i])) { + return false; + } + } + + return true; // If they *all* passed, we pass + } else { + return false; // Pattern is array, rule isn't + } + } // Not logic, not array, not a === match for rule. + + + return false; +} + +export default ruleLike; \ No newline at end of file diff --git a/lib/helpers/truthy.js b/lib/helpers/truthy.js new file mode 100644 index 0000000..2105689 --- /dev/null +++ b/lib/helpers/truthy.js @@ -0,0 +1,16 @@ +import isArray from './isArray'; +/* + This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. + + Spec and rationale here: http://jsonlogic.com/truthy + */ + +function truthy(value) { + if (isArray(value) && value.length === 0) { + return false; + } + + return !!value; +} + +export default truthy; \ No newline at end of file diff --git a/lib/helpers/usesData.js b/lib/helpers/usesData.js new file mode 100644 index 0000000..1d1c3db --- /dev/null +++ b/lib/helpers/usesData.js @@ -0,0 +1,32 @@ +import isArray from './isArray'; +import isLogic from './isLogic'; +import getOperator from './getOperator'; +import arrayUnique from './arrayUnique'; + +function usesData(logic) { + var collection = []; + + if (isLogic(logic)) { + var op = getOperator(logic); + var values = logic[op]; + + if (!isArray(values)) { + values = [values]; + } + + if (op === "var") { + // This doesn't cover the case where the arg to var is itself a rule. + collection.push(values[0]); + } else { + // Recursion! + values.map(function (val) { + collection.push.apply(collection, usesData(val)); + }); + } + } + + return arrayUnique(collection); +} + +; +export default usesData; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..434d9c9 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,18 @@ +import createJsonLogic from './createJsonLogic'; +import * as operations from './operations'; +import * as visitors from './visitors'; +import isLogic from "./helpers/isLogic"; +import truthy from "./helpers/truthy"; +import getOperator from "./helpers/getOperator"; +import getValues from "./helpers/getValues"; +import usesData from "./helpers/usesData"; +import ruleLike from "./helpers/ruleLike"; +var jsonLogic = createJsonLogic(operations, visitors); // restore original public API + +jsonLogic.is_logic = isLogic; +jsonLogic.truthy = truthy; +jsonLogic.get_operator = getOperator; +jsonLogic.get_values = getValues; +jsonLogic.uses_data = usesData; +jsonLogic.rule_like = ruleLike; +export default jsonLogic; \ No newline at end of file diff --git a/lib/operations/accessor/index.js b/lib/operations/accessor/index.js new file mode 100644 index 0000000..dcf58ba --- /dev/null +++ b/lib/operations/accessor/index.js @@ -0,0 +1,3 @@ +export { default as variable } from './variable'; +export { default as missing } from './missing'; +export { default as missingSome } from './missingSome'; \ No newline at end of file diff --git a/lib/operations/accessor/missing.js b/lib/operations/accessor/missing.js new file mode 100644 index 0000000..267c7ad --- /dev/null +++ b/lib/operations/accessor/missing.js @@ -0,0 +1,35 @@ +import isArray from '../../helpers/isArray'; +import variable from './variable'; + +function missing(apply) { + /* + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ + var missing = []; + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + var keys = isArray(args[0]) ? args[0] : args; + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = apply({ + "var": key + }, this); + + if (value === null || value === "") { + missing.push(key); + } + } + + return missing; +} + +; +missing.withApply = true; +export default missing; \ No newline at end of file diff --git a/lib/operations/accessor/missingSome.js b/lib/operations/accessor/missingSome.js new file mode 100644 index 0000000..ef432dc --- /dev/null +++ b/lib/operations/accessor/missingSome.js @@ -0,0 +1,18 @@ +import missing from './missing'; + +function missingSome(apply, need_count, options) { + // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. + var are_missing = apply({ + "missing": options + }, this); + + if (options.length - are_missing.length >= need_count) { + return []; + } else { + return are_missing; + } +} + +missingSome.code = 'missing_some'; +missingSome.withApply = true; +export default missingSome; \ No newline at end of file diff --git a/lib/operations/accessor/variable.js b/lib/operations/accessor/variable.js new file mode 100644 index 0000000..33aa5c3 --- /dev/null +++ b/lib/operations/accessor/variable.js @@ -0,0 +1,28 @@ +function variable(a, b) { + var not_found = b === undefined ? null : b; + var data = this; + + if (typeof a === "undefined" || a === "" || a === null) { + return data; + } + + var sub_props = String(a).split("."); + + for (var i = 0; i < sub_props.length; i++) { + if (data === null) { + return not_found; + } // Descending into data + + + data = data[sub_props[i]]; + + if (data === undefined) { + return not_found; + } + } + + return data; +} + +variable.code = 'var'; +export default variable; \ No newline at end of file diff --git a/lib/operations/arithmetic/add.js b/lib/operations/arithmetic/add.js new file mode 100644 index 0000000..fa7661a --- /dev/null +++ b/lib/operations/arithmetic/add.js @@ -0,0 +1,12 @@ +function add() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) + parseFloat(b, 10); + }, 0); +} + +add.code = '+'; +export default add; \ No newline at end of file diff --git a/lib/operations/arithmetic/divide.js b/lib/operations/arithmetic/divide.js new file mode 100644 index 0000000..c279ad4 --- /dev/null +++ b/lib/operations/arithmetic/divide.js @@ -0,0 +1,6 @@ +function divide(a, b) { + return a / b; +} + +divide.code = '/'; +export default divide; \ No newline at end of file diff --git a/lib/operations/arithmetic/index.js b/lib/operations/arithmetic/index.js new file mode 100644 index 0000000..fe1093b --- /dev/null +++ b/lib/operations/arithmetic/index.js @@ -0,0 +1,5 @@ +export { default as add } from './add'; +export { default as divide } from './divide'; +export { default as modulo } from './modulo'; +export { default as multiply } from './multiply'; +export { default as substract } from './substract'; \ No newline at end of file diff --git a/lib/operations/arithmetic/modulo.js b/lib/operations/arithmetic/modulo.js new file mode 100644 index 0000000..6cb0204 --- /dev/null +++ b/lib/operations/arithmetic/modulo.js @@ -0,0 +1,6 @@ +function modulo(a, b) { + return a % b; +} + +modulo.code = '%'; +export default modulo; \ No newline at end of file diff --git a/lib/operations/arithmetic/multiply.js b/lib/operations/arithmetic/multiply.js new file mode 100644 index 0000000..9c12558 --- /dev/null +++ b/lib/operations/arithmetic/multiply.js @@ -0,0 +1,12 @@ +function multiply() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return parseFloat(a, 10) * parseFloat(b, 10); + }, 1); +} + +multiply.code = '*'; +export default multiply; \ No newline at end of file diff --git a/lib/operations/arithmetic/substract.js b/lib/operations/arithmetic/substract.js new file mode 100644 index 0000000..72f44a7 --- /dev/null +++ b/lib/operations/arithmetic/substract.js @@ -0,0 +1,10 @@ +function substract(a, b) { + if (b === undefined) { + return -a; + } else { + return a - b; + } +} + +substract.code = '-'; +export default substract; \ No newline at end of file diff --git a/lib/operations/array/index.js b/lib/operations/array/index.js new file mode 100644 index 0000000..3d25475 --- /dev/null +++ b/lib/operations/array/index.js @@ -0,0 +1 @@ +export { default as merge } from './merge'; \ No newline at end of file diff --git a/lib/operations/array/merge.js b/lib/operations/array/merge.js new file mode 100644 index 0000000..2b3f749 --- /dev/null +++ b/lib/operations/array/merge.js @@ -0,0 +1,11 @@ +function merge() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.reduce(function (a, b) { + return a.concat(b); + }, []); +} + +export default merge; \ No newline at end of file diff --git a/lib/operations/index.js b/lib/operations/index.js new file mode 100644 index 0000000..34f019c --- /dev/null +++ b/lib/operations/index.js @@ -0,0 +1,7 @@ +export * from './accessor'; +export * from './arithmetic'; +export * from './array'; +export * from './logic'; +export * from './misc'; +export * from './numeric'; +export * from './string'; \ No newline at end of file diff --git a/lib/operations/logic/equal.js b/lib/operations/logic/equal.js new file mode 100644 index 0000000..5228162 --- /dev/null +++ b/lib/operations/logic/equal.js @@ -0,0 +1,6 @@ +function equal(a, b) { + return a == b; +} + +equal.code = '=='; +export default equal; \ No newline at end of file diff --git a/lib/operations/logic/falsy.js b/lib/operations/logic/falsy.js new file mode 100644 index 0000000..667df45 --- /dev/null +++ b/lib/operations/logic/falsy.js @@ -0,0 +1,8 @@ +import truthy from '../../helpers/truthy'; + +function falsy(a) { + return !truthy(a); +} + +falsy.code = '!'; +export default falsy; \ No newline at end of file diff --git a/lib/operations/logic/index.js b/lib/operations/logic/index.js new file mode 100644 index 0000000..38ecd86 --- /dev/null +++ b/lib/operations/logic/index.js @@ -0,0 +1,6 @@ +export { default as equal } from './equal'; +export { default as falsy } from './falsy'; +export { default as notEqual } from './notEqual'; +export { default as strictEqual } from './strictEqual'; +export { default as strictNotEqual } from './strictNotEqual'; +export { default as truthy } from './truthy'; \ No newline at end of file diff --git a/lib/operations/logic/notEqual.js b/lib/operations/logic/notEqual.js new file mode 100644 index 0000000..49e657a --- /dev/null +++ b/lib/operations/logic/notEqual.js @@ -0,0 +1,6 @@ +function notEqual(a, b) { + return a != b; +} + +notEqual.code = '!='; +export default notEqual; \ No newline at end of file diff --git a/lib/operations/logic/strictEqual.js b/lib/operations/logic/strictEqual.js new file mode 100644 index 0000000..e2e9c9f --- /dev/null +++ b/lib/operations/logic/strictEqual.js @@ -0,0 +1,6 @@ +function strictEqual(a, b) { + return a === b; +} + +strictEqual.code = '==='; +export default strictEqual; \ No newline at end of file diff --git a/lib/operations/logic/strictNotEqual.js b/lib/operations/logic/strictNotEqual.js new file mode 100644 index 0000000..9edaf8a --- /dev/null +++ b/lib/operations/logic/strictNotEqual.js @@ -0,0 +1,6 @@ +function strictNotEqual(a, b) { + return a !== b; +} + +strictNotEqual.code = '!=='; +export default strictNotEqual; \ No newline at end of file diff --git a/lib/operations/logic/truthy.js b/lib/operations/logic/truthy.js new file mode 100644 index 0000000..ab17b82 --- /dev/null +++ b/lib/operations/logic/truthy.js @@ -0,0 +1,3 @@ +import truthy from '../../helpers/truthy'; +truthy.code = '!!'; +export default truthy; \ No newline at end of file diff --git a/lib/operations/misc/index.js b/lib/operations/misc/index.js new file mode 100644 index 0000000..ac367e8 --- /dev/null +++ b/lib/operations/misc/index.js @@ -0,0 +1,3 @@ +export { default as indexOf } from './indexOf'; +export { default as log } from './log'; +export { default as method } from './method'; \ No newline at end of file diff --git a/lib/operations/misc/indexOf.js b/lib/operations/misc/indexOf.js new file mode 100644 index 0000000..4656686 --- /dev/null +++ b/lib/operations/misc/indexOf.js @@ -0,0 +1,7 @@ +function indexOf(a, b) { + if (!b || typeof b.indexOf === "undefined") return false; + return b.indexOf(a) !== -1; +} + +indexOf.code = 'in'; +export default indexOf; \ No newline at end of file diff --git a/lib/operations/misc/log.js b/lib/operations/misc/log.js new file mode 100644 index 0000000..07c6915 --- /dev/null +++ b/lib/operations/misc/log.js @@ -0,0 +1,6 @@ +function log(a) { + console.log(a); + return a; +} + +export default log; \ No newline at end of file diff --git a/lib/operations/misc/method.js b/lib/operations/misc/method.js new file mode 100644 index 0000000..613c8ea --- /dev/null +++ b/lib/operations/misc/method.js @@ -0,0 +1,5 @@ +function method(obj, method, args) { + return obj[method].apply(obj, args); +} + +export default method; \ No newline at end of file diff --git a/lib/operations/numeric/greater.js b/lib/operations/numeric/greater.js new file mode 100644 index 0000000..190710b --- /dev/null +++ b/lib/operations/numeric/greater.js @@ -0,0 +1,6 @@ +function greater(a, b) { + return a > b; +} + +greater.code = '>'; +export default greater; \ No newline at end of file diff --git a/lib/operations/numeric/greaterEqual.js b/lib/operations/numeric/greaterEqual.js new file mode 100644 index 0000000..825ed90 --- /dev/null +++ b/lib/operations/numeric/greaterEqual.js @@ -0,0 +1,6 @@ +function greaterEqual(a, b) { + return a >= b; +} + +greaterEqual.code = '>='; +export default greaterEqual; \ No newline at end of file diff --git a/lib/operations/numeric/index.js b/lib/operations/numeric/index.js new file mode 100644 index 0000000..23f69df --- /dev/null +++ b/lib/operations/numeric/index.js @@ -0,0 +1,6 @@ +export { default as greater } from './greater'; +export { default as greaterEqual } from './greaterEqual'; +export { default as lower } from './lower'; +export { default as lowerEqual } from './lowerEqual'; +export { default as max } from './max'; +export { default as min } from './min'; \ No newline at end of file diff --git a/lib/operations/numeric/lower.js b/lib/operations/numeric/lower.js new file mode 100644 index 0000000..37f9172 --- /dev/null +++ b/lib/operations/numeric/lower.js @@ -0,0 +1,6 @@ +function lower(a, b, c) { + return c === undefined ? a < b : a < b && b < c; +} + +lower.code = '<'; +export default lower; \ No newline at end of file diff --git a/lib/operations/numeric/lowerEqual.js b/lib/operations/numeric/lowerEqual.js new file mode 100644 index 0000000..0308b0f --- /dev/null +++ b/lib/operations/numeric/lowerEqual.js @@ -0,0 +1,6 @@ +function lowerEqual(a, b, c) { + return c === undefined ? a <= b : a <= b && b <= c; +} + +lowerEqual.code = '<='; +export default lowerEqual; \ No newline at end of file diff --git a/lib/operations/numeric/max.js b/lib/operations/numeric/max.js new file mode 100644 index 0000000..4afe6d3 --- /dev/null +++ b/lib/operations/numeric/max.js @@ -0,0 +1,5 @@ +function max() { + return Math.max.apply(this, arguments); +} + +export default max; \ No newline at end of file diff --git a/lib/operations/numeric/min.js b/lib/operations/numeric/min.js new file mode 100644 index 0000000..7a43a90 --- /dev/null +++ b/lib/operations/numeric/min.js @@ -0,0 +1,5 @@ +function min() { + return Math.min.apply(this, arguments); +} + +export default min; \ No newline at end of file diff --git a/lib/operations/string/cat.js b/lib/operations/string/cat.js new file mode 100644 index 0000000..d92c33b --- /dev/null +++ b/lib/operations/string/cat.js @@ -0,0 +1,5 @@ +function cat() { + return Array.prototype.join.call(arguments, ""); +} + +export default cat; \ No newline at end of file diff --git a/lib/operations/string/index.js b/lib/operations/string/index.js new file mode 100644 index 0000000..b0c996d --- /dev/null +++ b/lib/operations/string/index.js @@ -0,0 +1,2 @@ +export { default as cat } from './cat'; +export { default as substr } from './substr'; \ No newline at end of file diff --git a/lib/operations/string/substr.js b/lib/operations/string/substr.js new file mode 100644 index 0000000..db3e7b8 --- /dev/null +++ b/lib/operations/string/substr.js @@ -0,0 +1,12 @@ +function substr(source, start, end) { + if (end < 0) { + // JavaScript doesn't support negative end, this emulates PHP behavior + var temp = String(source).substr(start); + return temp.substr(0, temp.length + end); + } + + return String(source).substr(start, end); +} + +; +export default substr; \ No newline at end of file diff --git a/lib/visitors/accessor/missing.js b/lib/visitors/accessor/missing.js new file mode 100644 index 0000000..faa0d9d --- /dev/null +++ b/lib/visitors/accessor/missing.js @@ -0,0 +1,29 @@ +import isArray from '../../helpers/isArray'; +import variable from '../../operations/accessor/variable'; + +function missing() { + /* + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ + var missing = []; + var keys = isArray(arguments[0]) ? arguments[0] : arguments; + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = variable.call(this, { + "var": key + }, arguments); + + if (value === null || value === "") { + missing.push(key); + } + } + + return missing; +} + +; +export default missing; \ No newline at end of file diff --git a/lib/visitors/accessor/missingSome.js b/lib/visitors/accessor/missingSome.js new file mode 100644 index 0000000..c7f9bd2 --- /dev/null +++ b/lib/visitors/accessor/missingSome.js @@ -0,0 +1,17 @@ +import missing from './missing'; + +function missingSome(need_count, options) { + // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. + var are_missing = missing.call(this, { + "missing": options + }); + + if (options.length - are_missing.length >= need_count) { + return []; + } else { + return are_missing; + } +} + +missingSome.code = 'missing_some'; +export default missingSome; \ No newline at end of file diff --git a/lib/visitors/array/all.js b/lib/visitors/array/all.js new file mode 100644 index 0000000..ec00c77 --- /dev/null +++ b/lib/visitors/array/all.js @@ -0,0 +1,20 @@ +import truthy from "../../helpers/truthy"; + +function all(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; // All of an empty set is false. Note, some and none have correct fallback after the for loop + + if (!scopedData.length) { + return false; + } + + for (var i = 0; i < scopedData.length; i += 1) { + if (!truthy(apply(scopedLogic, scopedData[i]))) { + return false; // First falsy, short circuit + } + } + + return true; // All were truthy +} + +export default all; \ No newline at end of file diff --git a/lib/visitors/array/filter.js b/lib/visitors/array/filter.js new file mode 100644 index 0000000..6a65fe5 --- /dev/null +++ b/lib/visitors/array/filter.js @@ -0,0 +1,20 @@ +import isArray from '../../helpers/isArray'; +import truthy from "../../helpers/truthy"; + +function filter(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } // Return only the elements from the array in the first argument, + // that return truthy when passed to the logic in the second argument. + // For parity with JavaScript, reindex the returned array + + + return scopedData.filter(function (datum) { + return truthy(apply(scopedLogic, datum)); + }); +} + +export default filter; \ No newline at end of file diff --git a/lib/visitors/array/index.js b/lib/visitors/array/index.js new file mode 100644 index 0000000..6e5d691 --- /dev/null +++ b/lib/visitors/array/index.js @@ -0,0 +1,6 @@ +export { default as all } from './all'; +export { default as filter } from './filter'; +export { default as map } from './map'; +export { default as none } from './none'; +export { default as reduce } from './reduce'; +export { default as some } from './some'; \ No newline at end of file diff --git a/lib/visitors/array/map.js b/lib/visitors/array/map.js new file mode 100644 index 0000000..784b662 --- /dev/null +++ b/lib/visitors/array/map.js @@ -0,0 +1,16 @@ +import isArray from '../../helpers/isArray'; + +function map(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + + if (!isArray(scopedData)) { + return []; + } + + return scopedData.map(function (datum) { + return apply(scopedLogic, datum); + }); +} + +export default map; \ No newline at end of file diff --git a/lib/visitors/array/none.js b/lib/visitors/array/none.js new file mode 100644 index 0000000..eeb13f4 --- /dev/null +++ b/lib/visitors/array/none.js @@ -0,0 +1,8 @@ +function none(apply, data, values) { + var filtered = apply({ + 'filter': values + }, data); + return filtered.length === 0; +} + +export default none; \ No newline at end of file diff --git a/lib/visitors/array/reduce.js b/lib/visitors/array/reduce.js new file mode 100644 index 0000000..7eea583 --- /dev/null +++ b/lib/visitors/array/reduce.js @@ -0,0 +1,20 @@ +import isArray from '../../helpers/isArray'; + +function reduce(apply, data, values) { + var scopedData = apply(values[0], data); + var scopedLogic = values[1]; + var initial = typeof values[2] !== 'undefined' ? values[2] : null; + + if (!isArray(scopedData)) { + return initial; + } + + return scopedData.reduce(function (accumulator, current) { + return apply(scopedLogic, { + 'current': current, + 'accumulator': accumulator + }); + }, initial); +} + +export default reduce; \ No newline at end of file diff --git a/lib/visitors/array/some.js b/lib/visitors/array/some.js new file mode 100644 index 0000000..d1fb587 --- /dev/null +++ b/lib/visitors/array/some.js @@ -0,0 +1,8 @@ +function some(apply, data, values) { + var filtered = apply({ + 'filter': values + }, data); + return filtered.length > 0; +} + +export default some; \ No newline at end of file diff --git a/lib/visitors/index.js b/lib/visitors/index.js new file mode 100644 index 0000000..93efdf6 --- /dev/null +++ b/lib/visitors/index.js @@ -0,0 +1,2 @@ +export * from './array'; +export * from './logic'; \ No newline at end of file diff --git a/lib/visitors/logic/and.js b/lib/visitors/logic/and.js new file mode 100644 index 0000000..cf47058 --- /dev/null +++ b/lib/visitors/logic/and.js @@ -0,0 +1,17 @@ +import truthy from "../../helpers/truthy"; + +function and(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (!truthy(current)) { + return current; + } + } + + return current; // Last +} + +export default and; \ No newline at end of file diff --git a/lib/visitors/logic/condition.js b/lib/visitors/logic/condition.js new file mode 100644 index 0000000..3581211 --- /dev/null +++ b/lib/visitors/logic/condition.js @@ -0,0 +1,32 @@ +import truthy from '../../helpers/truthy'; + +function condition(apply, data, values) { + var i; + /* 'if' should be called with a odd number of parameters, 3 or greater + This works on the pattern: + if( 0 ){ 1 }else{ 2 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; + The implementation is: + For pairs of values (0,1 then 2,3 then 4,5 etc) + If the first evaluates truthy, evaluate and return the second + If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) + given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) + given 0 parameters, return NULL (not great practice, but there was no Else) + */ + + for (i = 0; i < values.length - 1; i += 2) { + if (truthy(apply(values[i], data))) { + return apply(values[i + 1], data); + } + } + + if (values.length === i + 1) { + return apply(values[i], data); + } + + return null; +} + +condition.code = ['if', '?:']; +export default condition; \ No newline at end of file diff --git a/lib/visitors/logic/index.js b/lib/visitors/logic/index.js new file mode 100644 index 0000000..495b2bc --- /dev/null +++ b/lib/visitors/logic/index.js @@ -0,0 +1,3 @@ +export { default as and } from './and'; +export { default as condition } from './condition'; +export { default as or } from './or'; \ No newline at end of file diff --git a/lib/visitors/logic/or.js b/lib/visitors/logic/or.js new file mode 100644 index 0000000..14d72c9 --- /dev/null +++ b/lib/visitors/logic/or.js @@ -0,0 +1,17 @@ +import truthy from "../../helpers/truthy"; + +function or(apply, data, values) { + var current; + + for (var i = 0; i < values.length; i++) { + current = apply(values[i], data); + + if (truthy(current)) { + return current; + } + } + + return current; // Last +} + +export default or; \ No newline at end of file From 330d37d2c71467df8e0de5d2eb8c88765a26f72a Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 16:05:06 +0100 Subject: [PATCH 22/33] deleted bower.json --- bower.json | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 bower.json diff --git a/bower.json b/bower.json deleted file mode 100644 index b7842d1..0000000 --- a/bower.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "json-logic-js", - "version": "1.2.2", - "homepage": "https://github.com/jwadhams/json-logic-js", - "authors": [ - "Jeremy Wadhams " - ], - "description": "Serialize complex logic in JSON, run it in JavaScript", - "main": "logic.js", - "moduleType": [ - "globals" - ], - "keywords": [ - "json", - "logic" - ], - "license": "MIT", - "private": false, - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "tests", - "gulpfile.js" - ] -} From 55987ffd77793c26f567509bdae0c65766e42f05 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 16:05:21 +0100 Subject: [PATCH 23/33] feat: exclude src from npm --- .npmignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.npmignore b/.npmignore index 013b7f4..566887d 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ tests/tests.json +src play.html bower.json From 9f87e40eefbad89851f9c194a95e199f18519bd6 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 16:06:47 +0100 Subject: [PATCH 24/33] chore: updated package.json infos --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 30b33c1..1f539e7 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "json-logic-js", + "name": "@axa-ch/json-logic-js", "version": "1.2.2", "description": "Build complex rules, serialize them as JSON, and execute them in JavaScript", "main": "dist/jsonLogic.js", @@ -41,7 +41,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/jwadhams/json-logic-js.git" + "url": "git+https://github.com/axa-ch/json-logic-js.git" }, "keywords": [ "json", @@ -52,7 +52,7 @@ "author": "Jeremy Wadhams (http://jsonlogic.com)", "license": "MIT", "bugs": { - "url": "https://github.com/jwadhams/json-logic-js/issues" + "url": "https://github.com/axa-ch/json-logic-js/issues" }, - "homepage": "https://github.com/jwadhams/json-logic-js#readme" + "homepage": "https://github.com/axa-ch/json-logic-js#readme" } From 8b00430d1efef34ac33ab9f7d03e6e72461d5df7 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 16:52:55 +0100 Subject: [PATCH 25/33] fix: removed duplicates --- src/visitors/accessor/missing.js | 27 --------------------------- src/visitors/accessor/missingSome.js | 16 ---------------- 2 files changed, 43 deletions(-) delete mode 100644 src/visitors/accessor/missing.js delete mode 100644 src/visitors/accessor/missingSome.js diff --git a/src/visitors/accessor/missing.js b/src/visitors/accessor/missing.js deleted file mode 100644 index 8dee315..0000000 --- a/src/visitors/accessor/missing.js +++ /dev/null @@ -1,27 +0,0 @@ -import isArray from '../../helpers/isArray'; -import variable from '../../operations/accessor/variable' - -function missing() { - /* - Missing can receive many keys as many arguments, like {"missing:[1,2]} - Missing can also receive *one* argument that is an array of keys, - which typically happens if it's actually acting on the output of another command - (like 'if' or 'merge') - */ - - const missing = []; - const keys = isArray(arguments[0]) ? arguments[0] : arguments; - - for(let i = 0; i < keys.length; i++) { - const key = keys[i]; - const value = variable.call(this, {"var": key}, arguments); - - if(value === null || value === "") { - missing.push(key); - } - } - - return missing; -}; - -export default missing; diff --git a/src/visitors/accessor/missingSome.js b/src/visitors/accessor/missingSome.js deleted file mode 100644 index d139925..0000000 --- a/src/visitors/accessor/missingSome.js +++ /dev/null @@ -1,16 +0,0 @@ -import missing from './missing'; - -function missingSome(need_count, options) { - // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. - var are_missing = missing.call(this, {"missing": options}); - - if(options.length - are_missing.length >= need_count) { - return []; - }else{ - return are_missing; - } -} - -missingSome.code = 'missing_some'; - -export default missingSome; From 42a7a32c56bc21116c3633f9f594a5309c66faf5 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 17:10:35 +0100 Subject: [PATCH 26/33] chore: added prettier with eslint --- .eslintrc.json | 33 ++++----------------------------- .prettierrc | 6 ++++++ package.json | 12 ++++++++++-- 3 files changed, 20 insertions(+), 31 deletions(-) create mode 100644 .prettierrc diff --git a/.eslintrc.json b/.eslintrc.json index 12bf434..915e83c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,33 +1,8 @@ { - "extends": "google", - + "extends": ["airbnb", "plugin:prettier/recommended"], "rules": { - "no-var": "off", - "indent": [ - "error", - 2 - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "double", { - "avoidEscape": true - } - ], - "semi": [ - "error", - "always" - ], - "max-len": [ - "warn", { - "ignoreComments": true - } - ], - "prefer-spread": ["off"], - "prefer-rest-params": ["off"], - "camelcase" : ["off"] + "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], + "no-use-before-define": ["error", { "functions": false }], + "camelcase": "off" } } diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..266594c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "semi": true, + "singleQuote": true, + "bracketSpacing": true +} diff --git a/package.json b/package.json index 1f539e7..3071da8 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,14 @@ "@babel/plugin-proposal-export-default-from": "^7.2.0", "@babel/preset-env": "^7.3.1", "cross-env": "^5.2.0", - "eslint": "^3.9.1", - "eslint-config-google": "^0.7.0", + "eslint": "^5.14.1", + "eslint-config-airbnb": "^17.1.0", + "eslint-config-prettier": "^4.0.0", + "eslint-plugin-import": "^2.16.0", + "eslint-plugin-jsx-a11y": "^6.2.1", + "eslint-plugin-prettier": "^3.0.1", + "eslint-plugin-react": "^7.12.4", + "prettier": "^1.16.4", "qunit": "^2.9.2", "request": "^2.65.0", "rimraf": "^2.6.3", @@ -30,6 +36,8 @@ "rollup-plugin-uglify": "^6.0.2" }, "scripts": { + "lint": "eslint src tests", + "lint-fix": "npm run lint -- --fix", "test": "cross-env NODE_ENV=test qunit 'tests/**/*.js' -r tap", "pretest": "npm run build-package", "build": "cross-env NODE_ENV=production npm run build-lib", From 255539c7e65effe2b1dc89f701c2eac400138cb1 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 17:10:55 +0100 Subject: [PATCH 27/33] chore: fix eslint, prettier errors --- dist/jsonLogic.js | 116 +++++---- dist/jsonLogic.js.map | 2 +- dist/jsonLogic.min.js | 2 +- dist/jsonLogic.min.js.map | 2 +- src/createJsonLogic.js | 49 ++-- src/helpers/arrayUnique.js | 2 +- src/helpers/getValues.js | 2 +- src/helpers/isLogic.js | 2 +- src/helpers/ruleLike.js | 44 ++-- src/helpers/truthy.js | 2 +- src/helpers/usesData.js | 20 +- src/index.js | 12 +- src/operations/accessor/missing.js | 19 +- src/operations/accessor/missingSome.js | 9 +- src/operations/accessor/variable.js | 12 +- src/operations/arithmetic/add.js | 4 +- src/operations/arithmetic/multiply.js | 4 +- src/operations/arithmetic/substract.js | 5 +- src/operations/array/index.js | 1 + src/operations/array/merge.js | 4 +- src/operations/logic/equal.js | 1 + src/operations/logic/falsy.js | 2 +- src/operations/logic/notEqual.js | 1 + src/operations/logic/truthy.js | 2 +- src/operations/misc/indexOf.js | 4 +- src/operations/misc/log.js | 1 + src/operations/misc/method.js | 5 +- src/operations/numeric/lower.js | 2 +- src/operations/numeric/lowerEqual.js | 2 +- src/operations/numeric/max.js | 4 +- src/operations/numeric/min.js | 4 +- src/operations/string/cat.js | 4 +- src/operations/string/substr.js | 2 +- src/visitors/array/all.js | 8 +- src/visitors/array/filter.js | 10 +- src/visitors/array/index.js | 12 +- src/visitors/array/map.js | 6 +- src/visitors/array/none.js | 2 +- src/visitors/array/reduce.js | 9 +- src/visitors/array/some.js | 2 +- src/visitors/logic/and.js | 6 +- src/visitors/logic/condition.js | 8 +- src/visitors/logic/index.js | 6 +- src/visitors/logic/or.js | 6 +- tests/tests.js | 331 ++++++++++++------------- 45 files changed, 380 insertions(+), 373 deletions(-) diff --git a/dist/jsonLogic.js b/dist/jsonLogic.js index 9653c76..56ef4e1 100644 --- a/dist/jsonLogic.js +++ b/dist/jsonLogic.js @@ -20,8 +20,28 @@ return _typeof(obj); } + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } + } + + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance"); + } + function isLogic(logic) { - return _typeof(logic) === "object" && // An object + return _typeof(logic) === 'object' && // An object logic !== null && // but not null !isArray(logic) && // and not an array Object.keys(logic).length === 1 // with exactly one key @@ -78,7 +98,9 @@ delete visitors[name]; } - function apply(logic, data) { + function apply(logic) { + var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + // Does this array contain logic? Only one way to find out. if (isArray(logic)) { return logic.map(function (l) { @@ -91,7 +113,6 @@ return logic; } - data = data || {}; var op = getOperator(logic); var values = logic[op]; var i; // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} @@ -114,15 +135,17 @@ var operator = operations[op]; - if (typeof operator === "function") { + if (typeof operator === 'function') { if (operator.withApply) { values.unshift(apply); } return operator.apply(data, values); - } else if (op.indexOf(".") > 0) { + } + + if (op.indexOf('.') > 0) { // Contains a dot, and not in the 0th position - var sub_ops = String(op).split("."); + var sub_ops = String(op).split('.'); var operation = operations; for (i = 0; i < sub_ops.length; i++) { @@ -130,14 +153,14 @@ operation = operation[sub_ops[i]]; if (operation === undefined) { - throw new Error("Unrecognized operation " + op + " (failed at " + sub_ops.slice(0, i + 1).join(".") + ")"); + throw new Error("Unrecognized operation ".concat(op, " (failed at ").concat(sub_ops.slice(0, i + 1).join('.'), ")")); } } return operation.apply(data, values); } - throw new Error("Unrecognized operation " + op); + throw new Error("Unrecognized operation ".concat(op)); } return { @@ -153,11 +176,11 @@ var not_found = b === undefined ? null : b; var data = this; - if (typeof a === "undefined" || a === "" || a === null) { + if (typeof a === 'undefined' || a === '' || a === null) { return data; } - var sub_props = String(a).split("."); + var sub_props = String(a).split('.'); for (var i = 0; i < sub_props.length; i++) { if (data === null) { @@ -184,7 +207,7 @@ which typically happens if it's actually acting on the output of another command (like 'if' or 'merge') */ - var missing = []; + var are_missing = []; for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; @@ -195,29 +218,30 @@ for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = apply({ - "var": key + var: key }, this); - if (value === null || value === "") { - missing.push(key); + if (value === null || value === '') { + are_missing.push(key); } } - return missing; + return are_missing; } + missing.withApply = true; function missingSome(apply, need_count, options) { // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. var are_missing = apply({ - "missing": options + missing: options }, this); if (options.length - are_missing.length >= need_count) { return []; - } else { - return are_missing; } + + return are_missing; } missingSome.code = 'missing_some'; @@ -262,9 +286,9 @@ function substract(a, b) { if (b === undefined) { return -a; - } else { - return a - b; } + + return a - b; } substract.code = '-'; @@ -279,7 +303,10 @@ }, []); } + // eslint-disable-next-line import/prefer-default-export + function equal(a, b) { + // eslint-disable-next-line eqeqeq return a == b; } @@ -306,6 +333,7 @@ falsy.code = '!'; function notEqual(a, b) { + // eslint-disable-next-line eqeqeq return a != b; } @@ -326,7 +354,7 @@ truthy.code = '!!'; function indexOf(a, b) { - if (!b || typeof b.indexOf === "undefined") return false; + if (!b || typeof b.indexOf === 'undefined') return false; return b.indexOf(a) !== -1; } @@ -337,8 +365,8 @@ return a; } - function method(obj, method, args) { - return obj[method].apply(obj, args); + function method(obj, methodName, args) { + return obj[methodName].apply(obj, args); } function greater(a, b) { @@ -366,15 +394,19 @@ lowerEqual.code = '<='; function max() { - return Math.max.apply(this, arguments); + return Math.max.apply(Math, arguments); } function min() { - return Math.min.apply(this, arguments); + return Math.min.apply(Math, arguments); } function cat() { - return Array.prototype.join.call(arguments, ""); + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.join(''); } function substr(source, start, end) { @@ -466,7 +498,7 @@ function none(apply, data, values) { var filtered = apply({ - 'filter': values + filter: values }, data); return filtered.length === 0; } @@ -482,15 +514,15 @@ return scopedData.reduce(function (accumulator, current) { return apply(scopedLogic, { - 'current': current, - 'accumulator': accumulator + current: current, + accumulator: accumulator }); }, initial); } function some(apply, data, values) { var filtered = apply({ - 'filter': values + filter: values }, data); return filtered.length > 0; } @@ -599,13 +631,13 @@ values = [values]; } - if (op === "var") { + if (op === 'var') { // This doesn't cover the case where the arg to var is itself a rule. collection.push(values[0]); } else { // Recursion! - values.map(function (val) { - collection.push.apply(collection, usesData(val)); + values.forEach(function (val) { + collection.push.apply(collection, _toConsumableArray(usesData(val))); }); } } @@ -620,20 +652,20 @@ } // TODO : Deep object equivalency? - if (pattern === "@") { + if (pattern === '@') { return true; } // Wildcard! - if (pattern === "number") { - return typeof rule === "number"; + if (pattern === 'number') { + return typeof rule === 'number'; } - if (pattern === "string") { - return typeof rule === "string"; + if (pattern === 'string') { + return typeof rule === 'string'; } - if (pattern === "array") { + if (pattern === 'array') { // !logic test might be superfluous in JavaScript return isArray(rule) && !isLogic(rule); } @@ -643,7 +675,7 @@ var pattern_op = getOperator(pattern); var rule_op = getOperator(rule); - if (pattern_op === "@" || pattern_op === rule_op) { + if (pattern_op === '@' || pattern_op === rule_op) { // echo "\nOperators match, go deeper\n"; return ruleLike(getValues(rule, false), getValues(pattern, false)); } @@ -670,9 +702,9 @@ } return true; // If they *all* passed, we pass - } else { - return false; // Pattern is array, rule isn't } + + return false; // Pattern is array, rule isn't } // Not logic, not array, not a === match for rule. diff --git a/dist/jsonLogic.js.map b/dist/jsonLogic.js.map index dee07aa..127335b 100644 --- a/dist/jsonLogic.js.map +++ b/dist/jsonLogic.js.map @@ -1 +1 @@ -{"version":3,"file":"jsonLogic.js","sources":["../src/helpers/isArray.js","../src/helpers/isLogic.js","../src/helpers/getOperator.js","../src/createJsonLogic.js","../src/operations/accessor/variable.js","../src/operations/accessor/missing.js","../src/operations/accessor/missingSome.js","../src/operations/arithmetic/add.js","../src/operations/arithmetic/divide.js","../src/operations/arithmetic/modulo.js","../src/operations/arithmetic/multiply.js","../src/operations/arithmetic/substract.js","../src/operations/array/merge.js","../src/operations/logic/equal.js","../src/helpers/truthy.js","../src/operations/logic/falsy.js","../src/operations/logic/notEqual.js","../src/operations/logic/strictEqual.js","../src/operations/logic/strictNotEqual.js","../src/operations/logic/truthy.js","../src/operations/misc/indexOf.js","../src/operations/misc/log.js","../src/operations/misc/method.js","../src/operations/numeric/greater.js","../src/operations/numeric/greaterEqual.js","../src/operations/numeric/lower.js","../src/operations/numeric/lowerEqual.js","../src/operations/numeric/max.js","../src/operations/numeric/min.js","../src/operations/string/cat.js","../src/operations/string/substr.js","../src/visitors/array/all.js","../src/visitors/array/filter.js","../src/visitors/array/map.js","../src/visitors/array/none.js","../src/visitors/array/reduce.js","../src/visitors/array/some.js","../src/visitors/logic/and.js","../src/visitors/logic/condition.js","../src/visitors/logic/or.js","../src/helpers/getValues.js","../src/helpers/arrayUnique.js","../src/helpers/usesData.js","../src/helpers/ruleLike.js","../src/index.js"],"sourcesContent":["export default Array.isArray;\n","import isArray from './isArray';\n\nfunction isLogic(logic) {\n return (\n typeof logic === \"object\" && // An object\n logic !== null && // but not null\n !isArray(logic) && // and not an array\n Object.keys(logic).length === 1 // with exactly one key\n );\n}\n\nexport default isLogic;\n","function getOperator(logic) {\n return Object.keys(logic)[0];\n}\n\nexport default getOperator;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(function(name) {\n const operation = _operations[name];\n\n addOperation(operation.code || name , operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(function (name) {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach((key) => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data) {\n // Does this array contain logic? Only one way to find out.\n if(isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if( ! isLogic(logic) ) {\n return logic;\n }\n\n data = data || {};\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if( ! isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if(typeof operator === \"function\") {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }else if(op.indexOf(\".\") > 0) { // Contains a dot, and not in the 0th position\n var sub_ops = String(op).split(\".\");\n var operation = operations;\n for(i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if(operation === undefined) {\n throw new Error(\"Unrecognized operation \" + op +\n \" (failed at \" + sub_ops.slice(0, i+1).join(\".\") + \")\");\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(\"Unrecognized operation \" + op );\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","function variable(a, b) {\n const not_found = (b === undefined) ? null : b;\n let data = this;\n\n if(typeof a === \"undefined\" || a===\"\" || a===null) {\n return data;\n }\n\n const sub_props = String(a).split(\".\");\n\n for(let i = 0; i < sub_props.length; i++) {\n if(data === null) {\n return not_found;\n }\n // Descending into data\n data = data[sub_props[i]];\n if(data === undefined) {\n return not_found;\n }\n }\n\n return data;\n}\n\nvariable.code = 'var';\n\nexport default variable;\n","import isArray from '../../helpers/isArray';\nimport variable from './variable'\n\nfunction missing(apply, ...args) {\n /*\n Missing can receive many keys as many arguments, like {\"missing:[1,2]}\n Missing can also receive *one* argument that is an array of keys,\n which typically happens if it's actually acting on the output of another command\n (like 'if' or 'merge')\n */\n\n var missing = [];\n var keys = isArray(args[0]) ? args[0] : args;\n\n for(var i = 0; i < keys.length; i++) {\n var key = keys[i];\n var value = apply({\"var\": key}, this);\n if(value === null || value === \"\") {\n missing.push(key);\n }\n }\n\n return missing;\n};\n\nmissing.withApply = true;\n\nexport default missing;\n","import missing from './missing';\n\nfunction missingSome(apply, need_count, options) {\n // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.\n const are_missing = apply({\"missing\": options}, this);\n\n if(options.length - are_missing.length >= need_count) {\n return [];\n }else{\n return are_missing;\n }\n}\n\nmissingSome.code = 'missing_some';\nmissingSome.withApply = true;\n\nexport default missingSome;\n","function add(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) + parseFloat(b, 10);\n }, 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function divide(a, b) {\n return a / b;\n}\n\ndivide.code = '/';\n\nexport default divide;\n","function modulo(a, b) {\n return a % b;\n}\n\nmodulo.code = '%';\n\nexport default modulo;\n","function multiply(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) * parseFloat(b, 10);\n }, 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function substract(a, b) {\n if(b === undefined) {\n return -a;\n }else{\n return a - b;\n }\n}\n\nsubstract.code = '-';\n\nexport default substract;\n","function merge(...args) {\n return args.reduce(function(a, b) {\n return a.concat(b);\n }, []);\n}\n\nexport default merge;\n","function equal(a, b) {\n return a == b;\n}\n\nequal.code = '==';\n\nexport default equal;\n","import isArray from './isArray';\n\n/*\n This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.\n\n Spec and rationale here: http://jsonlogic.com/truthy\n */\nfunction truthy(value) {\n if(isArray(value) && value.length === 0) {\n return false;\n }\n\n return !!value;\n}\n\nexport default truthy;\n","import truthy from '../../helpers/truthy'\n\nfunction falsy(a) {\n return !truthy(a);\n}\n\nfalsy.code = '!';\n\nexport default falsy;\n","function notEqual(a, b) {\n return a != b;\n}\n\nnotEqual.code = '!=';\n\nexport default notEqual;\n","function strictEqual(a, b) {\n return a === b;\n}\n\nstrictEqual.code = '===';\n\nexport default strictEqual;\n","function strictNotEqual(a, b) {\n return a !== b;\n}\n\nstrictNotEqual.code = '!==';\n\nexport default strictNotEqual;\n","import truthy from '../../helpers/truthy'\n\ntruthy.code = '!!';\n\nexport default truthy;\n","function indexOf(a, b) {\n if(!b || typeof b.indexOf === \"undefined\") return false;\n return (b.indexOf(a) !== -1);\n}\n\nindexOf.code = 'in';\n\nexport default indexOf;\n","function log(a) {\n console.log(a);\n\n return a;\n}\n\nexport default log;\n","function method(obj, method, args) {\n return obj[method].apply(obj, args);\n}\n\nexport default method;\n","function greater(a, b) {\n return a > b;\n}\n\ngreater.code = '>';\n\nexport default greater;\n","function greaterEqual(a, b) {\n return a >= b;\n}\n\ngreaterEqual.code = '>=';\n\nexport default greaterEqual;\n","function lower(a, b, c) {\n return (c === undefined) ? a < b : (a < b) && (b < c);\n}\n\nlower.code = '<';\n\nexport default lower;\n","function lowerEqual(a, b, c) {\n return (c === undefined) ? a <= b : (a <= b) && (b <= c);\n}\n\nlowerEqual.code = '<=';\n\nexport default lowerEqual;\n","function max() {\n return Math.max.apply(this, arguments);\n}\n\nexport default max;\n","function min() {\n return Math.min.apply(this, arguments);\n}\n\nexport default min;\n","function cat() {\n return Array.prototype.join.call(arguments, \"\");\n}\n\nexport default cat;\n","function substr(source, start, end) {\n if (end < 0) {\n // JavaScript doesn't support negative end, this emulates PHP behavior\n const temp = String(source).substr(start);\n return temp.substr(0, temp.length + end);\n }\n return String(source).substr(start, end);\n};\n\nexport default substr;\n","import truthy from \"../../helpers/truthy\";\n\nfunction all(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n // All of an empty set is false. Note, some and none have correct fallback after the for loop\n if( ! scopedData.length) {\n return false;\n }\n for(let i=0; i < scopedData.length; i+=1) {\n if( ! truthy( apply(scopedLogic, scopedData[i]) )) {\n return false; // First falsy, short circuit\n }\n }\n return true; // All were truthy\n}\n\nexport default all;\n","import isArray from '../../helpers/isArray';\nimport truthy from \"../../helpers/truthy\";\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum){\n return truthy( apply(scopedLogic, datum));\n });\n}\n\nexport default filter\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum){\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","function none(apply, data, values) {\n const filtered = apply({'filter' : values}, data);\n\n return filtered.length === 0;\n}\n\nexport default none;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if ( ! isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n function(accumulator, current){\n return apply(\n scopedLogic,\n {'current':current, 'accumulator':accumulator}\n );\n },\n initial\n );\n}\n\nexport default reduce;\n","function some(apply, data, values) {\n const filtered = apply({'filter' : values}, data);\n\n return filtered.length > 0;\n}\n\nexport default some;\n","import truthy from \"../../helpers/truthy\";\n\nfunction and(apply, data, values) {\n let current;\n\n for(let i=0; i < values.length; i++) {\n current = apply(values[i], data);\n if( ! truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default and;\n","import truthy from '../../helpers/truthy';\n\nfunction condition(apply, data, values) {\n let i;\n\n /* 'if' should be called with a odd number of parameters, 3 or greater\n This works on the pattern:\n if( 0 ){ 1 }else{ 2 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };\n\n The implementation is:\n For pairs of values (0,1 then 2,3 then 4,5 etc)\n If the first evaluates truthy, evaluate and return the second\n If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)\n given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)\n given 0 parameters, return NULL (not great practice, but there was no Else)\n */\n for(i = 0; i < values.length - 1; i += 2) {\n if( truthy( apply(values[i], data) ) ) {\n return apply(values[i+1], data);\n }\n }\n\n if(values.length === i+1) {\n return apply(values[i], data);\n }\n\n return null;\n}\n\ncondition.code = ['if', '?:'];\n\nexport default condition;\n","import truthy from \"../../helpers/truthy\";\n\nfunction or(apply, data, values) {\n let current;\n\n for(let i=0; i < values.length; i++) {\n current = apply(values[i], data);\n if( truthy(current) ) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default or;\n","import getOperator from './getOperator'\n\nfunction getValues(logic) {\n return logic[getOperator(logic)];\n}\n\nexport default getValues;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i=0, l=array.length; i addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","function variable(a, b) {\n const not_found = b === undefined ? null : b;\n let data = this;\n\n if (typeof a === 'undefined' || a === '' || a === null) {\n return data;\n }\n\n const sub_props = String(a).split('.');\n\n for (let i = 0; i < sub_props.length; i++) {\n if (data === null) {\n return not_found;\n }\n // Descending into data\n data = data[sub_props[i]];\n if (data === undefined) {\n return not_found;\n }\n }\n\n return data;\n}\n\nvariable.code = 'var';\n\nexport default variable;\n","import isArray from '../../helpers/isArray';\n\nfunction missing(apply, ...args) {\n /*\n Missing can receive many keys as many arguments, like {\"missing:[1,2]}\n Missing can also receive *one* argument that is an array of keys,\n which typically happens if it's actually acting on the output of another command\n (like 'if' or 'merge')\n */\n\n const are_missing = [];\n const keys = isArray(args[0]) ? args[0] : args;\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const value = apply({ var: key }, this);\n if (value === null || value === '') {\n are_missing.push(key);\n }\n }\n\n return are_missing;\n}\n\nmissing.withApply = true;\n\nexport default missing;\n","function missingSome(apply, need_count, options) {\n // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.\n const are_missing = apply({ missing: options }, this);\n\n if (options.length - are_missing.length >= need_count) {\n return [];\n }\n return are_missing;\n}\n\nmissingSome.code = 'missing_some';\nmissingSome.withApply = true;\n\nexport default missingSome;\n","function add(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) + parseFloat(b, 10);\n }, 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function divide(a, b) {\n return a / b;\n}\n\ndivide.code = '/';\n\nexport default divide;\n","function modulo(a, b) {\n return a % b;\n}\n\nmodulo.code = '%';\n\nexport default modulo;\n","function multiply(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) * parseFloat(b, 10);\n }, 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function substract(a, b) {\n if (b === undefined) {\n return -a;\n }\n return a - b;\n}\n\nsubstract.code = '-';\n\nexport default substract;\n","function merge(...args) {\n return args.reduce(function(a, b) {\n return a.concat(b);\n }, []);\n}\n\nexport default merge;\n","// eslint-disable-next-line import/prefer-default-export\nexport { default as merge } from './merge';\n","function equal(a, b) {\n // eslint-disable-next-line eqeqeq\n return a == b;\n}\n\nequal.code = '==';\n\nexport default equal;\n","import isArray from './isArray';\n\n/*\n This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.\n\n Spec and rationale here: http://jsonlogic.com/truthy\n */\nfunction truthy(value) {\n if (isArray(value) && value.length === 0) {\n return false;\n }\n\n return !!value;\n}\n\nexport default truthy;\n","import truthy from '../../helpers/truthy';\n\nfunction falsy(a) {\n return !truthy(a);\n}\n\nfalsy.code = '!';\n\nexport default falsy;\n","function notEqual(a, b) {\n // eslint-disable-next-line eqeqeq\n return a != b;\n}\n\nnotEqual.code = '!=';\n\nexport default notEqual;\n","function strictEqual(a, b) {\n return a === b;\n}\n\nstrictEqual.code = '===';\n\nexport default strictEqual;\n","function strictNotEqual(a, b) {\n return a !== b;\n}\n\nstrictNotEqual.code = '!==';\n\nexport default strictNotEqual;\n","import truthy from '../../helpers/truthy';\n\ntruthy.code = '!!';\n\nexport default truthy;\n","function indexOf(a, b) {\n if (!b || typeof b.indexOf === 'undefined') return false;\n return b.indexOf(a) !== -1;\n}\n\nindexOf.code = 'in';\n\nexport default indexOf;\n","function log(a) {\n console.log(a);\n\n return a;\n}\n\nexport default log;\n","function method(obj, methodName, args) {\n return obj[methodName].apply(obj, args);\n}\n\nexport default method;\n","function greater(a, b) {\n return a > b;\n}\n\ngreater.code = '>';\n\nexport default greater;\n","function greaterEqual(a, b) {\n return a >= b;\n}\n\ngreaterEqual.code = '>=';\n\nexport default greaterEqual;\n","function lower(a, b, c) {\n return c === undefined ? a < b : a < b && b < c;\n}\n\nlower.code = '<';\n\nexport default lower;\n","function lowerEqual(a, b, c) {\n return c === undefined ? a <= b : a <= b && b <= c;\n}\n\nlowerEqual.code = '<=';\n\nexport default lowerEqual;\n","function max(...args) {\n return Math.max(...args);\n}\n\nexport default max;\n","function min(...args) {\n return Math.min(...args);\n}\n\nexport default min;\n","function cat(...args) {\n return args.join('');\n}\n\nexport default cat;\n","function substr(source, start, end) {\n if (end < 0) {\n // JavaScript doesn't support negative end, this emulates PHP behavior\n const temp = String(source).substr(start);\n return temp.substr(0, temp.length + end);\n }\n return String(source).substr(start, end);\n}\n\nexport default substr;\n","import truthy from '../../helpers/truthy';\n\nfunction all(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n // All of an empty set is false. Note, some and none have correct fallback after the for loop\n if (!scopedData.length) {\n return false;\n }\n for (let i = 0; i < scopedData.length; i += 1) {\n if (!truthy(apply(scopedLogic, scopedData[i]))) {\n return false; // First falsy, short circuit\n }\n }\n return true; // All were truthy\n}\n\nexport default all;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum) {\n return truthy(apply(scopedLogic, datum));\n });\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum) {\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","function none(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length === 0;\n}\n\nexport default none;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(function(accumulator, current) {\n return apply(scopedLogic, { current, accumulator });\n }, initial);\n}\n\nexport default reduce;\n","function some(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length > 0;\n}\n\nexport default some;\n","import truthy from '../../helpers/truthy';\n\nfunction and(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (!truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default and;\n","import truthy from '../../helpers/truthy';\n\nfunction condition(apply, data, values) {\n let i;\n\n /* 'if' should be called with a odd number of parameters, 3 or greater\n This works on the pattern:\n if( 0 ){ 1 }else{ 2 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };\n\n The implementation is:\n For pairs of values (0,1 then 2,3 then 4,5 etc)\n If the first evaluates truthy, evaluate and return the second\n If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)\n given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)\n given 0 parameters, return NULL (not great practice, but there was no Else)\n */\n for (i = 0; i < values.length - 1; i += 2) {\n if (truthy(apply(values[i], data))) {\n return apply(values[i + 1], data);\n }\n }\n\n if (values.length === i + 1) {\n return apply(values[i], data);\n }\n\n return null;\n}\n\ncondition.code = ['if', '?:'];\n\nexport default condition;\n","import truthy from '../../helpers/truthy';\n\nfunction or(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default or;\n","import getOperator from './getOperator';\n\nfunction getValues(logic) {\n return logic[getOperator(logic)];\n}\n\nexport default getValues;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(function(val) {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport getValues from './getValues';\n\nfunction ruleLike(rule, pattern) {\n // console.log(\"Is \". JSON.stringify(rule) . \" like \" . JSON.stringify(pattern) . \"?\");\n if (pattern === rule) {\n return true;\n } // TODO : Deep object equivalency?\n if (pattern === '@') {\n return true;\n } // Wildcard!\n if (pattern === 'number') {\n return typeof rule === 'number';\n }\n if (pattern === 'string') {\n return typeof rule === 'string';\n }\n if (pattern === 'array') {\n // !logic test might be superfluous in JavaScript\n return isArray(rule) && !isLogic(rule);\n }\n\n if (isLogic(pattern)) {\n if (isLogic(rule)) {\n const pattern_op = getOperator(pattern);\n const rule_op = getOperator(rule);\n\n if (pattern_op === '@' || pattern_op === rule_op) {\n // echo \"\\nOperators match, go deeper\\n\";\n return ruleLike(getValues(rule, false), getValues(pattern, false));\n }\n }\n return false; // pattern is logic, rule isn't, can't be eq\n }\n\n if (isArray(pattern)) {\n if (isArray(rule)) {\n if (pattern.length !== rule.length) {\n return false;\n }\n /*\n Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)\n */\n for (let i = 0; i < pattern.length; i += 1) {\n // If any fail, we fail\n if (!ruleLike(rule[i], pattern[i])) {\n return false;\n }\n }\n return true; // If they *all* passed, we pass\n }\n return false; // Pattern is array, rule isn't\n }\n\n // Not logic, not array, not a === match for rule.\n return false;\n}\n\nexport default ruleLike;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n"],"names":["Array","isArray","isLogic","logic","Object","keys","length","getOperator","createJsonLogic","_operations","_visitors","operations","visitors","forEach","name","operation","addOperation","code","visitor","addVisitor","removeOperation","key","removeVisitor","apply","data","map","l","op","values","i","val","operator","withApply","unshift","indexOf","sub_ops","String","split","undefined","Error","slice","join","add_operation","rm_operation","add_visitor","rm_visitor","variable","a","b","not_found","sub_props","missing","are_missing","args","value","var","push","missingSome","need_count","options","add","reduce","parseFloat","divide","modulo","multiply","substract","merge","concat","equal","truthy","falsy","notEqual","strictEqual","strictNotEqual","log","console","method","obj","methodName","greater","greaterEqual","lower","c","lowerEqual","max","Math","min","cat","substr","source","start","end","temp","all","scopedData","scopedLogic","filter","datum","none","filtered","initial","accumulator","current","some","and","condition","or","getValues","arrayUnique","array","usesData","collection","ruleLike","rule","pattern","pattern_op","rule_op","jsonLogic","is_logic","get_operator","get_values","uses_data","rule_like"],"mappings":"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"} \ No newline at end of file diff --git a/dist/jsonLogic.min.js b/dist/jsonLogic.min.js index 6285051..b55b17c 100644 --- a/dist/jsonLogic.min.js +++ b/dist/jsonLogic.min.js @@ -1,2 +1,2 @@ -!(function(n,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(n=n||self).jsonLogic=r()})(this,(function(){"use strict";var d=Array.isArray;function r(n){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n})(n)}function s(n){return"object"===r(n)&&null!==n&&!d(n)&&1===Object.keys(n).length}function p(n){return Object.keys(n)[0]}function n(n,r){var t=void 0===r?null:r,e=this;if(void 0===n||""===n||null===n)return e;for(var o=String(n).split("."),u=0;u=r?[]:e}function o(){for(var n=arguments.length,r=new Array(n),t=0;t",w.code=">=",j.code="<",E.code="<=";var O=Object.freeze({variable:n,missing:t,missingSome:e,add:o,divide:u,modulo:i,multiply:f,substract:c,merge:function(){for(var n=arguments.length,r=new Array(n),t=0;t=n?[]:e}function o(){for(var r=arguments.length,n=new Array(r),t=0;t",A.code=">=",j.code="<",E.code="<=";var O=Object.freeze({variable:r,missing:t,missingSome:e,add:o,divide:i,modulo:f,multiply:c,substract:a,merge:function(){for(var r=arguments.length,n=new Array(r),t=0;t addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data) {\n // Does this array contain logic? Only one way to find out.\n if(isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if( ! isLogic(logic) ) {\n return logic;\n }\n\n data = data || {};\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if( ! isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if(typeof operator === \"function\") {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }else if(op.indexOf(\".\") > 0) { // Contains a dot, and not in the 0th position\n var sub_ops = String(op).split(\".\");\n var operation = operations;\n for(i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if(operation === undefined) {\n throw new Error(\"Unrecognized operation \" + op +\n \" (failed at \" + sub_ops.slice(0, i+1).join(\".\") + \")\");\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(\"Unrecognized operation \" + op );\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from \"./helpers/isLogic\";\nimport truthy from \"./helpers/truthy\";\nimport getOperator from \"./helpers/getOperator\";\nimport getValues from \"./helpers/getValues\";\nimport usesData from \"./helpers/usesData\";\nimport ruleLike from \"./helpers/ruleLike\";\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n","import isArray from '../../helpers/isArray';\nimport truthy from \"../../helpers/truthy\";\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum){\n return truthy( apply(scopedLogic, datum));\n });\n}\n\nexport default filter\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if ( ! isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum){\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if ( ! isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n function(accumulator, current){\n return apply(\n scopedLogic,\n {'current':current, 'accumulator':accumulator}\n );\n },\n initial\n );\n}\n\nexport default reduce;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n var collection = [];\n\n if(isLogic(logic) ) {\n var op = getOperator(logic);\n var values = logic[op];\n\n if( ! isArray(values)) {\n values = [values];\n }\n\n if(op === \"var\") {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n }else{\n // Recursion!\n values.map(function(val) {\n collection.push.apply(collection, usesData(val) );\n });\n }\n }\n\n return arrayUnique(collection);\n};\n\nexport default usesData;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i=0, l=array.length; i addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum) {\n return truthy(apply(scopedLogic, datum));\n });\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum) {\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(function(accumulator, current) {\n return apply(scopedLogic, { current, accumulator });\n }, initial);\n}\n\nexport default reduce;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(function(val) {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n"],"names":["createJsonLogic","arrayUnique"],"mappings":"69CACqB,CAAA,uNCAA,CAAA,08BCAA,CAAA,wvBCGrB,CAAA,kFA8BmB,CAAA,qEAzBkB,CAAA,mEAQF,CAAA,mJAoCZ,CAAA,uIAwBC,CAAA,igBCnENA,CAAAA,sNCGS,CAAA,iGCHH,CAAA,sLCCG,CAAA,ydCUN,CAAA,8CChBrB,CAAA,4FDsBSC,CAAAA"} \ No newline at end of file diff --git a/src/createJsonLogic.js b/src/createJsonLogic.js index a375fb9..7ef5a72 100644 --- a/src/createJsonLogic.js +++ b/src/createJsonLogic.js @@ -7,15 +7,15 @@ function createJsonLogic(_operations, _visitors) { const visitors = {}; if (_operations) { - Object.keys(_operations).forEach(function(name) { + Object.keys(_operations).forEach(name => { const operation = _operations[name]; - addOperation(operation.code || name , operation); + addOperation(operation.code || name, operation); }); } if (_visitors) { - Object.keys(_visitors).forEach(function (name) { + Object.keys(_visitors).forEach(name => { const visitor = _visitors[name]; addVisitor(visitor.code || name, visitor); @@ -32,7 +32,7 @@ function createJsonLogic(_operations, _visitors) { function addVisitor(name, code) { if (isArray(name)) { - name.forEach((key) => addVisitor(key, code)); + name.forEach(key => addVisitor(key, code)); return; } @@ -48,26 +48,22 @@ function createJsonLogic(_operations, _visitors) { delete visitors[name]; } - function apply(logic, data) { + function apply(logic, data = {}) { // Does this array contain logic? Only one way to find out. - if(isArray(logic)) { - return logic.map(function(l) { - return apply(l, data); - }); + if (isArray(logic)) { + return logic.map(l => apply(l, data)); } // You've recursed to a primitive, stop! - if( ! isLogic(logic) ) { + if (!isLogic(logic)) { return logic; } - data = data || {}; - const op = getOperator(logic); let values = logic[op]; let i; // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} - if( ! isArray(values)) { + if (!isArray(values)) { values = [values]; } @@ -77,36 +73,39 @@ function createJsonLogic(_operations, _visitors) { } // Everyone else gets immediate depth-first recursion - values = values.map(function(val) { - return apply(val, data); - }); + values = values.map(val => apply(val, data)); // The operation is called with "data" bound to its "this" and "values" passed as arguments. // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments const operator = operations[op]; - if(typeof operator === "function") { + if (typeof operator === 'function') { if (operator.withApply) { values.unshift(apply); } return operator.apply(data, values); - }else if(op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position - var sub_ops = String(op).split("."); - var operation = operations; - for(i = 0; i < sub_ops.length; i++) { + } + if (op.indexOf('.') > 0) { + // Contains a dot, and not in the 0th position + const sub_ops = String(op).split('.'); + let operation = operations; + for (i = 0; i < sub_ops.length; i++) { // Descending into operations operation = operation[sub_ops[i]]; - if(operation === undefined) { - throw new Error("Unrecognized operation " + op + - " (failed at " + sub_ops.slice(0, i+1).join(".") + ")"); + if (operation === undefined) { + throw new Error( + `Unrecognized operation ${op} (failed at ${sub_ops + .slice(0, i + 1) + .join('.')})` + ); } } return operation.apply(data, values); } - throw new Error("Unrecognized operation " + op ); + throw new Error(`Unrecognized operation ${op}`); } return { diff --git a/src/helpers/arrayUnique.js b/src/helpers/arrayUnique.js index 1cde3a7..8dd2dfb 100644 --- a/src/helpers/arrayUnique.js +++ b/src/helpers/arrayUnique.js @@ -5,7 +5,7 @@ */ function arrayUnique(array) { const a = []; - for (let i=0, l=array.length; i { + collection.push(...usesData(val)); }); } } return arrayUnique(collection); -}; +} export default usesData; diff --git a/src/index.js b/src/index.js index f26a1c0..f6f2450 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,12 @@ import createJsonLogic from './createJsonLogic'; import * as operations from './operations'; import * as visitors from './visitors'; -import isLogic from "./helpers/isLogic"; -import truthy from "./helpers/truthy"; -import getOperator from "./helpers/getOperator"; -import getValues from "./helpers/getValues"; -import usesData from "./helpers/usesData"; -import ruleLike from "./helpers/ruleLike"; +import isLogic from './helpers/isLogic'; +import truthy from './helpers/truthy'; +import getOperator from './helpers/getOperator'; +import getValues from './helpers/getValues'; +import usesData from './helpers/usesData'; +import ruleLike from './helpers/ruleLike'; const jsonLogic = createJsonLogic(operations, visitors); diff --git a/src/operations/accessor/missing.js b/src/operations/accessor/missing.js index 0af0618..9b1b169 100644 --- a/src/operations/accessor/missing.js +++ b/src/operations/accessor/missing.js @@ -1,5 +1,4 @@ import isArray from '../../helpers/isArray'; -import variable from './variable' function missing(apply, ...args) { /* @@ -9,19 +8,19 @@ function missing(apply, ...args) { (like 'if' or 'merge') */ - var missing = []; - var keys = isArray(args[0]) ? args[0] : args; + const are_missing = []; + const keys = isArray(args[0]) ? args[0] : args; - for(var i = 0; i < keys.length; i++) { - var key = keys[i]; - var value = apply({"var": key}, this); - if(value === null || value === "") { - missing.push(key); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = apply({ var: key }, this); + if (value === null || value === '') { + are_missing.push(key); } } - return missing; -}; + return are_missing; +} missing.withApply = true; diff --git a/src/operations/accessor/missingSome.js b/src/operations/accessor/missingSome.js index 2889cf1..c62a897 100644 --- a/src/operations/accessor/missingSome.js +++ b/src/operations/accessor/missingSome.js @@ -1,14 +1,11 @@ -import missing from './missing'; - function missingSome(apply, need_count, options) { // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. - const are_missing = apply({"missing": options}, this); + const are_missing = apply({ missing: options }, this); - if(options.length - are_missing.length >= need_count) { + if (options.length - are_missing.length >= need_count) { return []; - }else{ - return are_missing; } + return are_missing; } missingSome.code = 'missing_some'; diff --git a/src/operations/accessor/variable.js b/src/operations/accessor/variable.js index 3f3825b..d9dc68f 100644 --- a/src/operations/accessor/variable.js +++ b/src/operations/accessor/variable.js @@ -1,20 +1,20 @@ function variable(a, b) { - const not_found = (b === undefined) ? null : b; + const not_found = b === undefined ? null : b; let data = this; - if(typeof a === "undefined" || a==="" || a===null) { + if (typeof a === 'undefined' || a === '' || a === null) { return data; } - const sub_props = String(a).split("."); + const sub_props = String(a).split('.'); - for(let i = 0; i < sub_props.length; i++) { - if(data === null) { + for (let i = 0; i < sub_props.length; i++) { + if (data === null) { return not_found; } // Descending into data data = data[sub_props[i]]; - if(data === undefined) { + if (data === undefined) { return not_found; } } diff --git a/src/operations/arithmetic/add.js b/src/operations/arithmetic/add.js index d314e20..f167bf6 100644 --- a/src/operations/arithmetic/add.js +++ b/src/operations/arithmetic/add.js @@ -1,7 +1,5 @@ function add(...args) { - return args.reduce(function(a, b) { - return parseFloat(a, 10) + parseFloat(b, 10); - }, 0); + return args.reduce((a, b) => parseFloat(a, 10) + parseFloat(b, 10), 0); } add.code = '+'; diff --git a/src/operations/arithmetic/multiply.js b/src/operations/arithmetic/multiply.js index 2e72479..be4592d 100644 --- a/src/operations/arithmetic/multiply.js +++ b/src/operations/arithmetic/multiply.js @@ -1,7 +1,5 @@ function multiply(...args) { - return args.reduce(function(a, b) { - return parseFloat(a, 10) * parseFloat(b, 10); - }, 1); + return args.reduce((a, b) => parseFloat(a, 10) * parseFloat(b, 10), 1); } multiply.code = '*'; diff --git a/src/operations/arithmetic/substract.js b/src/operations/arithmetic/substract.js index 24c2d95..086b82f 100644 --- a/src/operations/arithmetic/substract.js +++ b/src/operations/arithmetic/substract.js @@ -1,9 +1,8 @@ function substract(a, b) { - if(b === undefined) { + if (b === undefined) { return -a; - }else{ - return a - b; } + return a - b; } substract.code = '-'; diff --git a/src/operations/array/index.js b/src/operations/array/index.js index f2e5c7a..86904cd 100644 --- a/src/operations/array/index.js +++ b/src/operations/array/index.js @@ -1 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export export { default as merge } from './merge'; diff --git a/src/operations/array/merge.js b/src/operations/array/merge.js index 0d31ec8..ad540d2 100644 --- a/src/operations/array/merge.js +++ b/src/operations/array/merge.js @@ -1,7 +1,5 @@ function merge(...args) { - return args.reduce(function(a, b) { - return a.concat(b); - }, []); + return args.reduce((a, b) => a.concat(b), []); } export default merge; diff --git a/src/operations/logic/equal.js b/src/operations/logic/equal.js index 31e0743..471d9ce 100644 --- a/src/operations/logic/equal.js +++ b/src/operations/logic/equal.js @@ -1,4 +1,5 @@ function equal(a, b) { + // eslint-disable-next-line eqeqeq return a == b; } diff --git a/src/operations/logic/falsy.js b/src/operations/logic/falsy.js index bda904c..dd49749 100644 --- a/src/operations/logic/falsy.js +++ b/src/operations/logic/falsy.js @@ -1,4 +1,4 @@ -import truthy from '../../helpers/truthy' +import truthy from '../../helpers/truthy'; function falsy(a) { return !truthy(a); diff --git a/src/operations/logic/notEqual.js b/src/operations/logic/notEqual.js index 98c7b72..af586a3 100644 --- a/src/operations/logic/notEqual.js +++ b/src/operations/logic/notEqual.js @@ -1,4 +1,5 @@ function notEqual(a, b) { + // eslint-disable-next-line eqeqeq return a != b; } diff --git a/src/operations/logic/truthy.js b/src/operations/logic/truthy.js index 60afc96..a0b460c 100644 --- a/src/operations/logic/truthy.js +++ b/src/operations/logic/truthy.js @@ -1,4 +1,4 @@ -import truthy from '../../helpers/truthy' +import truthy from '../../helpers/truthy'; truthy.code = '!!'; diff --git a/src/operations/misc/indexOf.js b/src/operations/misc/indexOf.js index 37ba9f2..c6a213c 100644 --- a/src/operations/misc/indexOf.js +++ b/src/operations/misc/indexOf.js @@ -1,6 +1,6 @@ function indexOf(a, b) { - if(!b || typeof b.indexOf === "undefined") return false; - return (b.indexOf(a) !== -1); + if (!b || typeof b.indexOf === 'undefined') return false; + return b.indexOf(a) !== -1; } indexOf.code = 'in'; diff --git a/src/operations/misc/log.js b/src/operations/misc/log.js index b15c053..dd1f63d 100644 --- a/src/operations/misc/log.js +++ b/src/operations/misc/log.js @@ -1,4 +1,5 @@ function log(a) { + // eslint-disable-next-line no-console console.log(a); return a; diff --git a/src/operations/misc/method.js b/src/operations/misc/method.js index fe66035..829eff0 100644 --- a/src/operations/misc/method.js +++ b/src/operations/misc/method.js @@ -1,5 +1,6 @@ -function method(obj, method, args) { - return obj[method].apply(obj, args); +function method(obj, methodName, args) { + // eslint-disable-next-line prefer-spread + return obj[methodName].apply(obj, args); } export default method; diff --git a/src/operations/numeric/lower.js b/src/operations/numeric/lower.js index 8d31a7d..cfeb734 100644 --- a/src/operations/numeric/lower.js +++ b/src/operations/numeric/lower.js @@ -1,5 +1,5 @@ function lower(a, b, c) { - return (c === undefined) ? a < b : (a < b) && (b < c); + return c === undefined ? a < b : a < b && b < c; } lower.code = '<'; diff --git a/src/operations/numeric/lowerEqual.js b/src/operations/numeric/lowerEqual.js index a49bf31..c952f4a 100644 --- a/src/operations/numeric/lowerEqual.js +++ b/src/operations/numeric/lowerEqual.js @@ -1,5 +1,5 @@ function lowerEqual(a, b, c) { - return (c === undefined) ? a <= b : (a <= b) && (b <= c); + return c === undefined ? a <= b : a <= b && b <= c; } lowerEqual.code = '<='; diff --git a/src/operations/numeric/max.js b/src/operations/numeric/max.js index 35ebc19..ca28c3f 100644 --- a/src/operations/numeric/max.js +++ b/src/operations/numeric/max.js @@ -1,5 +1,5 @@ -function max() { - return Math.max.apply(this, arguments); +function max(...args) { + return Math.max(...args); } export default max; diff --git a/src/operations/numeric/min.js b/src/operations/numeric/min.js index 3885aa0..e061030 100644 --- a/src/operations/numeric/min.js +++ b/src/operations/numeric/min.js @@ -1,5 +1,5 @@ -function min() { - return Math.min.apply(this, arguments); +function min(...args) { + return Math.min(...args); } export default min; diff --git a/src/operations/string/cat.js b/src/operations/string/cat.js index a630a8f..53feb26 100644 --- a/src/operations/string/cat.js +++ b/src/operations/string/cat.js @@ -1,5 +1,5 @@ -function cat() { - return Array.prototype.join.call(arguments, ""); +function cat(...args) { + return args.join(''); } export default cat; diff --git a/src/operations/string/substr.js b/src/operations/string/substr.js index 24da4bf..55ad81d 100644 --- a/src/operations/string/substr.js +++ b/src/operations/string/substr.js @@ -5,6 +5,6 @@ function substr(source, start, end) { return temp.substr(0, temp.length + end); } return String(source).substr(start, end); -}; +} export default substr; diff --git a/src/visitors/array/all.js b/src/visitors/array/all.js index c0ce1ec..6f37a3c 100644 --- a/src/visitors/array/all.js +++ b/src/visitors/array/all.js @@ -1,14 +1,14 @@ -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function all(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; // All of an empty set is false. Note, some and none have correct fallback after the for loop - if( ! scopedData.length) { + if (!scopedData.length) { return false; } - for(let i=0; i < scopedData.length; i+=1) { - if( ! truthy( apply(scopedLogic, scopedData[i]) )) { + for (let i = 0; i < scopedData.length; i += 1) { + if (!truthy(apply(scopedLogic, scopedData[i]))) { return false; // First falsy, short circuit } } diff --git a/src/visitors/array/filter.js b/src/visitors/array/filter.js index c85fe43..0a82394 100644 --- a/src/visitors/array/filter.js +++ b/src/visitors/array/filter.js @@ -1,19 +1,17 @@ import isArray from '../../helpers/isArray'; -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function filter(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; - if ( ! isArray(scopedData)) { + if (!isArray(scopedData)) { return []; } // Return only the elements from the array in the first argument, // that return truthy when passed to the logic in the second argument. // For parity with JavaScript, reindex the returned array - return scopedData.filter(function(datum){ - return truthy( apply(scopedLogic, datum)); - }); + return scopedData.filter(datum => truthy(apply(scopedLogic, datum))); } -export default filter +export default filter; diff --git a/src/visitors/array/index.js b/src/visitors/array/index.js index 8b11ac8..c49c6c8 100644 --- a/src/visitors/array/index.js +++ b/src/visitors/array/index.js @@ -1,6 +1,6 @@ -export { default as all } from './all' -export { default as filter } from './filter' -export { default as map } from './map' -export { default as none } from './none' -export { default as reduce } from './reduce' -export { default as some } from './some' +export { default as all } from './all'; +export { default as filter } from './filter'; +export { default as map } from './map'; +export { default as none } from './none'; +export { default as reduce } from './reduce'; +export { default as some } from './some'; diff --git a/src/visitors/array/map.js b/src/visitors/array/map.js index edab4aa..8dbb502 100644 --- a/src/visitors/array/map.js +++ b/src/visitors/array/map.js @@ -4,13 +4,11 @@ function map(apply, data, values) { const scopedData = apply(values[0], data); const scopedLogic = values[1]; - if ( ! isArray(scopedData)) { + if (!isArray(scopedData)) { return []; } - return scopedData.map(function(datum){ - return apply(scopedLogic, datum); - }); + return scopedData.map(datum => apply(scopedLogic, datum)); } export default map; diff --git a/src/visitors/array/none.js b/src/visitors/array/none.js index 35ecea4..d7618d0 100644 --- a/src/visitors/array/none.js +++ b/src/visitors/array/none.js @@ -1,5 +1,5 @@ function none(apply, data, values) { - const filtered = apply({'filter' : values}, data); + const filtered = apply({ filter: values }, data); return filtered.length === 0; } diff --git a/src/visitors/array/reduce.js b/src/visitors/array/reduce.js index 3c61ae6..b9f78df 100644 --- a/src/visitors/array/reduce.js +++ b/src/visitors/array/reduce.js @@ -5,17 +5,12 @@ function reduce(apply, data, values) { const scopedLogic = values[1]; const initial = typeof values[2] !== 'undefined' ? values[2] : null; - if ( ! isArray(scopedData)) { + if (!isArray(scopedData)) { return initial; } return scopedData.reduce( - function(accumulator, current){ - return apply( - scopedLogic, - {'current':current, 'accumulator':accumulator} - ); - }, + (accumulator, current) => apply(scopedLogic, { current, accumulator }), initial ); } diff --git a/src/visitors/array/some.js b/src/visitors/array/some.js index 700110c..85491c2 100644 --- a/src/visitors/array/some.js +++ b/src/visitors/array/some.js @@ -1,5 +1,5 @@ function some(apply, data, values) { - const filtered = apply({'filter' : values}, data); + const filtered = apply({ filter: values }, data); return filtered.length > 0; } diff --git a/src/visitors/logic/and.js b/src/visitors/logic/and.js index 709b40e..0e3b112 100644 --- a/src/visitors/logic/and.js +++ b/src/visitors/logic/and.js @@ -1,11 +1,11 @@ -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function and(apply, data, values) { let current; - for(let i=0; i < values.length; i++) { + for (let i = 0; i < values.length; i++) { current = apply(values[i], data); - if( ! truthy(current)) { + if (!truthy(current)) { return current; } } diff --git a/src/visitors/logic/condition.js b/src/visitors/logic/condition.js index dfc5883..741f7b8 100644 --- a/src/visitors/logic/condition.js +++ b/src/visitors/logic/condition.js @@ -16,13 +16,13 @@ function condition(apply, data, values) { given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) given 0 parameters, return NULL (not great practice, but there was no Else) */ - for(i = 0; i < values.length - 1; i += 2) { - if( truthy( apply(values[i], data) ) ) { - return apply(values[i+1], data); + for (i = 0; i < values.length - 1; i += 2) { + if (truthy(apply(values[i], data))) { + return apply(values[i + 1], data); } } - if(values.length === i+1) { + if (values.length === i + 1) { return apply(values[i], data); } diff --git a/src/visitors/logic/index.js b/src/visitors/logic/index.js index eeeff26..f8e0f76 100644 --- a/src/visitors/logic/index.js +++ b/src/visitors/logic/index.js @@ -1,3 +1,3 @@ -export { default as and } from './and' -export { default as condition } from './condition' -export { default as or } from './or' +export { default as and } from './and'; +export { default as condition } from './condition'; +export { default as or } from './or'; diff --git a/src/visitors/logic/or.js b/src/visitors/logic/or.js index 9b633ab..d4bcce1 100644 --- a/src/visitors/logic/or.js +++ b/src/visitors/logic/or.js @@ -1,11 +1,11 @@ -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function or(apply, data, values) { let current; - for(let i=0; i < values.length; i++) { + for (let i = 0; i < values.length; i++) { current = apply(values[i], data); - if( truthy(current) ) { + if (truthy(current)) { return current; } } diff --git a/tests/tests.js b/tests/tests.js index 15afe2d..313d924 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,45 +1,48 @@ -var http = require("http"); -var fs = require("fs"); -var jsonLogic = require("../dist/jsonLogic.js"); - -var download = function(url, dest, cb) { - var file = fs.createWriteStream(dest); - http.get(url, function(response) { - response.pipe(file); - file.on("finish", function() { - file.close(cb); // close() is async, call cb after close completes. +/* global QUnit */ + +const http = require('http'); +const fs = require('fs'); +const jsonLogic = require('../dist/jsonLogic.js'); + +const download = (url, dest, cb) => { + const file = fs.createWriteStream(dest); + http + .get(url, response => { + response.pipe(file); + file.on('finish', () => { + file.close(cb); // close() is async, call cb after close completes. + }); + }) + .on('error', err => { + // Handle errors + fs.unlink(dest); // Delete the file async. (But we don't check the result) + if (cb) cb(err.message); }); - }).on("error", function(err) { // Handle errors - fs.unlink(dest); // Delete the file async. (But we don't check the result) - if (cb) cb(err.message); - }); }; -var remote_or_cache = function (remote_url, local_file, description, runner) { - QUnit.test('Load and run remote tests form: ' + remote_url, function(assert) { +const remote_or_cache = (remote_url, local_file, description, runner) => { + QUnit.test(`Load and run remote tests form: ${remote_url}`, assert => { assert.expect(0); // Only waiting on the request() is async - var done = assert.async(); + const done = assert.async(); - var parse_and_iterate = function (local_file, description, runner) { - fs.readFile(local_file, "utf8", function (error, body) { - var tests; + const parse_and_iterate = () => { + fs.readFile(local_file, 'utf8', (error, body) => { + let tests; try { tests = JSON.parse(body); } catch (e) { - throw new Error("Trouble parsing " + description + ": " + e.message); + throw new Error(`Trouble parsing ${description}: ${e.message}`); } // Remove comments - tests = tests.filter(function (test) { - return typeof test !== 'string'; - }); + tests = tests.filter(test => typeof test !== 'string'); - console.log("Including " + tests.length + " " + description); + console.log(`Including ${tests.length} ${description}`); - QUnit.test(description, function (assert) { - tests.map(function(test) { - runner(test, assert); + QUnit.test(description, assertInner => { + tests.forEach(test => { + runner(test, assertInner); }); }); @@ -47,272 +50,264 @@ var remote_or_cache = function (remote_url, local_file, description, runner) { }); }; - fs.stat(local_file, function (err, stats) { + fs.stat(local_file, err => { if (err) { - console.log("Downloading " + description + " from JsonLogic.com"); - download(remote_url, local_file, function () { - parse_and_iterate(local_file, description, runner); + console.log(`Downloading ${description} from JsonLogic.com`); + download(remote_url, local_file, () => { + parse_and_iterate(); }); } else { - console.log("Using cached " + description); - parse_and_iterate(local_file, description, runner); + console.log(`Using cached ${description}`); + parse_and_iterate(); } }); }); }; remote_or_cache( - "http://jsonlogic.com/tests.json", - "tests/tests.json", - "applies() tests", - function(test, assert){ - var rule = test[0]; - var data = test[1]; - var expected = test[2]; + 'http://jsonlogic.com/tests.json', + 'tests/tests.json', + 'applies() tests', + (test, assert) => { + const rule = test[0]; + const data = test[1]; + const expected = test[2]; assert.deepEqual( jsonLogic.apply(rule, data), expected, - "jsonLogic.apply("+ JSON.stringify(rule) +"," + - JSON.stringify(data) +") === " + - JSON.stringify(expected) + `jsonLogic.apply(${JSON.stringify(rule)},${JSON.stringify( + data + )}) === ${JSON.stringify(expected)}` ); } ); remote_or_cache( - "http://jsonlogic.com/rule_like.json", - "tests/rule_like.json", - "rule_like() tests", - function(test, assert){ - var rule = test[0]; - var pattern = test[1]; - var expected = test[2]; + 'http://jsonlogic.com/rule_like.json', + 'tests/rule_like.json', + 'rule_like() tests', + (test, assert) => { + const rule = test[0]; + const pattern = test[1]; + const expected = test[2]; assert.deepEqual( jsonLogic.rule_like(rule, pattern), expected, - "jsonLogic.rule_like("+ JSON.stringify(rule) +"," + - JSON.stringify(pattern) +") === " + - JSON.stringify(expected) + `jsonLogic.rule_like(${JSON.stringify(rule)},${JSON.stringify( + pattern + )}) === ${JSON.stringify(expected)}` ); } ); -QUnit.test( "Bad operator", function( assert ) { - assert.throws( - function() { - jsonLogic.apply({"fubar": []}); - }, - /Unrecognized operation/ - ); +QUnit.test('Bad operator', assert => { + assert.throws(() => { + jsonLogic.apply({ fubar: [] }); + }, /Unrecognized operation/); }); -QUnit.test( "logging", function( assert ) { - var last_console; - var log = console.log; +QUnit.test('logging', assert => { + let last_console; + // eslint-disable-next-line func-names console.log = function(logged) { last_console = logged; }; - assert.equal( jsonLogic.apply({"log": [1]}), 1 ); - assert.equal( last_console, 1 ); + assert.equal(jsonLogic.apply({ log: [1] }), 1); + assert.equal(last_console, 1); delete console.log; }); -QUnit.test( "edge cases", function( assert ) { - assert.equal( jsonLogic.apply(), undefined, "Called with no arguments" ); +QUnit.test('edge cases', assert => { + assert.equal(jsonLogic.apply(), undefined, 'Called with no arguments'); }); -QUnit.test( "Expanding functionality with add_operator", function( assert) { +QUnit.test('Expanding functionality with add_operator', assert => { // Operator is not yet defined - assert.throws( - function() { - jsonLogic.apply({"add_to_a": []}); - }, - /Unrecognized operation/ - ); + assert.throws(() => { + jsonLogic.apply({ add_to_a: [] }); + }, /Unrecognized operation/); // Set up some outside data, and build a basic function operator - var a = 0; - var add_to_a = function(b) { - if(b === undefined) { - b=1; - } return a += b; + let aOutside = 0; + const add_to_a = b => { + aOutside += b !== undefined ? b : 1; + + return aOutside; }; - jsonLogic.add_operation("add_to_a", add_to_a); + jsonLogic.add_operation('add_to_a', add_to_a); // New operation executes, returns desired result // No args - assert.equal( jsonLogic.apply({"add_to_a": []}), 1 ); + assert.equal(jsonLogic.apply({ add_to_a: [] }), 1); // Unary syntactic sugar - assert.equal( jsonLogic.apply({"add_to_a": 41}), 42 ); + assert.equal(jsonLogic.apply({ add_to_a: 41 }), 42); // New operation had side effects. - assert.equal(a, 42); + assert.equal(aOutside, 42); - var fives = { - add: function(i) { + const fives = { + add(i) { return i + 5; }, - subtract: function(i) { + subtract(i) { return i - 5; }, }; - jsonLogic.add_operation("fives", fives); - assert.equal( jsonLogic.apply({"fives.add": 37}), 42 ); - assert.equal( jsonLogic.apply({"fives.subtract": [47]}), 42 ); + jsonLogic.add_operation('fives', fives); + assert.equal(jsonLogic.apply({ 'fives.add': 37 }), 42); + assert.equal(jsonLogic.apply({ 'fives.subtract': [47] }), 42); // Calling a method with multiple var as arguments. - jsonLogic.add_operation("times", function(a, b) { - return a*b; - }); + jsonLogic.add_operation('times', (a, b) => a * b); assert.equal( - jsonLogic.apply( - {"times": [{"var": "a"}, {"var": "b"}]}, - {a: 6, b: 7} - ), + jsonLogic.apply({ times: [{ var: 'a' }, { var: 'b' }] }, { a: 6, b: 7 }), 42 ); // Remove operation: - jsonLogic.rm_operation("times"); + jsonLogic.rm_operation('times'); - assert.throws( - function() { - jsonLogic.apply({"times": [2,2]}); - }, - /Unrecognized operation/ - ); + assert.throws(() => { + jsonLogic.apply({ times: [2, 2] }); + }, /Unrecognized operation/); // Calling a method that takes an array, but the inside of the array has rules, too - jsonLogic.add_operation("array_times", function(a) { - return a[0]*a[1]; + jsonLogic.add_operation('array_times', a => { + return a[0] * a[1]; }); assert.equal( jsonLogic.apply( - {"array_times": [[{"var": "a"}, {"var": "b"}]]}, - {a: 6, b: 7} + { array_times: [[{ var: 'a' }, { var: 'b' }]] }, + { a: 6, b: 7 } ), 42 ); }); -QUnit.test( "Expanding functionality with method", function( assert) { +QUnit.test('Expanding functionality with method', assert => { // Data contains a real object with methods and local state - var a = { + const a = { count: 0, - increment: function() { - return this.count += 1; + increment() { + this.count += 1; + + return this.count; }, - add: function(b) { - return this.count += b; + add(b) { + this.count += b; + + return this.count; }, }; // Look up "a" in data, and run the increment method on it with no args. assert.equal( - jsonLogic.apply( - {"method": [{"var": "a"}, "increment"]}, - {"a": a} - ), + jsonLogic.apply({ method: [{ var: 'a' }, 'increment'] }, { a }), 1 // Happy return value ); assert.equal(a.count, 1); // Happy state change // Run the add method with an argument assert.equal( - jsonLogic.apply( - {"method": [{"var": "a"}, "add", [41]]}, - {"a": a} - ), + jsonLogic.apply({ method: [{ var: 'a' }, 'add', [41]] }, { a }), 42 // Happy return value ); assert.equal(a.count, 42); // Happy state change }); - -QUnit.test( "Control structures don't eval depth-first", function(assert) { +QUnit.test("Control structures don't eval depth-first", assert => { // Depth-first recursion was wasteful but not harmful until we added custom operations that could have side-effects. // If operations run the condition, if truthy, it runs and returns that consequent. // Consequents of falsy conditions should not run. // After one truthy condition, no other condition should run - var conditions = []; - var consequents = []; - jsonLogic.add_operation("push.if", function(v) { - conditions.push(v); return v; + let conditions = []; + let consequents = []; + jsonLogic.add_operation('push.if', v => { + conditions.push(v); + return v; }); - jsonLogic.add_operation("push.then", function(v) { - consequents.push(v); return v; + jsonLogic.add_operation('push.then', v => { + consequents.push(v); + return v; }); - jsonLogic.add_operation("push.else", function(v) { - consequents.push(v); return v; + jsonLogic.add_operation('push.else', v => { + consequents.push(v); + return v; }); - jsonLogic.apply({"if": [ - {"push.if": [true]}, - {"push.then": ["first"]}, - {"push.if": [false]}, - {"push.then": ["second"]}, - {"push.else": ["third"]}, - ]}); + jsonLogic.apply({ + if: [ + { 'push.if': [true] }, + { 'push.then': ['first'] }, + { 'push.if': [false] }, + { 'push.then': ['second'] }, + { 'push.else': ['third'] }, + ], + }); assert.deepEqual(conditions, [true]); - assert.deepEqual(consequents, ["first"]); + assert.deepEqual(consequents, ['first']); conditions = []; consequents = []; - jsonLogic.apply({"if": [ - {"push.if": [false]}, - {"push.then": ["first"]}, - {"push.if": [true]}, - {"push.then": ["second"]}, - {"push.else": ["third"]}, - ]}); + jsonLogic.apply({ + if: [ + { 'push.if': [false] }, + { 'push.then': ['first'] }, + { 'push.if': [true] }, + { 'push.then': ['second'] }, + { 'push.else': ['third'] }, + ], + }); assert.deepEqual(conditions, [false, true]); - assert.deepEqual(consequents, ["second"]); + assert.deepEqual(consequents, ['second']); conditions = []; consequents = []; - jsonLogic.apply({"if": [ - {"push.if": [false]}, - {"push.then": ["first"]}, - {"push.if": [false]}, - {"push.then": ["second"]}, - {"push.else": ["third"]}, - ]}); + jsonLogic.apply({ + if: [ + { 'push.if': [false] }, + { 'push.then': ['first'] }, + { 'push.if': [false] }, + { 'push.then': ['second'] }, + { 'push.else': ['third'] }, + ], + }); assert.deepEqual(conditions, [false, false]); - assert.deepEqual(consequents, ["third"]); + assert.deepEqual(consequents, ['third']); + let i = []; - jsonLogic.add_operation("push", function(arg) { - i.push(arg); return arg; + jsonLogic.add_operation('push', arg => { + i.push(arg); + return arg; }); - var i = []; i = []; - jsonLogic.apply({"and": [{"push": [false]}, {"push": [false]}]}); + jsonLogic.apply({ and: [{ push: [false] }, { push: [false] }] }); assert.deepEqual(i, [false]); i = []; - jsonLogic.apply({"and": [{"push": [false]}, {"push": [true]}]}); + jsonLogic.apply({ and: [{ push: [false] }, { push: [true] }] }); assert.deepEqual(i, [false]); i = []; - jsonLogic.apply({"and": [{"push": [true]}, {"push": [false]}]}); + jsonLogic.apply({ and: [{ push: [true] }, { push: [false] }] }); assert.deepEqual(i, [true, false]); i = []; - jsonLogic.apply({"and": [{"push": [true]}, {"push": [true]}]}); + jsonLogic.apply({ and: [{ push: [true] }, { push: [true] }] }); assert.deepEqual(i, [true, true]); - i = []; - jsonLogic.apply({"or": [{"push": [false]}, {"push": [false]}]}); + jsonLogic.apply({ or: [{ push: [false] }, { push: [false] }] }); assert.deepEqual(i, [false, false]); i = []; - jsonLogic.apply({"or": [{"push": [false]}, {"push": [true]}]}); + jsonLogic.apply({ or: [{ push: [false] }, { push: [true] }] }); assert.deepEqual(i, [false, true]); i = []; - jsonLogic.apply({"or": [{"push": [true]}, {"push": [false]}]}); + jsonLogic.apply({ or: [{ push: [true] }, { push: [false] }] }); assert.deepEqual(i, [true]); i = []; - jsonLogic.apply({"or": [{"push": [true]}, {"push": [true]}]}); + jsonLogic.apply({ or: [{ push: [true] }, { push: [true] }] }); assert.deepEqual(i, [true]); }); From 108624c21250b8a0f213e2f1ae37b20370323f68 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 17:32:09 +0100 Subject: [PATCH 28/33] chore: added commit hook for eslint/prettier --- package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package.json b/package.json index 3071da8..e108829 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "eslint-plugin-jsx-a11y": "^6.2.1", "eslint-plugin-prettier": "^3.0.1", "eslint-plugin-react": "^7.12.4", + "husky": "^1.3.1", + "lint-staged": "^8.1.4", "prettier": "^1.16.4", "qunit": "^2.9.2", "request": "^2.65.0", @@ -36,6 +38,7 @@ "rollup-plugin-uglify": "^6.0.2" }, "scripts": { + "precommit": "lint-staged", "lint": "eslint src tests", "lint-fix": "npm run lint -- --fix", "test": "cross-env NODE_ENV=test qunit 'tests/**/*.js' -r tap", @@ -47,6 +50,9 @@ "build-package": "rollup --config", "prebuild-package": "rimraf dist" }, + "lint-staged": { + "src/**/*.{js,jsx,json,css,md}": ["npm run lint-fix", "git add"] + }, "repository": { "type": "git", "url": "git+https://github.com/axa-ch/json-logic-js.git" From acc6858575bbb5d8774ebc5e491095059aa10265 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 17:34:26 +0100 Subject: [PATCH 29/33] chore: rebuild --- dist/jsonLogic.js | 2 ++ dist/jsonLogic.js.map | 2 +- dist/jsonLogic.min.js.map | 2 +- lib/createJsonLogic.js | 17 ++++++++------- lib/helpers/isLogic.js | 2 +- lib/helpers/ruleLike.js | 18 ++++++++-------- lib/helpers/usesData.js | 15 +++++++++---- lib/index.js | 12 +++++------ lib/operations/accessor/missing.js | 12 +++++------ lib/operations/accessor/missingSome.js | 8 +++---- lib/operations/accessor/variable.js | 4 ++-- lib/operations/arithmetic/substract.js | 4 ++-- lib/operations/array/index.js | 1 + lib/operations/logic/equal.js | 1 + lib/operations/logic/notEqual.js | 1 + lib/operations/misc/indexOf.js | 2 +- lib/operations/misc/log.js | 1 + lib/operations/misc/method.js | 5 +++-- lib/operations/numeric/max.js | 2 +- lib/operations/numeric/min.js | 2 +- lib/operations/string/cat.js | 6 +++++- lib/operations/string/substr.js | 1 - lib/visitors/accessor/missing.js | 29 -------------------------- lib/visitors/accessor/missingSome.js | 17 --------------- lib/visitors/array/all.js | 2 +- lib/visitors/array/filter.js | 2 +- lib/visitors/array/none.js | 2 +- lib/visitors/array/reduce.js | 4 ++-- lib/visitors/array/some.js | 2 +- lib/visitors/logic/and.js | 2 +- lib/visitors/logic/or.js | 2 +- 31 files changed, 76 insertions(+), 106 deletions(-) delete mode 100644 lib/visitors/accessor/missing.js delete mode 100644 lib/visitors/accessor/missingSome.js diff --git a/dist/jsonLogic.js b/dist/jsonLogic.js index 56ef4e1..a817f40 100644 --- a/dist/jsonLogic.js +++ b/dist/jsonLogic.js @@ -361,11 +361,13 @@ indexOf.code = 'in'; function log(a) { + // eslint-disable-next-line no-console console.log(a); return a; } function method(obj, methodName, args) { + // eslint-disable-next-line prefer-spread return obj[methodName].apply(obj, args); } diff --git a/dist/jsonLogic.js.map b/dist/jsonLogic.js.map index 127335b..24420b5 100644 --- a/dist/jsonLogic.js.map +++ b/dist/jsonLogic.js.map @@ -1 +1 @@ -{"version":3,"file":"jsonLogic.js","sources":["../src/helpers/isArray.js","../src/helpers/isLogic.js","../src/helpers/getOperator.js","../src/createJsonLogic.js","../src/operations/accessor/variable.js","../src/operations/accessor/missing.js","../src/operations/accessor/missingSome.js","../src/operations/arithmetic/add.js","../src/operations/arithmetic/divide.js","../src/operations/arithmetic/modulo.js","../src/operations/arithmetic/multiply.js","../src/operations/arithmetic/substract.js","../src/operations/array/merge.js","../src/operations/array/index.js","../src/operations/logic/equal.js","../src/helpers/truthy.js","../src/operations/logic/falsy.js","../src/operations/logic/notEqual.js","../src/operations/logic/strictEqual.js","../src/operations/logic/strictNotEqual.js","../src/operations/logic/truthy.js","../src/operations/misc/indexOf.js","../src/operations/misc/log.js","../src/operations/misc/method.js","../src/operations/numeric/greater.js","../src/operations/numeric/greaterEqual.js","../src/operations/numeric/lower.js","../src/operations/numeric/lowerEqual.js","../src/operations/numeric/max.js","../src/operations/numeric/min.js","../src/operations/string/cat.js","../src/operations/string/substr.js","../src/visitors/array/all.js","../src/visitors/array/filter.js","../src/visitors/array/map.js","../src/visitors/array/none.js","../src/visitors/array/reduce.js","../src/visitors/array/some.js","../src/visitors/logic/and.js","../src/visitors/logic/condition.js","../src/visitors/logic/or.js","../src/helpers/getValues.js","../src/helpers/arrayUnique.js","../src/helpers/usesData.js","../src/helpers/ruleLike.js","../src/index.js"],"sourcesContent":["export default Array.isArray;\n","import isArray from './isArray';\n\nfunction isLogic(logic) {\n return (\n typeof logic === 'object' && // An object\n logic !== null && // but not null\n !isArray(logic) && // and not an array\n Object.keys(logic).length === 1 // with exactly one key\n );\n}\n\nexport default isLogic;\n","function getOperator(logic) {\n return Object.keys(logic)[0];\n}\n\nexport default getOperator;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(function(name) {\n const operation = _operations[name];\n\n addOperation(operation.code || name, operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(function(name) {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach(key => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","function variable(a, b) {\n const not_found = b === undefined ? null : b;\n let data = this;\n\n if (typeof a === 'undefined' || a === '' || a === null) {\n return data;\n }\n\n const sub_props = String(a).split('.');\n\n for (let i = 0; i < sub_props.length; i++) {\n if (data === null) {\n return not_found;\n }\n // Descending into data\n data = data[sub_props[i]];\n if (data === undefined) {\n return not_found;\n }\n }\n\n return data;\n}\n\nvariable.code = 'var';\n\nexport default variable;\n","import isArray from '../../helpers/isArray';\n\nfunction missing(apply, ...args) {\n /*\n Missing can receive many keys as many arguments, like {\"missing:[1,2]}\n Missing can also receive *one* argument that is an array of keys,\n which typically happens if it's actually acting on the output of another command\n (like 'if' or 'merge')\n */\n\n const are_missing = [];\n const keys = isArray(args[0]) ? args[0] : args;\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const value = apply({ var: key }, this);\n if (value === null || value === '') {\n are_missing.push(key);\n }\n }\n\n return are_missing;\n}\n\nmissing.withApply = true;\n\nexport default missing;\n","function missingSome(apply, need_count, options) {\n // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.\n const are_missing = apply({ missing: options }, this);\n\n if (options.length - are_missing.length >= need_count) {\n return [];\n }\n return are_missing;\n}\n\nmissingSome.code = 'missing_some';\nmissingSome.withApply = true;\n\nexport default missingSome;\n","function add(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) + parseFloat(b, 10);\n }, 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function divide(a, b) {\n return a / b;\n}\n\ndivide.code = '/';\n\nexport default divide;\n","function modulo(a, b) {\n return a % b;\n}\n\nmodulo.code = '%';\n\nexport default modulo;\n","function multiply(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) * parseFloat(b, 10);\n }, 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function substract(a, b) {\n if (b === undefined) {\n return -a;\n }\n return a - b;\n}\n\nsubstract.code = '-';\n\nexport default substract;\n","function merge(...args) {\n return args.reduce(function(a, b) {\n return a.concat(b);\n }, []);\n}\n\nexport default merge;\n","// eslint-disable-next-line import/prefer-default-export\nexport { default as merge } from './merge';\n","function equal(a, b) {\n // eslint-disable-next-line eqeqeq\n return a == b;\n}\n\nequal.code = '==';\n\nexport default equal;\n","import isArray from './isArray';\n\n/*\n This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.\n\n Spec and rationale here: http://jsonlogic.com/truthy\n */\nfunction truthy(value) {\n if (isArray(value) && value.length === 0) {\n return false;\n }\n\n return !!value;\n}\n\nexport default truthy;\n","import truthy from '../../helpers/truthy';\n\nfunction falsy(a) {\n return !truthy(a);\n}\n\nfalsy.code = '!';\n\nexport default falsy;\n","function notEqual(a, b) {\n // eslint-disable-next-line eqeqeq\n return a != b;\n}\n\nnotEqual.code = '!=';\n\nexport default notEqual;\n","function strictEqual(a, b) {\n return a === b;\n}\n\nstrictEqual.code = '===';\n\nexport default strictEqual;\n","function strictNotEqual(a, b) {\n return a !== b;\n}\n\nstrictNotEqual.code = '!==';\n\nexport default strictNotEqual;\n","import truthy from '../../helpers/truthy';\n\ntruthy.code = '!!';\n\nexport default truthy;\n","function indexOf(a, b) {\n if (!b || typeof b.indexOf === 'undefined') return false;\n return b.indexOf(a) !== -1;\n}\n\nindexOf.code = 'in';\n\nexport default indexOf;\n","function log(a) {\n console.log(a);\n\n return a;\n}\n\nexport default log;\n","function method(obj, methodName, args) {\n return obj[methodName].apply(obj, args);\n}\n\nexport default method;\n","function greater(a, b) {\n return a > b;\n}\n\ngreater.code = '>';\n\nexport default greater;\n","function greaterEqual(a, b) {\n return a >= b;\n}\n\ngreaterEqual.code = '>=';\n\nexport default greaterEqual;\n","function lower(a, b, c) {\n return c === undefined ? a < b : a < b && b < c;\n}\n\nlower.code = '<';\n\nexport default lower;\n","function lowerEqual(a, b, c) {\n return c === undefined ? a <= b : a <= b && b <= c;\n}\n\nlowerEqual.code = '<=';\n\nexport default lowerEqual;\n","function max(...args) {\n return Math.max(...args);\n}\n\nexport default max;\n","function min(...args) {\n return Math.min(...args);\n}\n\nexport default min;\n","function cat(...args) {\n return args.join('');\n}\n\nexport default cat;\n","function substr(source, start, end) {\n if (end < 0) {\n // JavaScript doesn't support negative end, this emulates PHP behavior\n const temp = String(source).substr(start);\n return temp.substr(0, temp.length + end);\n }\n return String(source).substr(start, end);\n}\n\nexport default substr;\n","import truthy from '../../helpers/truthy';\n\nfunction all(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n // All of an empty set is false. Note, some and none have correct fallback after the for loop\n if (!scopedData.length) {\n return false;\n }\n for (let i = 0; i < scopedData.length; i += 1) {\n if (!truthy(apply(scopedLogic, scopedData[i]))) {\n return false; // First falsy, short circuit\n }\n }\n return true; // All were truthy\n}\n\nexport default all;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum) {\n return truthy(apply(scopedLogic, datum));\n });\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum) {\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","function none(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length === 0;\n}\n\nexport default none;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(function(accumulator, current) {\n return apply(scopedLogic, { current, accumulator });\n }, initial);\n}\n\nexport default reduce;\n","function some(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length > 0;\n}\n\nexport default some;\n","import truthy from '../../helpers/truthy';\n\nfunction and(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (!truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default and;\n","import truthy from '../../helpers/truthy';\n\nfunction condition(apply, data, values) {\n let i;\n\n /* 'if' should be called with a odd number of parameters, 3 or greater\n This works on the pattern:\n if( 0 ){ 1 }else{ 2 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };\n\n The implementation is:\n For pairs of values (0,1 then 2,3 then 4,5 etc)\n If the first evaluates truthy, evaluate and return the second\n If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)\n given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)\n given 0 parameters, return NULL (not great practice, but there was no Else)\n */\n for (i = 0; i < values.length - 1; i += 2) {\n if (truthy(apply(values[i], data))) {\n return apply(values[i + 1], data);\n }\n }\n\n if (values.length === i + 1) {\n return apply(values[i], data);\n }\n\n return null;\n}\n\ncondition.code = ['if', '?:'];\n\nexport default condition;\n","import truthy from '../../helpers/truthy';\n\nfunction or(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default or;\n","import getOperator from './getOperator';\n\nfunction getValues(logic) {\n return logic[getOperator(logic)];\n}\n\nexport default getValues;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(function(val) {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport getValues from './getValues';\n\nfunction ruleLike(rule, pattern) {\n // console.log(\"Is \". JSON.stringify(rule) . \" like \" . JSON.stringify(pattern) . \"?\");\n if (pattern === rule) {\n return true;\n } // TODO : Deep object equivalency?\n if (pattern === '@') {\n return true;\n } // Wildcard!\n if (pattern === 'number') {\n return typeof rule === 'number';\n }\n if (pattern === 'string') {\n return typeof rule === 'string';\n }\n if (pattern === 'array') {\n // !logic test might be superfluous in JavaScript\n return isArray(rule) && !isLogic(rule);\n }\n\n if (isLogic(pattern)) {\n if (isLogic(rule)) {\n const pattern_op = getOperator(pattern);\n const rule_op = getOperator(rule);\n\n if (pattern_op === '@' || pattern_op === rule_op) {\n // echo \"\\nOperators match, go deeper\\n\";\n return ruleLike(getValues(rule, false), getValues(pattern, false));\n }\n }\n return false; // pattern is logic, rule isn't, can't be eq\n }\n\n if (isArray(pattern)) {\n if (isArray(rule)) {\n if (pattern.length !== rule.length) {\n return false;\n }\n /*\n Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)\n */\n for (let i = 0; i < pattern.length; i += 1) {\n // If any fail, we fail\n if (!ruleLike(rule[i], pattern[i])) {\n return false;\n }\n }\n return true; // If they *all* passed, we pass\n }\n return false; // Pattern is array, rule isn't\n }\n\n // Not logic, not array, not a === match for rule.\n return false;\n}\n\nexport default ruleLike;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n"],"names":["Array","isArray","isLogic","logic","Object","keys","length","getOperator","createJsonLogic","_operations","_visitors","operations","visitors","forEach","name","operation","addOperation","code","visitor","addVisitor","removeOperation","key","removeVisitor","apply","data","map","l","op","values","i","val","operator","withApply","unshift","indexOf","sub_ops","String","split","undefined","Error","slice","join","add_operation","rm_operation","add_visitor","rm_visitor","variable","a","b","not_found","sub_props","missing","are_missing","args","value","var","push","missingSome","need_count","options","add","reduce","parseFloat","divide","modulo","multiply","substract","merge","concat","equal","truthy","falsy","notEqual","strictEqual","strictNotEqual","log","console","method","obj","methodName","greater","greaterEqual","lower","c","lowerEqual","max","Math","min","cat","substr","source","start","end","temp","all","scopedData","scopedLogic","filter","datum","none","filtered","initial","accumulator","current","some","and","condition","or","getValues","arrayUnique","array","usesData","collection","ruleLike","rule","pattern","pattern_op","rule_op","jsonLogic","is_logic","get_operator","get_values","uses_data","rule_like"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"jsonLogic.js","sources":["../src/helpers/isArray.js","../src/helpers/isLogic.js","../src/helpers/getOperator.js","../src/createJsonLogic.js","../src/operations/accessor/variable.js","../src/operations/accessor/missing.js","../src/operations/accessor/missingSome.js","../src/operations/arithmetic/add.js","../src/operations/arithmetic/divide.js","../src/operations/arithmetic/modulo.js","../src/operations/arithmetic/multiply.js","../src/operations/arithmetic/substract.js","../src/operations/array/merge.js","../src/operations/array/index.js","../src/operations/logic/equal.js","../src/helpers/truthy.js","../src/operations/logic/falsy.js","../src/operations/logic/notEqual.js","../src/operations/logic/strictEqual.js","../src/operations/logic/strictNotEqual.js","../src/operations/logic/truthy.js","../src/operations/misc/indexOf.js","../src/operations/misc/log.js","../src/operations/misc/method.js","../src/operations/numeric/greater.js","../src/operations/numeric/greaterEqual.js","../src/operations/numeric/lower.js","../src/operations/numeric/lowerEqual.js","../src/operations/numeric/max.js","../src/operations/numeric/min.js","../src/operations/string/cat.js","../src/operations/string/substr.js","../src/visitors/array/all.js","../src/visitors/array/filter.js","../src/visitors/array/map.js","../src/visitors/array/none.js","../src/visitors/array/reduce.js","../src/visitors/array/some.js","../src/visitors/logic/and.js","../src/visitors/logic/condition.js","../src/visitors/logic/or.js","../src/helpers/getValues.js","../src/helpers/arrayUnique.js","../src/helpers/usesData.js","../src/helpers/ruleLike.js","../src/index.js"],"sourcesContent":["export default Array.isArray;\n","import isArray from './isArray';\n\nfunction isLogic(logic) {\n return (\n typeof logic === 'object' && // An object\n logic !== null && // but not null\n !isArray(logic) && // and not an array\n Object.keys(logic).length === 1 // with exactly one key\n );\n}\n\nexport default isLogic;\n","function getOperator(logic) {\n return Object.keys(logic)[0];\n}\n\nexport default getOperator;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(name => {\n const operation = _operations[name];\n\n addOperation(operation.code || name, operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(name => {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach(key => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(l => apply(l, data));\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(val => apply(val, data));\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","function variable(a, b) {\n const not_found = b === undefined ? null : b;\n let data = this;\n\n if (typeof a === 'undefined' || a === '' || a === null) {\n return data;\n }\n\n const sub_props = String(a).split('.');\n\n for (let i = 0; i < sub_props.length; i++) {\n if (data === null) {\n return not_found;\n }\n // Descending into data\n data = data[sub_props[i]];\n if (data === undefined) {\n return not_found;\n }\n }\n\n return data;\n}\n\nvariable.code = 'var';\n\nexport default variable;\n","import isArray from '../../helpers/isArray';\n\nfunction missing(apply, ...args) {\n /*\n Missing can receive many keys as many arguments, like {\"missing:[1,2]}\n Missing can also receive *one* argument that is an array of keys,\n which typically happens if it's actually acting on the output of another command\n (like 'if' or 'merge')\n */\n\n const are_missing = [];\n const keys = isArray(args[0]) ? args[0] : args;\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const value = apply({ var: key }, this);\n if (value === null || value === '') {\n are_missing.push(key);\n }\n }\n\n return are_missing;\n}\n\nmissing.withApply = true;\n\nexport default missing;\n","function missingSome(apply, need_count, options) {\n // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.\n const are_missing = apply({ missing: options }, this);\n\n if (options.length - are_missing.length >= need_count) {\n return [];\n }\n return are_missing;\n}\n\nmissingSome.code = 'missing_some';\nmissingSome.withApply = true;\n\nexport default missingSome;\n","function add(...args) {\n return args.reduce((a, b) => parseFloat(a, 10) + parseFloat(b, 10), 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function divide(a, b) {\n return a / b;\n}\n\ndivide.code = '/';\n\nexport default divide;\n","function modulo(a, b) {\n return a % b;\n}\n\nmodulo.code = '%';\n\nexport default modulo;\n","function multiply(...args) {\n return args.reduce((a, b) => parseFloat(a, 10) * parseFloat(b, 10), 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function substract(a, b) {\n if (b === undefined) {\n return -a;\n }\n return a - b;\n}\n\nsubstract.code = '-';\n\nexport default substract;\n","function merge(...args) {\n return args.reduce((a, b) => a.concat(b), []);\n}\n\nexport default merge;\n","// eslint-disable-next-line import/prefer-default-export\nexport { default as merge } from './merge';\n","function equal(a, b) {\n // eslint-disable-next-line eqeqeq\n return a == b;\n}\n\nequal.code = '==';\n\nexport default equal;\n","import isArray from './isArray';\n\n/*\n This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.\n\n Spec and rationale here: http://jsonlogic.com/truthy\n */\nfunction truthy(value) {\n if (isArray(value) && value.length === 0) {\n return false;\n }\n\n return !!value;\n}\n\nexport default truthy;\n","import truthy from '../../helpers/truthy';\n\nfunction falsy(a) {\n return !truthy(a);\n}\n\nfalsy.code = '!';\n\nexport default falsy;\n","function notEqual(a, b) {\n // eslint-disable-next-line eqeqeq\n return a != b;\n}\n\nnotEqual.code = '!=';\n\nexport default notEqual;\n","function strictEqual(a, b) {\n return a === b;\n}\n\nstrictEqual.code = '===';\n\nexport default strictEqual;\n","function strictNotEqual(a, b) {\n return a !== b;\n}\n\nstrictNotEqual.code = '!==';\n\nexport default strictNotEqual;\n","import truthy from '../../helpers/truthy';\n\ntruthy.code = '!!';\n\nexport default truthy;\n","function indexOf(a, b) {\n if (!b || typeof b.indexOf === 'undefined') return false;\n return b.indexOf(a) !== -1;\n}\n\nindexOf.code = 'in';\n\nexport default indexOf;\n","function log(a) {\n // eslint-disable-next-line no-console\n console.log(a);\n\n return a;\n}\n\nexport default log;\n","function method(obj, methodName, args) {\n // eslint-disable-next-line prefer-spread\n return obj[methodName].apply(obj, args);\n}\n\nexport default method;\n","function greater(a, b) {\n return a > b;\n}\n\ngreater.code = '>';\n\nexport default greater;\n","function greaterEqual(a, b) {\n return a >= b;\n}\n\ngreaterEqual.code = '>=';\n\nexport default greaterEqual;\n","function lower(a, b, c) {\n return c === undefined ? a < b : a < b && b < c;\n}\n\nlower.code = '<';\n\nexport default lower;\n","function lowerEqual(a, b, c) {\n return c === undefined ? a <= b : a <= b && b <= c;\n}\n\nlowerEqual.code = '<=';\n\nexport default lowerEqual;\n","function max(...args) {\n return Math.max(...args);\n}\n\nexport default max;\n","function min(...args) {\n return Math.min(...args);\n}\n\nexport default min;\n","function cat(...args) {\n return args.join('');\n}\n\nexport default cat;\n","function substr(source, start, end) {\n if (end < 0) {\n // JavaScript doesn't support negative end, this emulates PHP behavior\n const temp = String(source).substr(start);\n return temp.substr(0, temp.length + end);\n }\n return String(source).substr(start, end);\n}\n\nexport default substr;\n","import truthy from '../../helpers/truthy';\n\nfunction all(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n // All of an empty set is false. Note, some and none have correct fallback after the for loop\n if (!scopedData.length) {\n return false;\n }\n for (let i = 0; i < scopedData.length; i += 1) {\n if (!truthy(apply(scopedLogic, scopedData[i]))) {\n return false; // First falsy, short circuit\n }\n }\n return true; // All were truthy\n}\n\nexport default all;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(datum => truthy(apply(scopedLogic, datum)));\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(datum => apply(scopedLogic, datum));\n}\n\nexport default map;\n","function none(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length === 0;\n}\n\nexport default none;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n (accumulator, current) => apply(scopedLogic, { current, accumulator }),\n initial\n );\n}\n\nexport default reduce;\n","function some(apply, data, values) {\n const filtered = apply({ filter: values }, data);\n\n return filtered.length > 0;\n}\n\nexport default some;\n","import truthy from '../../helpers/truthy';\n\nfunction and(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (!truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default and;\n","import truthy from '../../helpers/truthy';\n\nfunction condition(apply, data, values) {\n let i;\n\n /* 'if' should be called with a odd number of parameters, 3 or greater\n This works on the pattern:\n if( 0 ){ 1 }else{ 2 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };\n if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };\n\n The implementation is:\n For pairs of values (0,1 then 2,3 then 4,5 etc)\n If the first evaluates truthy, evaluate and return the second\n If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)\n given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)\n given 0 parameters, return NULL (not great practice, but there was no Else)\n */\n for (i = 0; i < values.length - 1; i += 2) {\n if (truthy(apply(values[i], data))) {\n return apply(values[i + 1], data);\n }\n }\n\n if (values.length === i + 1) {\n return apply(values[i], data);\n }\n\n return null;\n}\n\ncondition.code = ['if', '?:'];\n\nexport default condition;\n","import truthy from '../../helpers/truthy';\n\nfunction or(apply, data, values) {\n let current;\n\n for (let i = 0; i < values.length; i++) {\n current = apply(values[i], data);\n if (truthy(current)) {\n return current;\n }\n }\n return current; // Last\n}\n\nexport default or;\n","import getOperator from './getOperator';\n\nfunction getValues(logic) {\n return logic[getOperator(logic)];\n}\n\nexport default getValues;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(val => {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport getValues from './getValues';\n\nfunction ruleLike(rule, pattern) {\n // console.log(\"Is \". JSON.stringify(rule) . \" like \" . JSON.stringify(pattern) . \"?\");\n if (pattern === rule) {\n return true;\n } // TODO : Deep object equivalency?\n if (pattern === '@') {\n return true;\n } // Wildcard!\n if (pattern === 'number') {\n return typeof rule === 'number';\n }\n if (pattern === 'string') {\n return typeof rule === 'string';\n }\n if (pattern === 'array') {\n // !logic test might be superfluous in JavaScript\n return isArray(rule) && !isLogic(rule);\n }\n\n if (isLogic(pattern)) {\n if (isLogic(rule)) {\n const pattern_op = getOperator(pattern);\n const rule_op = getOperator(rule);\n\n if (pattern_op === '@' || pattern_op === rule_op) {\n // echo \"\\nOperators match, go deeper\\n\";\n return ruleLike(getValues(rule, false), getValues(pattern, false));\n }\n }\n return false; // pattern is logic, rule isn't, can't be eq\n }\n\n if (isArray(pattern)) {\n if (isArray(rule)) {\n if (pattern.length !== rule.length) {\n return false;\n }\n /*\n Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)\n */\n for (let i = 0; i < pattern.length; i += 1) {\n // If any fail, we fail\n if (!ruleLike(rule[i], pattern[i])) {\n return false;\n }\n }\n return true; // If they *all* passed, we pass\n }\n return false; // Pattern is array, rule isn't\n }\n\n // Not logic, not array, not a === match for rule.\n return false;\n}\n\nexport default ruleLike;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n"],"names":["Array","isArray","isLogic","logic","Object","keys","length","getOperator","createJsonLogic","_operations","_visitors","operations","visitors","forEach","name","operation","addOperation","code","visitor","addVisitor","removeOperation","key","removeVisitor","apply","data","map","l","op","values","i","val","operator","withApply","unshift","indexOf","sub_ops","String","split","undefined","Error","slice","join","add_operation","rm_operation","add_visitor","rm_visitor","variable","a","b","not_found","sub_props","missing","are_missing","args","value","var","push","missingSome","need_count","options","add","reduce","parseFloat","divide","modulo","multiply","substract","merge","concat","equal","truthy","falsy","notEqual","strictEqual","strictNotEqual","log","console","method","obj","methodName","greater","greaterEqual","lower","c","lowerEqual","max","Math","min","cat","substr","source","start","end","temp","all","scopedData","scopedLogic","filter","datum","none","filtered","initial","accumulator","current","some","and","condition","or","getValues","arrayUnique","array","usesData","collection","ruleLike","rule","pattern","pattern_op","rule_op","jsonLogic","is_logic","get_operator","get_values","uses_data","rule_like"],"mappings":"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"} \ No newline at end of file diff --git a/dist/jsonLogic.min.js.map b/dist/jsonLogic.min.js.map index 7ff122f..f141d7f 100644 --- a/dist/jsonLogic.min.js.map +++ b/dist/jsonLogic.min.js.map @@ -1 +1 @@ -{"version":3,"file":"jsonLogic.min.js","sources":["../src/operations/arithmetic/add.js","../src/operations/arithmetic/multiply.js","../src/operations/array/merge.js","../src/createJsonLogic.js","../src/index.js","../src/visitors/array/filter.js","../src/visitors/array/map.js","../src/visitors/array/reduce.js","../src/helpers/usesData.js","../src/helpers/arrayUnique.js"],"sourcesContent":["function add(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) + parseFloat(b, 10);\n }, 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function multiply(...args) {\n return args.reduce(function(a, b) {\n return parseFloat(a, 10) * parseFloat(b, 10);\n }, 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function merge(...args) {\n return args.reduce(function(a, b) {\n return a.concat(b);\n }, []);\n}\n\nexport default merge;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(function(name) {\n const operation = _operations[name];\n\n addOperation(operation.code || name, operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(function(name) {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach(key => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(function(l) {\n return apply(l, data);\n });\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(function(val) {\n return apply(val, data);\n });\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(function(datum) {\n return truthy(apply(scopedLogic, datum));\n });\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(function(datum) {\n return apply(scopedLogic, datum);\n });\n}\n\nexport default map;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(function(accumulator, current) {\n return apply(scopedLogic, { current, accumulator });\n }, initial);\n}\n\nexport default reduce;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(function(val) {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n"],"names":["createJsonLogic","arrayUnique"],"mappings":"69CACqB,CAAA,uNCAA,CAAA,08BCAA,CAAA,wvBCGrB,CAAA,kFA8BmB,CAAA,qEAzBkB,CAAA,mEAQF,CAAA,mJAoCZ,CAAA,uIAwBC,CAAA,igBCnENA,CAAAA,sNCGS,CAAA,iGCHH,CAAA,sLCCG,CAAA,ydCUN,CAAA,8CChBrB,CAAA,4FDsBSC,CAAAA"} \ No newline at end of file +{"version":3,"file":"jsonLogic.min.js","sources":["../src/operations/arithmetic/add.js","../src/operations/arithmetic/multiply.js","../src/operations/array/merge.js","../src/createJsonLogic.js","../src/index.js","../src/visitors/array/filter.js","../src/visitors/array/map.js","../src/visitors/array/reduce.js","../src/helpers/usesData.js","../src/helpers/arrayUnique.js"],"sourcesContent":["function add(...args) {\n return args.reduce((a, b) => parseFloat(a, 10) + parseFloat(b, 10), 0);\n}\n\nadd.code = '+';\n\nexport default add;\n","function multiply(...args) {\n return args.reduce((a, b) => parseFloat(a, 10) * parseFloat(b, 10), 1);\n}\n\nmultiply.code = '*';\n\nexport default multiply;\n","function merge(...args) {\n return args.reduce((a, b) => a.concat(b), []);\n}\n\nexport default merge;\n","import isArray from './helpers/isArray';\nimport isLogic from './helpers/isLogic';\nimport getOperator from './helpers/getOperator';\n\nfunction createJsonLogic(_operations, _visitors) {\n const operations = {};\n const visitors = {};\n\n if (_operations) {\n Object.keys(_operations).forEach(name => {\n const operation = _operations[name];\n\n addOperation(operation.code || name, operation);\n });\n }\n\n if (_visitors) {\n Object.keys(_visitors).forEach(name => {\n const visitor = _visitors[name];\n\n addVisitor(visitor.code || name, visitor);\n });\n }\n\n function addOperation(name, code) {\n operations[name] = code;\n }\n\n function removeOperation(name) {\n delete operations[name];\n }\n\n function addVisitor(name, code) {\n if (isArray(name)) {\n name.forEach(key => addVisitor(key, code));\n return;\n }\n\n visitors[name] = code;\n }\n\n function removeVisitor(name) {\n if (isArray(name)) {\n name.forEach(removeVisitor);\n return;\n }\n\n delete visitors[name];\n }\n\n function apply(logic, data = {}) {\n // Does this array contain logic? Only one way to find out.\n if (isArray(logic)) {\n return logic.map(l => apply(l, data));\n }\n // You've recursed to a primitive, stop!\n if (!isLogic(logic)) {\n return logic;\n }\n\n const op = getOperator(logic);\n let values = logic[op];\n let i;\n\n // easy syntax for unary operators, like {\"var\" : \"x\"} instead of strict {\"var\" : [\"x\"]}\n if (!isArray(values)) {\n values = [values];\n }\n\n // apply matching visitors first\n if (typeof visitors[op] === 'function') {\n return visitors[op](apply, data, values);\n }\n\n // Everyone else gets immediate depth-first recursion\n values = values.map(val => apply(val, data));\n\n // The operation is called with \"data\" bound to its \"this\" and \"values\" passed as arguments.\n // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments\n const operator = operations[op];\n if (typeof operator === 'function') {\n if (operator.withApply) {\n values.unshift(apply);\n }\n\n return operator.apply(data, values);\n }\n if (op.indexOf('.') > 0) {\n // Contains a dot, and not in the 0th position\n const sub_ops = String(op).split('.');\n let operation = operations;\n for (i = 0; i < sub_ops.length; i++) {\n // Descending into operations\n operation = operation[sub_ops[i]];\n if (operation === undefined) {\n throw new Error(\n `Unrecognized operation ${op} (failed at ${sub_ops\n .slice(0, i + 1)\n .join('.')})`\n );\n }\n }\n\n return operation.apply(data, values);\n }\n\n throw new Error(`Unrecognized operation ${op}`);\n }\n\n return {\n apply,\n add_operation: addOperation,\n rm_operation: removeOperation,\n add_visitor: addVisitor,\n rm_visitor: removeVisitor,\n };\n}\n\nexport default createJsonLogic;\n","import createJsonLogic from './createJsonLogic';\nimport * as operations from './operations';\nimport * as visitors from './visitors';\nimport isLogic from './helpers/isLogic';\nimport truthy from './helpers/truthy';\nimport getOperator from './helpers/getOperator';\nimport getValues from './helpers/getValues';\nimport usesData from './helpers/usesData';\nimport ruleLike from './helpers/ruleLike';\n\nconst jsonLogic = createJsonLogic(operations, visitors);\n\n// restore original public API\njsonLogic.is_logic = isLogic;\njsonLogic.truthy = truthy;\njsonLogic.get_operator = getOperator;\njsonLogic.get_values = getValues;\njsonLogic.uses_data = usesData;\njsonLogic.rule_like = ruleLike;\n\nexport default jsonLogic;\n","import isArray from '../../helpers/isArray';\nimport truthy from '../../helpers/truthy';\n\nfunction filter(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n // Return only the elements from the array in the first argument,\n // that return truthy when passed to the logic in the second argument.\n // For parity with JavaScript, reindex the returned array\n return scopedData.filter(datum => truthy(apply(scopedLogic, datum)));\n}\n\nexport default filter;\n","import isArray from '../../helpers/isArray';\n\nfunction map(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n\n if (!isArray(scopedData)) {\n return [];\n }\n\n return scopedData.map(datum => apply(scopedLogic, datum));\n}\n\nexport default map;\n","import isArray from '../../helpers/isArray';\n\nfunction reduce(apply, data, values) {\n const scopedData = apply(values[0], data);\n const scopedLogic = values[1];\n const initial = typeof values[2] !== 'undefined' ? values[2] : null;\n\n if (!isArray(scopedData)) {\n return initial;\n }\n\n return scopedData.reduce(\n (accumulator, current) => apply(scopedLogic, { current, accumulator }),\n initial\n );\n}\n\nexport default reduce;\n","import isArray from './isArray';\nimport isLogic from './isLogic';\nimport getOperator from './getOperator';\nimport arrayUnique from './arrayUnique';\n\nfunction usesData(logic) {\n const collection = [];\n\n if (isLogic(logic)) {\n const op = getOperator(logic);\n let values = logic[op];\n\n if (!isArray(values)) {\n values = [values];\n }\n\n if (op === 'var') {\n // This doesn't cover the case where the arg to var is itself a rule.\n collection.push(values[0]);\n } else {\n // Recursion!\n values.forEach(val => {\n collection.push(...usesData(val));\n });\n }\n }\n\n return arrayUnique(collection);\n}\n\nexport default usesData;\n","/**\n * Return an array that contains no duplicates (original not modified)\n * @param {array} array Original reference array\n * @return {array} New array with no duplicates\n */\nfunction arrayUnique(array) {\n const a = [];\n for (let i = 0, l = array.length; i < l; i++) {\n if (a.indexOf(array[i]) === -1) {\n a.push(array[i]);\n }\n }\n return a;\n}\n\nexport default arrayUnique;\n"],"names":["createJsonLogic","arrayUnique"],"mappings":"69CACqB,CAAA,uNCAA,CAAA,08BCAA,CAAA,wvBCGrB,CAAA,kFA8BmB,CAAA,qEAzBkB,CAAA,mEAQF,CAAA,mJAoCZ,CAAA,uIAsBC,CAAA,igBCjENA,CAAAA,sNCGS,CAAA,iGCHH,CAAA,sLCEpB,CAAA,ydCSiB,CAAA,8CChBrB,CAAA,4FDsBSC,CAAAA"} \ No newline at end of file diff --git a/lib/createJsonLogic.js b/lib/createJsonLogic.js index c264609..9c44581 100644 --- a/lib/createJsonLogic.js +++ b/lib/createJsonLogic.js @@ -48,7 +48,9 @@ function createJsonLogic(_operations, _visitors) { delete visitors[name]; } - function apply(logic, data) { + function apply(logic) { + var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + // Does this array contain logic? Only one way to find out. if (isArray(logic)) { return logic.map(function (l) { @@ -61,7 +63,6 @@ function createJsonLogic(_operations, _visitors) { return logic; } - data = data || {}; var op = getOperator(logic); var values = logic[op]; var i; // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} @@ -84,15 +85,17 @@ function createJsonLogic(_operations, _visitors) { var operator = operations[op]; - if (typeof operator === "function") { + if (typeof operator === 'function') { if (operator.withApply) { values.unshift(apply); } return operator.apply(data, values); - } else if (op.indexOf(".") > 0) { + } + + if (op.indexOf('.') > 0) { // Contains a dot, and not in the 0th position - var sub_ops = String(op).split("."); + var sub_ops = String(op).split('.'); var operation = operations; for (i = 0; i < sub_ops.length; i++) { @@ -100,14 +103,14 @@ function createJsonLogic(_operations, _visitors) { operation = operation[sub_ops[i]]; if (operation === undefined) { - throw new Error("Unrecognized operation " + op + " (failed at " + sub_ops.slice(0, i + 1).join(".") + ")"); + throw new Error("Unrecognized operation ".concat(op, " (failed at ").concat(sub_ops.slice(0, i + 1).join('.'), ")")); } } return operation.apply(data, values); } - throw new Error("Unrecognized operation " + op); + throw new Error("Unrecognized operation ".concat(op)); } return { diff --git a/lib/helpers/isLogic.js b/lib/helpers/isLogic.js index aafe1c8..e1868f6 100644 --- a/lib/helpers/isLogic.js +++ b/lib/helpers/isLogic.js @@ -3,7 +3,7 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterat import isArray from './isArray'; function isLogic(logic) { - return _typeof(logic) === "object" && // An object + return _typeof(logic) === 'object' && // An object logic !== null && // but not null !isArray(logic) && // and not an array Object.keys(logic).length === 1 // with exactly one key diff --git a/lib/helpers/ruleLike.js b/lib/helpers/ruleLike.js index dbdf86e..2b7e589 100644 --- a/lib/helpers/ruleLike.js +++ b/lib/helpers/ruleLike.js @@ -10,20 +10,20 @@ function ruleLike(rule, pattern) { } // TODO : Deep object equivalency? - if (pattern === "@") { + if (pattern === '@') { return true; } // Wildcard! - if (pattern === "number") { - return typeof rule === "number"; + if (pattern === 'number') { + return typeof rule === 'number'; } - if (pattern === "string") { - return typeof rule === "string"; + if (pattern === 'string') { + return typeof rule === 'string'; } - if (pattern === "array") { + if (pattern === 'array') { // !logic test might be superfluous in JavaScript return isArray(rule) && !isLogic(rule); } @@ -33,7 +33,7 @@ function ruleLike(rule, pattern) { var pattern_op = getOperator(pattern); var rule_op = getOperator(rule); - if (pattern_op === "@" || pattern_op === rule_op) { + if (pattern_op === '@' || pattern_op === rule_op) { // echo "\nOperators match, go deeper\n"; return ruleLike(getValues(rule, false), getValues(pattern, false)); } @@ -60,9 +60,9 @@ function ruleLike(rule, pattern) { } return true; // If they *all* passed, we pass - } else { - return false; // Pattern is array, rule isn't } + + return false; // Pattern is array, rule isn't } // Not logic, not array, not a === match for rule. diff --git a/lib/helpers/usesData.js b/lib/helpers/usesData.js index 1d1c3db..a212e6f 100644 --- a/lib/helpers/usesData.js +++ b/lib/helpers/usesData.js @@ -1,3 +1,11 @@ +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } + +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } + +function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } + +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } + import isArray from './isArray'; import isLogic from './isLogic'; import getOperator from './getOperator'; @@ -14,13 +22,13 @@ function usesData(logic) { values = [values]; } - if (op === "var") { + if (op === 'var') { // This doesn't cover the case where the arg to var is itself a rule. collection.push(values[0]); } else { // Recursion! - values.map(function (val) { - collection.push.apply(collection, usesData(val)); + values.forEach(function (val) { + collection.push.apply(collection, _toConsumableArray(usesData(val))); }); } } @@ -28,5 +36,4 @@ function usesData(logic) { return arrayUnique(collection); } -; export default usesData; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 434d9c9..f6eeca1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,12 +1,12 @@ import createJsonLogic from './createJsonLogic'; import * as operations from './operations'; import * as visitors from './visitors'; -import isLogic from "./helpers/isLogic"; -import truthy from "./helpers/truthy"; -import getOperator from "./helpers/getOperator"; -import getValues from "./helpers/getValues"; -import usesData from "./helpers/usesData"; -import ruleLike from "./helpers/ruleLike"; +import isLogic from './helpers/isLogic'; +import truthy from './helpers/truthy'; +import getOperator from './helpers/getOperator'; +import getValues from './helpers/getValues'; +import usesData from './helpers/usesData'; +import ruleLike from './helpers/ruleLike'; var jsonLogic = createJsonLogic(operations, visitors); // restore original public API jsonLogic.is_logic = isLogic; diff --git a/lib/operations/accessor/missing.js b/lib/operations/accessor/missing.js index 267c7ad..6b9366e 100644 --- a/lib/operations/accessor/missing.js +++ b/lib/operations/accessor/missing.js @@ -1,5 +1,4 @@ import isArray from '../../helpers/isArray'; -import variable from './variable'; function missing(apply) { /* @@ -8,7 +7,7 @@ function missing(apply) { which typically happens if it's actually acting on the output of another command (like 'if' or 'merge') */ - var missing = []; + var are_missing = []; for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; @@ -19,17 +18,16 @@ function missing(apply) { for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = apply({ - "var": key + var: key }, this); - if (value === null || value === "") { - missing.push(key); + if (value === null || value === '') { + are_missing.push(key); } } - return missing; + return are_missing; } -; missing.withApply = true; export default missing; \ No newline at end of file diff --git a/lib/operations/accessor/missingSome.js b/lib/operations/accessor/missingSome.js index ef432dc..391f0c6 100644 --- a/lib/operations/accessor/missingSome.js +++ b/lib/operations/accessor/missingSome.js @@ -1,16 +1,14 @@ -import missing from './missing'; - function missingSome(apply, need_count, options) { // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. var are_missing = apply({ - "missing": options + missing: options }, this); if (options.length - are_missing.length >= need_count) { return []; - } else { - return are_missing; } + + return are_missing; } missingSome.code = 'missing_some'; diff --git a/lib/operations/accessor/variable.js b/lib/operations/accessor/variable.js index 33aa5c3..26adb22 100644 --- a/lib/operations/accessor/variable.js +++ b/lib/operations/accessor/variable.js @@ -2,11 +2,11 @@ function variable(a, b) { var not_found = b === undefined ? null : b; var data = this; - if (typeof a === "undefined" || a === "" || a === null) { + if (typeof a === 'undefined' || a === '' || a === null) { return data; } - var sub_props = String(a).split("."); + var sub_props = String(a).split('.'); for (var i = 0; i < sub_props.length; i++) { if (data === null) { diff --git a/lib/operations/arithmetic/substract.js b/lib/operations/arithmetic/substract.js index 72f44a7..a6a5bc2 100644 --- a/lib/operations/arithmetic/substract.js +++ b/lib/operations/arithmetic/substract.js @@ -1,9 +1,9 @@ function substract(a, b) { if (b === undefined) { return -a; - } else { - return a - b; } + + return a - b; } substract.code = '-'; diff --git a/lib/operations/array/index.js b/lib/operations/array/index.js index 3d25475..f4bf94f 100644 --- a/lib/operations/array/index.js +++ b/lib/operations/array/index.js @@ -1 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export export { default as merge } from './merge'; \ No newline at end of file diff --git a/lib/operations/logic/equal.js b/lib/operations/logic/equal.js index 5228162..1330968 100644 --- a/lib/operations/logic/equal.js +++ b/lib/operations/logic/equal.js @@ -1,4 +1,5 @@ function equal(a, b) { + // eslint-disable-next-line eqeqeq return a == b; } diff --git a/lib/operations/logic/notEqual.js b/lib/operations/logic/notEqual.js index 49e657a..4889718 100644 --- a/lib/operations/logic/notEqual.js +++ b/lib/operations/logic/notEqual.js @@ -1,4 +1,5 @@ function notEqual(a, b) { + // eslint-disable-next-line eqeqeq return a != b; } diff --git a/lib/operations/misc/indexOf.js b/lib/operations/misc/indexOf.js index 4656686..c55b4f9 100644 --- a/lib/operations/misc/indexOf.js +++ b/lib/operations/misc/indexOf.js @@ -1,5 +1,5 @@ function indexOf(a, b) { - if (!b || typeof b.indexOf === "undefined") return false; + if (!b || typeof b.indexOf === 'undefined') return false; return b.indexOf(a) !== -1; } diff --git a/lib/operations/misc/log.js b/lib/operations/misc/log.js index 07c6915..8c0b316 100644 --- a/lib/operations/misc/log.js +++ b/lib/operations/misc/log.js @@ -1,4 +1,5 @@ function log(a) { + // eslint-disable-next-line no-console console.log(a); return a; } diff --git a/lib/operations/misc/method.js b/lib/operations/misc/method.js index 613c8ea..c3aac36 100644 --- a/lib/operations/misc/method.js +++ b/lib/operations/misc/method.js @@ -1,5 +1,6 @@ -function method(obj, method, args) { - return obj[method].apply(obj, args); +function method(obj, methodName, args) { + // eslint-disable-next-line prefer-spread + return obj[methodName].apply(obj, args); } export default method; \ No newline at end of file diff --git a/lib/operations/numeric/max.js b/lib/operations/numeric/max.js index 4afe6d3..2d104aa 100644 --- a/lib/operations/numeric/max.js +++ b/lib/operations/numeric/max.js @@ -1,5 +1,5 @@ function max() { - return Math.max.apply(this, arguments); + return Math.max.apply(Math, arguments); } export default max; \ No newline at end of file diff --git a/lib/operations/numeric/min.js b/lib/operations/numeric/min.js index 7a43a90..5749f36 100644 --- a/lib/operations/numeric/min.js +++ b/lib/operations/numeric/min.js @@ -1,5 +1,5 @@ function min() { - return Math.min.apply(this, arguments); + return Math.min.apply(Math, arguments); } export default min; \ No newline at end of file diff --git a/lib/operations/string/cat.js b/lib/operations/string/cat.js index d92c33b..baef7f3 100644 --- a/lib/operations/string/cat.js +++ b/lib/operations/string/cat.js @@ -1,5 +1,9 @@ function cat() { - return Array.prototype.join.call(arguments, ""); + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return args.join(''); } export default cat; \ No newline at end of file diff --git a/lib/operations/string/substr.js b/lib/operations/string/substr.js index db3e7b8..d1a6c0d 100644 --- a/lib/operations/string/substr.js +++ b/lib/operations/string/substr.js @@ -8,5 +8,4 @@ function substr(source, start, end) { return String(source).substr(start, end); } -; export default substr; \ No newline at end of file diff --git a/lib/visitors/accessor/missing.js b/lib/visitors/accessor/missing.js deleted file mode 100644 index faa0d9d..0000000 --- a/lib/visitors/accessor/missing.js +++ /dev/null @@ -1,29 +0,0 @@ -import isArray from '../../helpers/isArray'; -import variable from '../../operations/accessor/variable'; - -function missing() { - /* - Missing can receive many keys as many arguments, like {"missing:[1,2]} - Missing can also receive *one* argument that is an array of keys, - which typically happens if it's actually acting on the output of another command - (like 'if' or 'merge') - */ - var missing = []; - var keys = isArray(arguments[0]) ? arguments[0] : arguments; - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - var value = variable.call(this, { - "var": key - }, arguments); - - if (value === null || value === "") { - missing.push(key); - } - } - - return missing; -} - -; -export default missing; \ No newline at end of file diff --git a/lib/visitors/accessor/missingSome.js b/lib/visitors/accessor/missingSome.js deleted file mode 100644 index c7f9bd2..0000000 --- a/lib/visitors/accessor/missingSome.js +++ /dev/null @@ -1,17 +0,0 @@ -import missing from './missing'; - -function missingSome(need_count, options) { - // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. - var are_missing = missing.call(this, { - "missing": options - }); - - if (options.length - are_missing.length >= need_count) { - return []; - } else { - return are_missing; - } -} - -missingSome.code = 'missing_some'; -export default missingSome; \ No newline at end of file diff --git a/lib/visitors/array/all.js b/lib/visitors/array/all.js index ec00c77..cf72795 100644 --- a/lib/visitors/array/all.js +++ b/lib/visitors/array/all.js @@ -1,4 +1,4 @@ -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function all(apply, data, values) { var scopedData = apply(values[0], data); diff --git a/lib/visitors/array/filter.js b/lib/visitors/array/filter.js index 6a65fe5..00b458d 100644 --- a/lib/visitors/array/filter.js +++ b/lib/visitors/array/filter.js @@ -1,5 +1,5 @@ import isArray from '../../helpers/isArray'; -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function filter(apply, data, values) { var scopedData = apply(values[0], data); diff --git a/lib/visitors/array/none.js b/lib/visitors/array/none.js index eeb13f4..718fa9a 100644 --- a/lib/visitors/array/none.js +++ b/lib/visitors/array/none.js @@ -1,6 +1,6 @@ function none(apply, data, values) { var filtered = apply({ - 'filter': values + filter: values }, data); return filtered.length === 0; } diff --git a/lib/visitors/array/reduce.js b/lib/visitors/array/reduce.js index 7eea583..bc14848 100644 --- a/lib/visitors/array/reduce.js +++ b/lib/visitors/array/reduce.js @@ -11,8 +11,8 @@ function reduce(apply, data, values) { return scopedData.reduce(function (accumulator, current) { return apply(scopedLogic, { - 'current': current, - 'accumulator': accumulator + current: current, + accumulator: accumulator }); }, initial); } diff --git a/lib/visitors/array/some.js b/lib/visitors/array/some.js index d1fb587..302df02 100644 --- a/lib/visitors/array/some.js +++ b/lib/visitors/array/some.js @@ -1,6 +1,6 @@ function some(apply, data, values) { var filtered = apply({ - 'filter': values + filter: values }, data); return filtered.length > 0; } diff --git a/lib/visitors/logic/and.js b/lib/visitors/logic/and.js index cf47058..b79efde 100644 --- a/lib/visitors/logic/and.js +++ b/lib/visitors/logic/and.js @@ -1,4 +1,4 @@ -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function and(apply, data, values) { var current; diff --git a/lib/visitors/logic/or.js b/lib/visitors/logic/or.js index 14d72c9..f852823 100644 --- a/lib/visitors/logic/or.js +++ b/lib/visitors/logic/or.js @@ -1,4 +1,4 @@ -import truthy from "../../helpers/truthy"; +import truthy from '../../helpers/truthy'; function or(apply, data, values) { var current; From c3f18dfebe461f5340887b24f6fecf70105c18b9 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 17:36:37 +0100 Subject: [PATCH 30/33] docs: updated array notes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf86a90..79d0ff5 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ jsonLogic.apply(false, i_wasnt_even_supposed_to_be_here); ## Compatibility -This library makes use of `Array.map` and `Array.reduce`, so it's not *exactly* Internet Explorer 8 friendly. +This library makes use of `Array.isArray`, `Array.forEach`, `Array.map` and `Array.reduce`, so it's not *exactly* Internet Explorer 8 friendly. If you want to use JsonLogic *and* support deprecated browsers, you could easily use [BabelJS's polyfill](https://babeljs.io/docs/usage/polyfill/) or directly incorporate the polyfills documented on MDN for [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce). From 5450731d4eb50c1bd562758be1be96f507d60a42 Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 17:47:31 +0100 Subject: [PATCH 31/33] docs: added chrry picking docs --- README.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 79d0ff5..f759058 100644 --- a/README.md +++ b/README.md @@ -8,24 +8,33 @@ The same format can also be executed in PHP by the library [json-logic-php](http ## Installation -To parse JsonLogic rules in a JavaScript frontend, install this library is via [Bower](http://bower.io/): +To parse JsonLogic rules in a JavaScript backend (like Node.js), install this library via [NPM](https://www.npmjs.com/): ```bash -bower install --save json-logic-js +npm install @axa-ch/json-logic-js --save ``` -To parse JsonLogic rules in a JavaScript backend (like Node.js), install this library via [NPM](https://www.npmjs.com/): +Note that this project provides: +- an ESM build for cherry picking +- a [module loader](http://ricostacruz.com/cheatsheets/umdjs.html) that also makes it suitable for RequireJS projects. +- a minified bundle ready for the browser -```bash -npm install json-logic-js -``` +If that doesn't suit you, and you want to manage updates yourself, the entire library is self-contained in `dist/jsonLogic.js` and you can download it straight into your project as you see fit. -Note that this project uses a [module loader](http://ricostacruz.com/cheatsheets/umdjs.html) that also makes it suitable for RequireJS projects. +## Cherry-picked build -If that doesn't suit you, and you want to manage updates yourself, the entire library is self-contained in `logic.js` and you can download it straight into your project as you see fit. +If the default bundle size is too big for you or you only need certain operations, just utilize the ESM build and create your customized `jsonLogic` object as follows: -```bash -curl -O https://raw.githubusercontent.com/jwadhams/json-logic-js/master/logic.js +```js +import createJsonLogic from './createJsonLogic'; + +// pick just what you need, or create your own +import { equal } from './operations'; +import { and, or } from './visitors'; + +const jsonLogic = createJsonLogic({ equal }, { and, or }); + +export default jsonLogic; ``` ## Examples From 182447f8aaa1dc3a7faa9e55d4bf7fb7a6e4b44a Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Fri, 22 Feb 2019 17:54:58 +0100 Subject: [PATCH 32/33] fix: removed old logic.js --- logic.js | 464 ------------------------------------------------------ play.html | 4 +- 2 files changed, 2 insertions(+), 466 deletions(-) delete mode 100644 logic.js diff --git a/logic.js b/logic.js deleted file mode 100644 index 6b827df..0000000 --- a/logic.js +++ /dev/null @@ -1,464 +0,0 @@ -/* globals define,module */ -/* -Using a Universal Module Loader that should be browser, require, and AMD friendly -http://ricostacruz.com/cheatsheets/umdjs.html -*/ -;(function(root, factory) { - if (typeof define === "function" && define.amd) { - define(factory); - } else if (typeof exports === "object") { - module.exports = factory(); - } else { - root.jsonLogic = factory(); - } -}(this, function() { - "use strict"; - /* globals console:false */ - - if ( ! Array.isArray) { - Array.isArray = function(arg) { - return Object.prototype.toString.call(arg) === "[object Array]"; - }; - } - - /** - * Return an array that contains no duplicates (original not modified) - * @param {array} array Original reference array - * @return {array} New array with no duplicates - */ - function arrayUnique(array) { - var a = []; - for (var i=0, l=array.length; i": function(a, b) { - return a > b; - }, - ">=": function(a, b) { - return a >= b; - }, - "<": function(a, b, c) { - return (c === undefined) ? a < b : (a < b) && (b < c); - }, - "<=": function(a, b, c) { - return (c === undefined) ? a <= b : (a <= b) && (b <= c); - }, - "!!": function(a) { - return jsonLogic.truthy(a); - }, - "!": function(a) { - return !jsonLogic.truthy(a); - }, - "%": function(a, b) { - return a % b; - }, - "log": function(a) { - console.log(a); return a; - }, - "in": function(a, b) { - if(!b || typeof b.indexOf === "undefined") return false; - return (b.indexOf(a) !== -1); - }, - "cat": function() { - return Array.prototype.join.call(arguments, ""); - }, - "substr":function(source, start, end) { - if(end < 0){ - // JavaScript doesn't support negative end, this emulates PHP behavior - var temp = String(source).substr(start); - return temp.substr(0, temp.length + end); - } - return String(source).substr(start, end); - }, - "+": function() { - return Array.prototype.reduce.call(arguments, function(a, b) { - return parseFloat(a, 10) + parseFloat(b, 10); - }, 0); - }, - "*": function() { - return Array.prototype.reduce.call(arguments, function(a, b) { - return parseFloat(a, 10) * parseFloat(b, 10); - }); - }, - "-": function(a, b) { - if(b === undefined) { - return -a; - }else{ - return a - b; - } - }, - "/": function(a, b) { - return a / b; - }, - "min": function() { - return Math.min.apply(this, arguments); - }, - "max": function() { - return Math.max.apply(this, arguments); - }, - "merge": function() { - return Array.prototype.reduce.call(arguments, function(a, b) { - return a.concat(b); - }, []); - }, - "var": function(a, b) { - var not_found = (b === undefined) ? null : b; - var data = this; - if(typeof a === "undefined" || a==="" || a===null) { - return data; - } - var sub_props = String(a).split("."); - for(var i = 0; i < sub_props.length; i++) { - if(data === null) { - return not_found; - } - // Descending into data - data = data[sub_props[i]]; - if(data === undefined) { - return not_found; - } - } - return data; - }, - "missing": function() { - /* - Missing can receive many keys as many arguments, like {"missing:[1,2]} - Missing can also receive *one* argument that is an array of keys, - which typically happens if it's actually acting on the output of another command - (like 'if' or 'merge') - */ - - var missing = []; - var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments; - - for(var i = 0; i < keys.length; i++) { - var key = keys[i]; - var value = jsonLogic.apply({"var": key}, this); - if(value === null || value === "") { - missing.push(key); - } - } - - return missing; - }, - "missing_some": function(need_count, options) { - // missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence. - var are_missing = jsonLogic.apply({"missing": options}, this); - - if(options.length - are_missing.length >= need_count) { - return []; - }else{ - return are_missing; - } - }, - "method": function(obj, method, args) { - return obj[method].apply(obj, args); - }, - - }; - - jsonLogic.is_logic = function(logic) { - return ( - typeof logic === "object" && // An object - logic !== null && // but not null - ! Array.isArray(logic) && // and not an array - Object.keys(logic).length === 1 // with exactly one key - ); - }; - - /* - This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. - - Spec and rationale here: http://jsonlogic.com/truthy - */ - jsonLogic.truthy = function(value) { - if(Array.isArray(value) && value.length === 0) { - return false; - } - return !! value; - }; - - - jsonLogic.get_operator = function(logic) { - return Object.keys(logic)[0]; - }; - - jsonLogic.get_values = function(logic) { - return logic[jsonLogic.get_operator(logic)]; - }; - - jsonLogic.apply = function(logic, data) { - // Does this array contain logic? Only one way to find out. - if(Array.isArray(logic)) { - return logic.map(function(l) { - return jsonLogic.apply(l, data); - }); - } - // You've recursed to a primitive, stop! - if( ! jsonLogic.is_logic(logic) ) { - return logic; - } - - data = data || {}; - - var op = jsonLogic.get_operator(logic); - var values = logic[op]; - var i; - var current; - var scopedLogic, scopedData, filtered, initial; - - // easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]} - if( ! Array.isArray(values)) { - values = [values]; - } - - // 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed. - if(op === "if" || op == "?:") { - /* 'if' should be called with a odd number of parameters, 3 or greater - This works on the pattern: - if( 0 ){ 1 }else{ 2 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; - - The implementation is: - For pairs of values (0,1 then 2,3 then 4,5 etc) - If the first evaluates truthy, evaluate and return the second - If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) - given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) - given 0 parameters, return NULL (not great practice, but there was no Else) - */ - for(i = 0; i < values.length - 1; i += 2) { - if( jsonLogic.truthy( jsonLogic.apply(values[i], data) ) ) { - return jsonLogic.apply(values[i+1], data); - } - } - if(values.length === i+1) return jsonLogic.apply(values[i], data); - return null; - }else if(op === "and") { // Return first falsy, or last - for(i=0; i < values.length; i+=1) { - current = jsonLogic.apply(values[i], data); - if( ! jsonLogic.truthy(current)) { - return current; - } - } - return current; // Last - }else if(op === "or") {// Return first truthy, or last - for(i=0; i < values.length; i+=1) { - current = jsonLogic.apply(values[i], data); - if( jsonLogic.truthy(current) ) { - return current; - } - } - return current; // Last - - - - - }else if(op === 'filter'){ - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - - if ( ! Array.isArray(scopedData)) { - return []; - } - // Return only the elements from the array in the first argument, - // that return truthy when passed to the logic in the second argument. - // For parity with JavaScript, reindex the returned array - return scopedData.filter(function(datum){ - return jsonLogic.truthy( jsonLogic.apply(scopedLogic, datum)); - }); - }else if(op === 'map'){ - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - - if ( ! Array.isArray(scopedData)) { - return []; - } - - return scopedData.map(function(datum){ - return jsonLogic.apply(scopedLogic, datum); - }); - - }else if(op === 'reduce'){ - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - initial = typeof values[2] !== 'undefined' ? values[2] : null; - - if ( ! Array.isArray(scopedData)) { - return initial; - } - - return scopedData.reduce( - function(accumulator, current){ - return jsonLogic.apply( - scopedLogic, - {'current':current, 'accumulator':accumulator} - ); - }, - initial - ); - - }else if(op === "all") { - scopedData = jsonLogic.apply(values[0], data); - scopedLogic = values[1]; - // All of an empty set is false. Note, some and none have correct fallback after the for loop - if( ! scopedData.length) { - return false; - } - for(i=0; i < scopedData.length; i+=1) { - if( ! jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) { - return false; // First falsy, short circuit - } - } - return true; // All were truthy - }else if(op === "none") { - filtered = jsonLogic.apply({'filter' : values}, data); - return filtered.length === 0; - - }else if(op === "some") { - filtered = jsonLogic.apply({'filter' : values}, data); - return filtered.length > 0; - } - - // Everyone else gets immediate depth-first recursion - values = values.map(function(val) { - return jsonLogic.apply(val, data); - }); - - - // The operation is called with "data" bound to its "this" and "values" passed as arguments. - // Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments - if(typeof operations[op] === "function") { - return operations[op].apply(data, values); - }else if(op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position - var sub_ops = String(op).split("."); - var operation = operations; - for(i = 0; i < sub_ops.length; i++) { - // Descending into operations - operation = operation[sub_ops[i]]; - if(operation === undefined) { - throw new Error("Unrecognized operation " + op + - " (failed at " + sub_ops.slice(0, i+1).join(".") + ")"); - } - } - - return operation.apply(data, values); - } - - throw new Error("Unrecognized operation " + op ); - }; - - jsonLogic.uses_data = function(logic) { - var collection = []; - - if( jsonLogic.is_logic(logic) ) { - var op = jsonLogic.get_operator(logic); - var values = logic[op]; - - if( ! Array.isArray(values)) { - values = [values]; - } - - if(op === "var") { - // This doesn't cover the case where the arg to var is itself a rule. - collection.push(values[0]); - }else{ - // Recursion! - values.map(function(val) { - collection.push.apply(collection, jsonLogic.uses_data(val) ); - }); - } - } - - return arrayUnique(collection); - }; - - jsonLogic.add_operation = function(name, code) { - operations[name] = code; - }; - - jsonLogic.rm_operation = function(name) { - delete operations[name]; - }; - - jsonLogic.rule_like = function(rule, pattern) { - // console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?"); - if(pattern === rule) { - return true; - } // TODO : Deep object equivalency? - if(pattern === "@") { - return true; - } // Wildcard! - if(pattern === "number") { - return (typeof rule === "number"); - } - if(pattern === "string") { - return (typeof rule === "string"); - } - if(pattern === "array") { - // !logic test might be superfluous in JavaScript - return Array.isArray(rule) && ! jsonLogic.is_logic(rule); - } - - if(jsonLogic.is_logic(pattern)) { - if(jsonLogic.is_logic(rule)) { - var pattern_op = jsonLogic.get_operator(pattern); - var rule_op = jsonLogic.get_operator(rule); - - if(pattern_op === "@" || pattern_op === rule_op) { - // echo "\nOperators match, go deeper\n"; - return jsonLogic.rule_like( - jsonLogic.get_values(rule, false), - jsonLogic.get_values(pattern, false) - ); - } - } - return false; // pattern is logic, rule isn't, can't be eq - } - - if(Array.isArray(pattern)) { - if(Array.isArray(rule)) { - if(pattern.length !== rule.length) { - return false; - } - /* - Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) - */ - for(var i = 0; i < pattern.length; i += 1) { - // If any fail, we fail - if( ! jsonLogic.rule_like(rule[i], pattern[i])) { - return false; - } - } - return true; // If they *all* passed, we pass - }else{ - return false; // Pattern is array, rule isn't - } - } - - // Not logic, not array, not a === match for rule. - return false; - }; - - return jsonLogic; -})); diff --git a/play.html b/play.html index f239440..f98a4b4 100644 --- a/play.html +++ b/play.html @@ -60,7 +60,7 @@

Test JsonLogic in your Browser

- +