Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

RuleBox : A natural language Rule Engine

385 views

Published on

RuleBox is a modern intuitive and natural language rule engine based upon the great work of RuleBook: https://github.com/rulebook-rules/rulebook ported over to ColdFusion (CFML).

Tired of classes filled with if/then/else statements? Need a nice abstraction that allows rules to be easily specified in a way that decouples them from each other? Want to write rules the same way that you write the rest of your code [in ColdFusion]? RuleBox is right for you!

Published in: Technology
  • Be the first to comment

  • Be the first to like this

RuleBox : A natural language Rule Engine

  1. 1. RuleBox => Modern & Natural Rule Engine!
  2. 2. WHO AM I? • Luis Majano • Computer Engineer • Born in El Salvador ->Texas • CEO of Ortus Solutions • Sandals -> ESRI -> Ortus @lmajano @ortussolutions
  3. 3. What is RuleBox Why RuleBox was created The Problem What is a Rule Engine Why use a Rule Engine Implementation of RuleBox
  4. 4. What is RuleBox • A modern and natural language rule engine • Inspired by RuleBook (Java Project) • Rules are written in the RuleBox Domain Specific Language (DSL) • Modeled after Given-When-Then • Dynamic and Expressive https://forgebox.io/view/rulebox Reference: https://github.com/rulebook-rules/rulebook install rulebox
  5. 5. Why Create RuleBox • Thanks to RevAgency for inspiration: • Real world problem to handle cruise promotions • Legacy app plagued with nested if-then-else • Logic and data mixed • Much more… • Did my research: • Majority of rule engines are complex and convoluted • Nothing in ColdFusion (CFML) Landscape • Found RuleBook articles inspiring • Lots of caffeine, and RuleBox was born!
  6. 6. The Problem
  7. 7. The Problem • Ever nesting of if-then-else logic • Imperative algorithms showing how instead of what? • No Logic and Data Separation • Logic is not easily followed • Maintenance pain • How testable is that code? Has it really been tested? • The Domino Effect: Single if statement can rule them all
  8. 8. What is a rule engine? • Alternative computational model to traditional imperative programming • Imperative: Sequence of commands with conditionals, loops and data • Set of rules that have a condition and an action based on data=>facts • The engine evaluates rules in the appropriate order and make sense of it • You focus on the conditions and actions and not the how you do it.
  9. 9. Advantages • Functional and Declarative Programming • Speed and Scalability of development • Understandable and Named Rules • Increase Maintenance • Increase Encapsulation of Logic • Logic and Data Separation
  10. 10. When?
  11. 11. When… • Remember the problem • Logic and data intermixed • Maintenance is painful • Logic is brittle • The logic changes often • 500 lines of if-else statements
  12. 12. How?
  13. 13. How does it work?
  14. 14. How does it work?
  15. 15. Chain of Responsibility • RuleBox Models CoR • A RuleBook defines rules and chains them together to provider order • Facts flow to each rule (data-binding) • RuleBooks control results • Each Rule has a condition, an exception and one or more actions https://sourcemaking.com/design_patterns/chain_of_responsibility
  16. 16. RuleBox Flow install rulebox Create RuleBooks Define Rules: when/then Run Rules with Given Facts Process Results
  17. 17. WireBox Mappings • Rule@rulebox - A transient rule object • RuleBook@rulebox - A transient rule book object • Builder@rulebox - A static class that can be used to build a-la-carte rules and rulebooks. • Result@rulebox - RuleBooks produce results and this is the object that models such results. Similar to Java Optionals
  18. 18. RuleBox DSL • Given( name, value ) - The facts • GivenAll( struct ) - Struct of facts
 • When( closure/lambda ) - The conditions • Except( closure/lambda ) - The exceptions • Then( closure/lambda ) - The actions/consumers Rules Against a RuleBook
  19. 19. RuleBooks component extends="rulebox.models.RuleBook"{     function defineRules(){ setName( “My RuleBook” );         // Add a new rule to this rulebook         addRule(             newRule( "MyRule" )                 .when( function( facts ){                     return facts.keyExists( “hello” );                 } )                 .then( function( facts ){                     systemOutput( "World" );                 } )         );     } } • ATransient CFC inherits from rulebox.models.RuleBook • Can have a name property • 1 Method: defineRules() • Create rules using the DSL
  20. 20. Creating Rules addRule(     newRule( “name” )     .when( function( facts ){     } )     .except( function( facts ){     } )     .then( function( facts, result ) {     } ) ) .addRule( function( rule ){     rule     .setName( ‘name’ )     .when( function( facts ){     } )     .except( function( facts ){     } )     .then( function( facts, result ) {     } ) .stop(); } ) • Using the addRule( rule ) method • Alternative Syntax, choose your comfort! • Each Rule can have the following: • Name (optional) • 1 when() • 0,1 except() • * then() • 1 stop() • Best friend:Api Docs https://apidocs.ortussolutions.com/#/coldbox-modules/rulebox/
  21. 21. When() addRule(     newRule()         .when( function( facts ){             return facts.keyExists( "hello" );         })         .then( function( facts ){             sytemOutput( facts.hello );         } ) ) • The condition portion • Argument is a closure/lambda • Must evaluate to boolean • True => Executes Consumers (thens) • False => Rule does not apply, skip consumers • Receives facts as a struct
  22. 22. Except() addRule(     newRule()         .when( function( facts ){             return facts.keyExists( "hello" );         }) .except( function( facts ){     return facts.accountDisabled; } )         .then( function( facts ){             sytemOutput( facts.hello );         } ) ) • Negates the when() condition • True => Skip consumers (thens) • False => Continue to consumers • Special cases and of course exceptions • Why?To not pollute the when() with negated logic
  23. 23. Consumers : then() then( function( facts, result ){ sytemOutput( facts.world ); } ) then( function( facts, result ){ result.setValue( result.getValue() * 0.80 ); } ) • The actions for your logic • Closure that receives • Facts => A struct of facts • Results => A RuleBox Result object • Can have multiple actions • If the action returns true then it stops the chain of consumers • Best friend:Api Docs https://apidocs.ortussolutions.com/#/coldbox-modules/rulebox/
  24. 24. Stopping Consumers .then( function( facts, result ) ){     // do stuff     // break the next then()     return true; }) .then( function( facts, result ) ){     // This never fires })
  25. 25. Stopping Rules //credit score under 600 gets a 4x rate increase addRule(     newRule()     .when( function( facts ){ return facts.applicant.getCreditScore() < 600; } )     .then( function( facts, result ){ result.setValue( result.getValue() * 4 ); } )     .stop() ); • stop() on a rule • Breaks the rule chain • No more rules are processed if a stop() is issued
  26. 26. Result Object • Rules can work on a result object’s value (rulebox.models.Result) • Seed it with a default value Operation Description reset() Reset the value with the default value getValue() Get the value getDefaultValue() Get the default value (if any) isPresent() Verifies the value is not null ifPresent( consumer ) If a value is present, the consumer will be called and the value passed to it. orElse( other ) If value is not set, then return other else the actual value orElseGet( producer ) If value is not set, then call the producer and return its value, else return the actual value setValue() Set the value in the result object setDefaultValue() Override the default value in the result object
  27. 27. Build a Loan Rule Engine
  28. 28. component extends="rulebox.models.RuleBook"{     function defineRules(){         addRule(             newRule( “//credit score under 600 gets a 4x rate increase” )             .when( function( facts ){ return facts[ "creditScore" ] < 600; } ) .then( function( facts, result ){ result.setValue( result.getValue() * 4 ); } )
 .stop()         )         .addRule( //credit score between 600 and 700 pays a 1 point increase             newRule()             .when( function( facts ){ return facts[ "creditScore" ] < 700; } )             .then( function( facts, result ){ result.setValue( result.getValue() + 1 ); } )         )         .addRule( //credit score is 700 and they have at least $25,000 cash on hand             newRule()             .when( function( facts ){                 return ( facts[ "creditScore" ] >= 700 && facts[ "cashOnHand" ] >= 25000 );             } )             .then( function( facts, result ){ result.setValue( result.getValue() - 0.25 ); } )         )         .addRule( // first time homebuyers get 20% off their rate (except if they have a creditScore < 600)             newRule()             .when( function( facts ){ return facts[ "firstTimeHomeBuyer" ]; } )             .then( function( facts, result ){ result.setValue( result.getValue() * 0.80 ); } )         );     } }
  29. 29. What’s Missing? Execute Rules
  30. 30. Executing Rules • Create the RuleBook and then we can: • Set a result default value: withDefaultResult() • Bind a fact: given( name, value ) • Bind multiple facts: givenAll( struct ) • Execute the rules: run( {facts} ) • Retrieve the Result object (if any): getResult()
  31. 31. describe( "Home Loan Rate Rules", function(){     it( "Can calculate a first time home buyer with 20,000 down and 650 credit score", function(){         var homeLoans = getInstance( "tests.resources.HomeLoanRateRuleBook" )             .withDefaultResult( 4.5 )             .given( "creditScore", 650 )             .given( "cashOnHand", 20000 )             .given( "firstTimeHomeBuyer", true );         homeLoans.run();         expect( homeLoans.getResult().isPresent() ).toBeTrue();         expect( homeLoans.getResult().getValue() ).toBe( 4.4 );     });     it( "Can calculate a non first home buyer with 20,000 down and 650 credit score", function(){         var homeLoans = getInstance( "tests.resources.HomeLoanRateRuleBook" )             .withDefaultResult( 4.5 )             .givenAll( {                 "creditScore" : 650,                 "cashOnHand" : 20000,                 "firstTimeHomeBuyer" : false             } );         homeLoans.run();         expect( homeLoans.getResult().isPresent() ).toBeTrue();         expect( homeLoans.getResult().getValue() ).toBe( 5.5 );     }); });
  32. 32. Auditing Rules • All rules are audited for execution • Important to name them in a human readable form • Else, love the UUID created for you • Methods for status: • getRuleStatus( ruleName ) - Get a single rule status • getRuleStatusMap() - All rule statuses Status Description NONE Rule never executed (default) SKIPPED Rule was skipped EXECUTED Rule was executed
  33. 33. Still in infancy Need your feedback Stay true to simplicity Focus on Functional Programming Nested Rules StatusVisualizer Dynamic rules from DB/Storage More Docs More Samples Roadmap
  34. 34. QUESTIONS? Go Build Some Rules!! www.ortussolutions.com @ortussolutions

×