A rule engine in less than 70 lines of code
As I continue to explore what we can do with DSL, I am getting more and more excited. Let us take a look at the following syntax:
when User.IsPreferred and Order.TotalCost > 1000: addDiscountPrecentage 5 applyFreeShipping when not User.IsPreferred and Order.TotalCost > 1000: suggestUpgradeToPreferred applyFreeShipping when User.IsNotPreferred and Order.TotalCost > 500: applyFreeShipping
The backend for that is a simple 68 lines class. Again, we had to extend the language to support the when keyword, but that is all we really had to do.
I tried to build the same using Windows Workflow, but I gave up after a few minutes. It was too much clicking, and not enough results.
It should look something like the image to the left, I assume.
From the perspective of maintainability and actually being able to look at what is going on, I know what I would like to have.
At any rate, you can get the backend here, and get the unit tests here. Both are part of the test suites of the Rhino DSL project.
To be frank, I can't believe how easy this stuff is.
.
Comments
Try the PolicyActivity instead. You could put all those rules into a single ruleset for the PolicyActivity to evaluate and it will take care of managing priorities, and forward chaining on any intra-rule dependencies. There are other pros (and cons), but its a better fit than a sequential workflow (you don't want to control the flow of execution, you want the execution to evaluate declarative knowledge).
Yep, Scott is right on the money; this kind of thing would be far more suitable to a policy, and actually chaining can make it easier to define them. For example, notice that your example actually has a single rule defined three times, whereas if you had chaining you could have it a single time.
BTW, you can actually invoke the WF rules engine outside of WF, so I guess that might be useful on it's own (though I haven't had a chance to use it like this yet).
Tomas,
Can you explain why I have a single rule defined three times? I see only a single rule.
Sorry, I wasn't very clear. I was probably presuming much from your small code snippet, so don't pay much attention to me about that. (I was thinking along the lines that the way your example calculates the "freeShipping" option didn't make much sense).
Chaining is more useful in slightly more complex scenarios were the rule applications have side-effects that can invalidate other rules in the policy (or make valid previously invalid rules).... this is useful, but can also be a performance drag (or introduce unwanted cycles). In most rules-based systems like that you don't really apply rules, but entire policies (you probably know that already), which, in your case, I'd guess would be a complete .boo file.
The logic in the script is that the first one to match is the one that is being evaluated.
I am using boo file per policy here, yes.
Another option would be to define:
rule 'name':
when ...
This is really impressive. I like how you added the "When" keyword and the then statement is inferred by statement block.
I am going to have to check out this code. Nice job.
This looks really cool. It would be great to see a Hibernating Rhinos episode one day. BTW, thanks for the excellent video casts, it's been great to watch these as they are very informative and interesting. Your Resharper chops are truly impressive! I lost some of my skills after working in Eclipse all of last year!
Comment preview