Adding Operations (and outside libraries!)
What if you need to use a serious, existing, battle-hardened library inside your JsonLogic rules?
You can import that library into JsonLogic’s list of operators!
Note: JsonLogic can’t know what’s going on inside your custom operations. Maybe they have side effects? Maybe they update global state? Those things are OK, but inside your custom operations, I can’t guarantee Virtue #3 “Rules have no write access to anything.” Obviously.
OK, here are some examples:
Adding one new operator
If you have a function you want to expose as a JsonLogic operation, you can use:
var plus = function(a,b){ return a+b; }; jsonLogic.add_operation("plus", plus); jsonLogic.apply({"plus":[23, 19]}); //Returns 42 You don’t have to create your own functions, you can use libraries and built-ins:
jsonLogic.add_operation("sqrt", Math.sqrt); // you even get the unary syntactic sugar if you like! jsonLogic.apply({"sqrt":1764}); //Returns 42 Adding a whole library
You can also import a whole library, and use its methods with dot-notation:
jsonLogic.add_operation("Math", Math); //Note the empty array for no arguments jsonLogic.apply({"Math.random":[]}); //Returns a float between 0 and 1 jsonLogic.apply({"Math.abs":-42}); //Returns 42 jsonLogic.apply({"Math.ceil":41.001}); //Returns 42 jsonLogic.apply({"Math.log":1739274941520497700}); //Returns 42 Calling methods on objects
The ability to call an arbitrary method on an object led to a prototype pollution advisory and has been removed as of json-logic-js 2.0.0. I’d strongly recommend you convert object-oriented method calls into small simple functions like Math.abs above, when importing them into JsonLogic.
Limitations
You can’t use a custom operation to introduce a new control structure. The built-in control structures, if, and, and or only execute the code paths you’d expect. For example:
{ "if" : [ {"is_meatloaf" : {"var":"user"}}, {"for_love" : "anything"}, {"that" : []} ]} This code will always execute the custom is_meatloaf operation. If that return value is truthy, it’ll execute for_love, and it won’t do that. Duh, right?
Every other operator including custom operators, performs depth-first recursion.
Lets say you’re a fan of Perl’s unless, and you write:
var unless = function(condition, consequent){ if(!condition){ return consequent; } }; jsonLogic.add_operation("unless", unless}); //Nothing will work unless you do --Maya Angelou jsonLogic({"unless" : [ {"is_working" : {"var":"user"}}, {"will_work" : "nothing"} ]}); You’re going to find that is_working is called first, but will_work gets called regardless of is_working’s output and before unless has a chance to do anything.
If you want to introduce a new control structure, you need to patch the implementation, or open a Pull request.