100% Java, Lambda Enabled, Lightweight Rules Engine with a Simple and Intuitive DSL
100% Java, Lambda Enabled, Lightweight Rules Engine with a Simple and Intuitive DSL
» A Simple & Intuitive Rules Abstraction for Java 100% Java · Lambda Enabled · Simple, Intuitive DSL · Lightweight
RuleBook rules are built in the way that Java developers think: Java code. And they are executed in the way that programmers expect: In order. RuleBook also allows you to specify rules using an easy to use Lambda enabled Domain Specific Language or using POJOs that you define!
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 Java]? RuleBook just might be the rules abstraction you've been waiting for!
Got questions? Here are answers to Frequently Asked Questions!
Still not finding what you are looking for? Try the Wiki!
git clone https://github.com/Clayton7510/RuleBook.git
cd RuleBook
./gradlew buildAdd the code below to your pom.xml
com.deliveredtechnologies
rulebook-core
0.12
Add the code below to your build.gradle
compile 'com.deliveredtechnologies:rulebook-core:0.12'[Top]
RuleBook ruleBook = RuleBookBuilder.create()
.addRule(rule -> rule.withNoSpecifiedFactType()
.then(f -> System.out.print("Hello "))
.then(f -> System.out.println("World")))
.build();...or use 2 rules
RuleBook ruleBook = RuleBookBuilder.create()
.addRule(rule -> rule.withNoSpecifiedFactType().then(f -> System.out.print("Hello ")))
.addRule(rule -> rule.withNoSpecifiedFactType().then(f -> System.out.println("World")))
.build();now, run it!
ruleBook.run(new FactMap());RuleBook ruleBook = RuleBookBuilder.create()
.addRule(rule -> rule.withFactType(String.class)
.when(f -> f.containsKey("hello"))
.using("hello")
.then(System.out::print))
.addRule(rule -> rule.withFactType(String.class)
.when(f -> f.containsKey("world"))
.using("world")
.then(System.out::println))
.build();..or it could be a single rule
RuleBook ruleBook = RuleBookBuilder.create()
.addRule(rule -> rule.withFactType(String.class)
.when(f -> f.containsKey("hello") && f.containsKey("world"))
.using("hello").then(System.out::print)
.using("world").then(System.out::println))
.build();now, run it!
NameValueReferableMap factMap = new FactMap();
factMap.setValue("hello", "Hello ");
factMap.setValue("world", " World");
ruleBook.run(factMap);MegaBank issues home loans. If an applicant's credit score is less than 600 then they must pay 4x the current rate. If an applicant’s credit score is between 600, but less than 700, then they must pay a an additional point on top of their rate. If an applicant’s credit score is at least 700 and they have at least $25,000 cash on hand, then they get a quarter point reduction on their rate. If an applicant is a first time home buyer then they get a 20% reduction on their calculated rate after adjustments are made based on credit score (note: first time home buyer discount is only available for applicants with a 600 credit score or greater).
……public class ExampleSolution {
public static void main(String[] args) {
RuleBook homeLoanRateRuleBook = RuleBookBuilder.create(HomeLoanRateRuleBook.class).withResultType(Double.class)
.withDefaultResult(4.5)
.build();
NameValueReferableMap facts = new FactMap();
facts.setValue("applicant", new ApplicantBean(650, 20000.0, true));
homeLoanRateRuleBook.run(facts);
homeLoanRateRuleBook.getResult().ifPresent(result -> System.out.println("Applicant qualified for the following rate: " + result));
}
}...or nix the ApplicantBean and just use independent Facts
……RuleBooks are threadsafe. However, FactMaps and other implementations of NameValueReferableMap are not. This means that a single instance of a RuleBook can be run in different threads with different Facts without unexpected results. However, using the same exact FactMap across different threads may cause unexpected results. Facts represent data for individual invocations of a RuleBook, whereas RuleBooks represent reusable sets of Rules.
[Top]
The RuleBook Java Domain Specific Language (DSL) uses the Given-When-Then format, popularized by Behavior Driven Development (BDD) and associated testing frameworks (e.g. Cucumber and Spock). Many of the ideas that went into creating the RuleBook Java DSL are also borrowed from BDD, including: Sentences should be used to describe rules and Rules should be defined using a ubiquitous language that translates into the codebase.
Much like the Given-When-Then language for defining tests that was popularized by BDD, RuleBook uses a Given-When-Then language for defining rules. The RuleBook Given-When-Then methods have the following meanings.
Given methods can accept one or more facts in various different forms and are used as a collection of information provided to a single Rule. When grouping Rules into a RuleBook, facts are supplied to the Rules when the RuleBook is run, so the 'Given' can be inferred.
When methods accept a Predicate that evaluates a condition based on the Facts provided. Only one when() method can be specified per Rule.
Then methods accept a Consumer (or BiConsumer for Rules that have a Result) that describe the action to be invoked if the condition in the when() method evaluates to true. There can be multiple then() methods specified in a Rule that will all be invoked in the order they are specified if the when() condition evaluates to true.
Using methods reduce the set of facts available to a then() method. Multiple using() methods can also be chained together if so desired. The aggregate of the facts with the names specified in all using() methods immediately preceeding a then() method will be made available to that then() method. An example of how using() works is shown above.
Stop methods break the rule chain. If a stop() method is specified when defining a rule, it means that if the when() condition evaluates to true, following the completion of the then() action(s), the rule chain should be broken and no more rules in that chain should be evaluated.
Facts can be provided to Rules using the given() method. In RuleBooks, facts are provided to Rules when the RuleBook is run. The facts available to Rules and RuleBooks are contained in a NameValueReferableMap (the base implementation being FactMap), which is a special kind of Map that allows for easy access to the underlying objects contained in facts. The reason why facts exist is so that there is always a reference to the objects that Rules work with - even if say, an immutable object is replaced, the perception is that the Fact still exists and provides a named reference to a representative object.
Facts really only have a single convenience method. Since the NameValueReferableMap (e.g. FactMap) is what is passed into when() and then() methods, most of the convenience methods around facts are made available in the Map. However, there is one convenience method included in the Fact class... the constructor. Facts consist of a name value pair. But in some cases, the name of the Fact should just be the string value of the object it contains. In those cases, a constructor with a single argument of the
No open issues yet, or sync has not completed.