Recently I sat down to work up a solution around a rules engine. There were a few things I noticed right off.
- When there is a request to implement or build a rules engine it is very often (I’m guessing a solid 40-60% of the time) reasoned that there is a need solely based on a lack of understanding around what the problem space is that actually needs a solution. The simple assumption, is 40-60% of the time somebody says “let’s implement a rules engine to solve these unknown problems” really translates to “we really don’t know much about this domain so let’s implement something arbitrary as a stop gap”.
- Implementing a business rules engine can quickly become a “support the user” scenario for the developers that implement the rules engine. This is a situation in which the developers actually have to help the people writing the rules to be processed. This is not an ideal situation at all, generally developers supporting users writing rules is a quick way to ensure burn out, misappropriation of skills and turnover.
- Many developers will, without hesitation, spout out “are you sure you want to implement a rules engine?” and then follow that up with “let’s discuss your actual problem” with that leading to “are you sure you want to implement a rules engine?”. Other developers upon hearing that one will implement a rules engine immediately respond with, “shit, I’m out.”
At this point I realized I had X, Y and Z reason to use it and would just have to persevere with all of the threats that are inclusive of implementing a rules engine. Sometimes one just has to step into the realm of scary and get it done.
So here’s what I dug up. I’m really not sure about the name of this project, as it appears to be some sort of odd usage, so whatever, but it is indeed called nools (github repo). Nools is a business rules engine based on the Rete Algorithm, something that is helpful to read up on when implementing. The main deployment for nools is to a Node.js server, but I’ve read that it is prospectively deployable in most browsers too.

There are a few other things that need definitions. So before moving on here are a few key words.
- rule – A collection of constraints that must be satisfied for in order for a action to occur.
- action – The code that will execute when a all of a rule’s constraints have been satisfied.
- fact – An object/item inserted into a session that the rule’s constraints match against.
- flow – This is container for rules that will be executed, you can think of it as a rule book.
- session – This is an “instance” of a flow used to match facts. Sessions are obtained from a flow.
Working With the Examples
The first thing I tried to run was a simple little sample of a nools rule file using the DSL. The nools file I named helloworld.nools and put in a directory called rules. The file looked like the example available in the README.md of the nools repo, but I immediately hit a bump.
The nools Rules
[sourcecode language=”javascript”]
define Message {
text : ”,
constructor : function(message){
this.text = message;
}
}
//find any message that starts with hello
rule Hello {
when {
m : Message m.text =~ /^hello(\s*world)?$/;
}
then {
modify(m, function(){this.text += " goodbye";});
}
}
//find all messages then end in goodbye
rule Goodbye {
when {
m : Message m.text =~ /.*goodbye$/;
}
then {
console.log(m.text);
}
}
[/sourcecode]
I created a server.js file with a simple bit of code just to see the results.
[sourcecode language=”javascript”]
var nools = require("nools");
var ruleFilePath = __dirname + "rules/helloworld.nools";
var flow = nools.compile();
var Message = flow.getDefined("message");
console.log("Rules executed @ " + ruleFilePath);
console.log(Message);
[/sourcecode]
I tried running this with a simple node server.js command. The result, unfortunately was just an error dumped into the bash.
[sourcecode language=”bash”]
16:29 $ node server.js
/Users/adron/Codez/testing-nools/node_modules/nools/lib/index.js:61
throw new Error("Name required when compiling nools source");
^
Error: Name required when compiling nools source
at Object.exports.compile (/Users/adron/Codez/testing-nools/node_modules/nools/lib/index.js:61:15)
at Object.<anonymous> (/Users/adron/Codez/testing-nools/server.js:3:18)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3
[/sourcecode]
Ugh, I suppose some things just don’t work on the first try. Based on that error message I dove into the actual nools code, to line 61 to get some insight into what the error messages actually means. The function the error is actually located in looks like this.
[sourcecode language=”javascript”]
exports.compile = function (file, options, cb) {
if (extd.isFunction(options)) {
cb = options;
options = {};
} else {
options = options || {};
cb = null;
}
if (extd.isString(file)) {
options.name = options.name || (isNoolsFile(file) ? path.basename(file, path.extname(file)) : null);
file = parse(file);
}
if (!options.name) {
throw new Error("Name required when compiling nools source");
}
return compile.compile(file, options, cb, FlowContainer);
};
[/sourcecode]
Line 61 is the line that reads throw new Error(“Name required when compiling nools source”);, which I suppose is a bit obvious. I did a little search on the README.md for the word options, which brought up the way to complie a nools rule and how to name it in the function call. I then looked back at my code and realized I’d not passed in the actual file to the compile function! Doh! I made that minor change of
[sourcecode language=”javascript”]
var flow = nools.compile();
[/sourcecode]
to this
[sourcecode language=”javascript”]
var flow = nools.compile(ruleFilePath);
[/sourcecode]
Before running I noticed I’d have one more little path problem and changed this
[sourcecode language=”javascript”]
var ruleFilePath = __dirname + "rules/helloworld.nools";
[/sourcecode]
to this
[sourcecode language=”javascript”]
var ruleFilePath = __dirname + "/rules/helloworld.nools";
[/sourcecode]
Now when I ran node server.js the result displayed as expected.
[sourcecode language=”bash”]
22:10 $ node server.js
Rules executed @ /Users/adron/Codez/testing-nools/rules/helloworld.nools
[Function]
[/sourcecode]
At this point the individual parts of this code are in need of explanation. But those explanations will be coming in a subsequent blog post. So keep reading, subscribe, and that post will be coming Sunday (that’s tomorrow) at 8:00am!
Good stuff Nools, read up the Rete Algorithm. Collecting the rule sets reminds me about a discussion with Andrew Ng on apprentice learning. Let the pilot fly the helicopter https://www.youtube.com/watch?v=M-QUkgk3HyE
Thanks a lot for the article. Helped a lot in understanding rule engine. Is there any related article as mentioned in this article which I can read further
I have some in my queue to finish up, but other than this one I’d take a read of the README docs in the project itself. Not much other material that I know off of hand. Good luck!
Thanks tons Adron !! look forward to read more stuff from you
Hi! Adron,
Could it be possible we can extend nools to fetch crieteria and action from db like Mongo.
interesting … I am planning to do quite similar but instead using JSONStore (mobility).. pls do let me know if someone is planning to do similar.
Hi Adron,
{
modify(m, function(){this.text += " goodbye";})
}
Just notice that in your example, the double quote has been escaped. The right one should be as below:
{
modify(m, function(){this.text += ” goodbye”;});
}