100% Java, Lambda Enabled, Lightweight Rules Engine with a Simple and Intuitive DSL

Overview

RuleBook

» A Simple & Intuitive Rules Abstraction for Java
100% Java · Lambda Enabled · Simple, Intuitive DSL · Lightweight


License Maven Central Build Status Coverage Status Paypal

Why RuleBook?

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!

Contents

1 Getting RuleBook

1.1 Building RuleBook

git clone https://github.com/Clayton7510/RuleBook.git
cd RuleBook
./gradlew build

1.2 Maven Central Releases

  • rulebook-core    Maven Central
  • rulebook-spring Maven Central

1.3 Latest Sonatype SNAPSHOT (Development) Release

  • rulebook-core    Sonatype Nexus
  • rulebook-spring Sonatype Nexus

1.4 Adding RuleBook to Your Maven Project

Add the code below to your pom.xml

<dependency>
    <groupId>com.deliveredtechnologies</groupId>
    <artifactId>rulebook-core</artifactId>
    <version>0.12</version>
</dependency>

1.5 Adding RuleBook to Your Gradle Project

Add the code below to your build.gradle

compile 'com.deliveredtechnologies:rulebook-core:0.12'

[Top]

2 Using RuleBook

2.1 A HelloWorld Example

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());

2.2 The Above Example Using Facts

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);

2.3 A [Slightly] More Complex Scenario

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 ApplicantBean {
  private int creditScore;
  private double cashOnHand;
  private boolean firstTimeHomeBuyer;

  public ApplicantBean(int creditScore, double cashOnHand, boolean firstTimeHomeBuyer) {
    this.creditScore = creditScore;
    this.cashOnHand = cashOnHand;
    this.firstTimeHomeBuyer = firstTimeHomeBuyer;
  }

  public int getCreditScore() {
    return creditScore;
  }

  public void setCreditScore(int creditScore) {     
    this.creditScore = creditScore;
  }

  public double getCashOnHand() {
    return cashOnHand;
  }

  public void setCashOnHand(double cashOnHand) {
    this.cashOnHand = cashOnHand;
  }

  public boolean isFirstTimeHomeBuyer() {
    return firstTimeHomeBuyer;
  }

  public void setFirstTimeHomeBuyer(boolean firstTimeHomeBuyer) {
    this.firstTimeHomeBuyer = firstTimeHomeBuyer;
  }
}
public class HomeLoanRateRuleBook extends CoRRuleBook<Double> {
  @Override
  public void defineRules() {
    //credit score under 600 gets a 4x rate increase
    addRule(RuleBuilder.create().withFactType(ApplicantBean.class).withResultType(Double.class)
      .when(facts -> facts.getOne().getCreditScore() < 600)
      .then((facts, result) -> result.setValue(result.getValue() * 4))
      .stop()
      .build());

    //credit score between 600 and 700 pays a 1 point increase
    addRule(RuleBuilder.create().withFactType(ApplicantBean.class).withResultType(Double.class)
      .when(facts -> facts.getOne().getCreditScore() < 700)
      .then((facts, result) -> result.setValue(result.getValue() + 1))
      .build());

    //credit score is 700 and they have at least $25,000 cash on hand
    addRule(RuleBuilder.create().withFactType(ApplicantBean.class).withResultType(Double.class)
      .when(facts ->
            facts.getOne().getCreditScore() >= 700 &&
            facts.getOne().getCashOnHand() >= 25000)
      .then((facts, result) -> result.setValue(result.getValue() - 0.25))
      .build());

    //first time homebuyers get 20% off their rate (except if they have a creditScore < 600)
    addRule(RuleBuilder.create().withFactType(ApplicantBean.class).withResultType(Double.class)
      .when(facts -> facts.getOne().isFirstTimeHomeBuyer())
      .then((facts, result) -> result.setValue(result.getValue() * 0.80))
      .build());
    }
}
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

public class HomeLoanRateRuleBook extends CoRRuleBook<Double> {
  @Override
  public void defineRules() {
    //credit score under 600 gets a 4x rate increase
    addRule(RuleBuilder.create().withResultType(Double.class)
      .when(facts -> facts.getIntVal("Credit Score") < 600)
      .then((facts, result) -> result.setValue(result.getValue() * 4))
      .stop()
      .build());

    //credit score between 600 and 700 pays a 1 point increase
    addRule(RuleBuilder.create().withResultType(Double.class)
      .when(facts -> facts.getIntVal("Credit Score") < 700)
      .then((facts, result) -> result.setValue(result.getValue() + 1))
      .build());

    //credit score is 700 and they have at least $25,000 cash on hand
    addRule(RuleBuilder.create().withResultType(Double.class)
      .when(facts ->
        facts.getIntVal("Credit Score") >= 700 &&
        facts.getDblVal("Cash on Hand") >= 25000)
      .then((facts, result) -> result.setValue(result.getValue() - 0.25))
      .build());

    //first time homebuyers get 20% off their rate (except if they have a creditScore < 600)
    addRule(RuleBuilder.create().withFactType(Boolean.class).withResultType(Double.class)
      .when(facts -> facts.getOne())
      .then((facts, result) -> result.setValue(result.getValue() * 0.80))
      .build());
  }
}
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("Credit Score", 650);
    facts.setValue("Cash on Hand", 20000);
    facts.setValue("First Time Homebuyer", true);

    homeLoanRateRuleBook.run(facts);

    homeLoanRateRuleBook.getResult().ifPresent(result -> System.out.println("Applicant qualified for the following rate: " + result));
    }
}

2.4 Thread Safety

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]

3 The RuleBook Domain Specific Language Explained

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.

3.1 Given-When-Then: The Basis of the RuleBook Language

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 some Fact(s)
  • When a condition evaluates to true
  • Then an action is triggered

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.

3.2 The Using Method

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.

3.3 The Stop Method

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.

3.4 Working With Facts

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.

3.4.1 The Single Fact Convenience Method

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 type of the object contained in the fact can be used.

3.4.2 The FactMap Convenience Methods

Although the reason for NameValueReferableMaps (commonly referred to as FactMaps) is important, that doesn't mean anyone wants to chain a bunch of boiler plate calls to get to the value object contained in an underlying Fact. So, some convenience methods are there to make life easier when working with when() and then() methods.

getOne() gets the value of the Fact when only one Fact exists in the FactMap

getValue(String name) gets the value of the Fact by the name of the Fact

setValue(String name, T value) sets the Fact with the name specified to the new value

put(Fact fact) adds a Fact to the FactMap, using the Fact's name as the key for the Map

toString() toString gets the toString() method of the Fact's value when only one Fact exists

The following methods are part of the NameValueReferrableTypeConvertible interface, which is implemented by the TypeConvertibleFactMap class as a NameValueReferrable decorator. You can think of it as a decorator for FactMaps (because it's also that too!) and it's what's used to inject facts into when() and then() methods.

getStrVal(String name) gets the value of the Fact by name as a String

getDblVal(String) gets the value of the Fact by name as a Double

getIntVal(String) gets the value of the Fact by name as an Integer

getBigDeciVal(String) gets the value of the Fact by name as a BigDecimal

getBoolVal(String) gets the value of the Fact by name as a Boolean

3.5 Auditing Rules

Rules auditing can be enabled when constructing a RuleBook by specifying asAuditor() as follows.

 RuleBook rulebook = RuleBookBuilder.create().asAuditor()
   .addRule(rule -> rule.withName("Rule1").withNoSpecifiedFactType()
     .when(facts -> true)
     .then(facts -> { } ))
   .addRule(rule -> rule.withName("Rule2").withNoSpecifiedFactType()
     .when(facts -> false)
     .then(facts -> { } )).build();
     
 rulebook.run(new FactMap());

By using asAuditor() each rule in the RuleBook can register itself as an Auditable Rule if its name is specified. Each Auditable Rule added to a RuleBook Auditor has its state recorded in the RuleBook. At the time when rules are registered as auditable in the RuleBook, their RuleStatus is NONE. After the RuleBook is run, their RuleStatus is changed to SKIPPED for all rules that fail or whose conditions do not evaluate to true. For rules whose conditions do evaluate to true and whose then() action completes successfully, their RuleStatus is changed to EXECUTED.

Retrieving the status of a rule can be done as follows.

 Auditor auditor = (Auditor)rulebook;
 System.out.println(auditor.getRuleStatus("Rule1")); //prints EXECUTED
 System.out.println(auditor.getRuleStatus("Rule2")); //prints SKIPPED

A map of all rule names and their corresponding status can be retrieved as follows.

 Map<String, RuleStatus> auditMap = auditor.getRuleStatusMap();

3.6 Rule Chain Behavior

By default, errors found when loading rules or exceptions thrown when running rules, remove those rules from the rule chain. In other words, rules that error are just skipped. Additionally, by default, a rule can only stop the rule chain if its condition evaluates to true and if its actions successfully complete.

However, this behavior can be changed on a per-rule basis.

RuleBook ruleBook = RuleBookBuilder.create()
    .addRule(
        RuleBuilder.create(GoldenRule.class, RuleChainActionType.STOP_ON_FAILURE)
            .withFactType(String.class)
            .when(facts -> true)
            .then(consumer)
            .stop()
            .build())
    .addRule(
        RuleBuilder.create()
            .withFactType(String.class)
            .when(facts -> true)
            .then(consumer)
            .build())
    .build();

In the above example, the default RuleChainActionType.CONTINUE_ON_FAILURE is changed to RuleChainActionType.STOP_ON_FAILURE in the first rule. This will ensure that if there is an error in the first rule, the 2nd rule will never be invoked. However, no error will be thrown.

If the desired behavior was to throw any exception that occurred in the first rule and stop the rule chain, the following code could be used.

RuleBook ruleBook = RuleBookBuilder.create()
    .addRule(
        RuleBuilder.create(GoldenRule.class, RuleChainActionType.ERROR_ON_FAILURE)
            .withFactType(String.class)
            .when(facts -> true)
            .then(consumer)
            .build())
    .addRule(
        RuleBuilder.create()
            .withFactType(String.class)
            .when(facts -> true)
            .then(consumer)
            .build())
    .build();

3.6.1 Rule Chain Action Types Defined

RuleChainActionType Description
CONTINUE_ON_FAILURE the default RuleChainActionType; false rule conditions and errors effectively 'skip' the rule
ERROR_ON_FAILURE exceptions thrown by rules stop the rule chain and bubble up the exception as a RuleException
STOP_ON_FAILURE rules that have their RuleState set to BREAK will stop the RuleChain if the rule's condition is false or if an exception is thrown

Top

4 POJO Rules

As of RuleBook v0.2, POJO rules are supported. Simply define your rules as annotated POJOs in a package and then use RuleBookRunner to scan the package for rules and create a RuleBook out of them. It's that simple!

4.1 A Hello World Example

package com.example.rulebook.helloworld;

import com.deliveredtechnologies.rulebook.annotations.*;
import com.deliveredtechnologies.rulebook.RuleState;

@Rule
public class HelloWorld {

  @Given("hello")
  private String hello;

  @Given("world")
  private String world;

  @Result
  private String helloworld;

  @When
  public boolean when() {
      return true;
  }

  @Then
  public RuleState then() {
      helloworld = hello + " " + world;
      return RuleState.BREAK;
  }
}
public static void main(String args[]) {
  RuleBookRunner ruleBook = new RuleBookRunner("com.example.rulebook.helloworld");
  NameValueReferableMap facts = new FactMap();
  facts.setValue("hello", "Hello");
  facts.setValue("world", "World");
  ruleBook.run(facts);
  ruleBook.getResult().ifPresent(System.out::println); //prints "Hello World"
}

4.2 A New MegaBank Example With POJO Rules

MegaBank changed their rate adjustment policy. They also now accept loan applications that include up to 3 applicants. If all of the applicants credit scores are below 600, then they must pay 4x the current rate. However, if all of the applicants have a credit score of less than 700, but at least one applicant has a credit score greater than 600, then they must pay an additional point on top the rate. Also, if any of the applicants have a credit score of 700 or more and the sum of the cash on hand available from all applicants is greater than or equal to $50,000, then they get a quarter point reduction in their rate. And if at least one applicant is a first time home buyer and at least one applicant has a credit score of over 600, then they get a 20% reduction in their calculated rate after all other adjustments are made.

...using the ApplicantBean defined above

@Rule(order = 1) //order specifies the order the rule should execute in; if not specified, any order may be used
public class ApplicantNumberRule {
  @Given
  private List<ApplicantBean> applicants; //Annotated Lists get injected with all Facts of the declared generic type

  @When
  public boolean when() {
    return applicants.size() > 3;
  }

  @Then
  public RuleState then() {
    return RuleState.BREAK;
  }
}
@Rule(order = 2)
public class LowCreditScoreRule {
  @Given
  private List<ApplicantBean> applicants;

  @Result
  private double rate;

  @When
  public boolean when() {
    return applicants.stream()
      .allMatch(applicant -> applicant.getCreditScore() < 600);
  }

  @Then
  public RuleState then() {
    rate *= 4;
    return BREAK;
  }
}
@Rule(order = 3)
public class QuarterPointReductionRule {
  @Given
  List<ApplicantBean> applicants;

  @Result
  private double rate;

  @When
  public boolean when() {
    return
      applicants.stream().anyMatch(applicant -> applicant.getCreditScore() >= 700) &&
      applicants.stream().map(applicant -> applicant.getCashOnHand()).reduce(0.0, Double::sum) >= 50000;
  }

  @Then
  public void then() {
    rate = rate - (rate * 0.25);
  }
}
@Rule(order = 3)
public class ExtraPointRule {
  @Given
  List<ApplicantBean> applicants;

  @Result
  private double rate;

  @When
  public boolean when() {
    return
      applicants.stream().anyMatch(applicant -> applicant.getCreditScore() < 700 && applicant.getCreditScore() >= 600);
  }

  @Then
  public void then() {
    rate += 1;
  }
}
@Rule(order = 4)
public class FirstTimeHomeBuyerRule {
  @Given
  List<ApplicantBean> applicants;

  @Result
  private double rate;

  @When
  public boolean when() {
    return
      applicants.stream().anyMatch(applicant -> applicant.isFirstTimeHomeBuyer());
  }

  @Then
  public void then() {
    rate = rate - (rate * 0.20);
  }
}
public class ExampleSolution {
  public static void main(String[] args) {
    RuleBookRunner ruleBook = new RuleBookRunner("com.example.rulebook.megabank");
    NameValueReferableMap<ApplicantBean> facts = new FactMap<>();
    ApplicantBean applicant1 = new ApplicantBean(650, 20000, true);
    ApplicantBean applicant2 = new ApplicantBean(620, 30000, true);
    facts.put(new Fact<>(applicant1));
    facts.put(new Fact<>(applicant2));

    ruleBook.setDefaultResult(4.5);
    ruleBook.run(facts);
    ruleBook.getResult().ifPresent(result -> System.out.println("Applicant qualified for the following rate: " + result));
  }
}

4.3 POJO Rules Explained

POJO Rules are annotated with @Rule at the class level. This lets the RuleBookRunner know that the class you defined is really a Rule. Facts are injected into POJO Rules using @Given annotations. The value passed into the @Given annotation is the name of the Fact given to the RuleBookRunner. The types annotated by @Given can either be the generic type of the matching Fact or the Fact type as seen above. The big difference between the two is that changes applied to immutable objects are not propagated down the rule chain if the Fact’s generic object is changed (because it would then be a new object). However, if you set the value on a Fact object, those changes will be persisted down the rule chain.

The @When annotation denotes the method that is used as the condition for executing the ‘then’ action. The method annotated with @When should accept no arguments and it should return a boolean result.

The @Then annotation denotes the action(s) of the rule that is executed if the ‘when’ condition evaluates to true. The method(s) annotated with @Then should accept no arugments and it can optionally return a RuleState result. If more than one method in a POJO rule is annotated with @Then, then all rules annotated with @Then are executed if the 'when' condition evaluates to true.

The @Result annotation denotes the result of the Rule. Of course, some Rules may not have a result. In that case, just don’t use the @Result annotation. It’s that simple.

4.3.1 Ordering POJO Rules

The ‘order’ property can [optionally] be used with the @Rule annoataion to specify the order in which POJO Rules will execute as seen above. If the order property is not specified, then Rules may execute in any order. Similarly, more than one Rule may have the same order, which would mean that the Rules with a matching order can fire in any order - order would then denote a group of rules, where the execution of the group is ordered among other rules, but the execution of the rules within that group doesn’t matter.

4.3.2 Injecting Collections into POJO Rules

If the following conditions are met then the objects contained in all Facts of generic type specified are injected into a collection:

  • A List, Set, Map or FactMap is annotated with a @Given annotation
  • The @Given annotation on the collection has no value specified
  • The generic type of the List, Set, Map (the first generic type in a Map is String - representing the name of the Fact injected) or FactMap is the same type of at least one Fact supplied to the RuleBookRunner

4.3.3 POJO Rule Annotation Inheritance

As of v.0.3.2, RuleBook supports annotation inheritance on POJO Rules. That means if you have a subclass, whose parent is annotated with RuleBook annotations (i.e. @Given, @When, @Then, @Result) then the subclass will inherit the parent’s annotations. @Given and @Result attributes injected in the parent, will be available to the subclass. @Then and @When methods defined in the parent will be visible in the subclass.

4.3.4 Auditing POJO Rules

Auditing is built into POJO Rules via the RuleBookRunner and each POJO Rule is automatically audited. If a name is specified in the @Rule attribute, then that name is used for auditing. Otherwise, the class name of the POJO rule is used. For example, assuming that there is a POJO rule named "My Rule" that was run by the RuleBookRunner, the status of that rule execution can be retrieved as follows.

 Auditor auditor = (Auditor)rulebookRunner;
 RuleStatus myRuleStatus = auditor.getRuleStatus("My Rule");

4.3.5 POJO Rule Chain Behavior

@Rule(ruleChainAction = ERROR_ON_FAILURE)
public class ErrorRule {
  @When
  public boolean when() {
    return true;
  }

  @Then
  public void then() throws Exception {
    throw new CustomException("Sumthin' Broke!");
  }
}

As seen in the example directly above, the ruleChainAction Rule parameter can be use to change the rule chain behavior for specific rules as detailed in 3.6 Rule Chain Behavior.

[Top]

5 Using RuleBook with Spring

RuleBook can be integrated with Spring to inject instances of RuleBooks that are created from POJOs in a package. RuleBooks can be specified using either the Java DSL or POJO Rules. And since RuleBooks are threadsafe, they can be used as Singeltons, Spring's default for injecting beans. Additionally, POJO Rules can now be made Spring Aware, so you can inject Spring components using @Autowire.

5.1 Adding RuleBook Spring Support to Your Project

The preferred way to use RuleBook with Spring is to configure a SpringAwareRuleBookRunner. Then, simply add the @RuleBean annotation to any POJO Rules that you would like to work with Spring. If you omit the @RuleBean annotation then the @POJO Rule(s) without @RuleBean can still be loaded and run, they just will not be managed by or scoped properly for Spring and @Autowired will not work within the Rule.

5.2 Creating Spring Enabled POJO Rules

POJO Rules can be created just like they were created above without Spring, but with some extra Spring goodness! To create Spring enabled POJO Rules, first add rulebook-spring as a dependency.

Maven:

<dependency>
    <groupId>com.deliveredtechnologies</groupId>
    <artifactId>rulebook-spring</artifactId>
    <version>0.12</version>
</dependency>

Gradle:

compile 'com.deliveredtechnologies:rulebook-spring:0.12'

Note: 0.11 is currently the only version of rulebook-spring that provides SpringAwareRuleBookRunner, which is what allows Rules to have injected @Autowired Spring components.

The trivial example below demonstates the basic functionality.

package com.exampl.rulebook.helloworld.component;

@Component
public class HelloWorldComponent {
  public String getHelloWorld(String hello, String world) {
    return hello + " " + world + "!";
  }
}
package com.example.rulebook.helloworld;

@RuleBean
@Rule(order = 1)
public class HelloSpringRule {
  @Given("hello")
  private String hello;

  @Result
  private String result;

  @When
  public boolean when() {
    return hello != null;
  }

  @Then
  public void then() {
    result = hello;
  }
}
package com.example.rulebook.helloworld;

@RuleBean
@Rule(order = 2)
public class WorldSpringRule {
  @Autowired
  HelloWorldComponent helloWorldComponent;
  
  @Given("world")
  private String world;

  @Result
  private String result;

  @When
  public boolean when() {
    return world != null;
  }

  @Then
  public void then() {
    result = helloWorldComponent.getHelloWorld(result, world);
  }
}

5.3 Configuring a RuleBook in Spring

@Configuration
@ComponentScan("com.example.rulebook.helloworld")
public class SpringConfig {
  @Bean
  public RuleBook ruleBook() {
    RuleBook ruleBook = new SpringAwareRuleBookRunner("com.example.rulebook.helloworld");
    return ruleBook;
  }
}

5.4 Using a Spring Enabled RuleBook

  @Autowired
  private RuleBook ruleBook;

  public void printResult() {
    NameValueReferableMap<String> facts = new FactMap<>();
    facts.setValue("hello", "Hello ");
    facts.setValue("world", "World");
    ruleBook.run(facts);
    ruleBook.getResult().ifPresent(System.out::println); //prints Hello World!
  }

5.5 Ordering Rules With Spring

If you were using the RuleBean annotation to create Spring enabled Rules, all of that stuff still works. And there Spring enabled POJO Rules can still be configured in RuleBooks in Spring [using SpringRuleBook]. But RuleBean doesn't have an order property. So, if you need to order beans scanned using a RuleBookFactoryBean, just use the @Rule annotation like you would with regular non-Spring enabled POJO Rules. It works exactly the same way!

[Top]

6 How to Contribute

Suggestions and code contributions are welcome! Please see the Developer Guidelines below.

6.1 Developer Guidelines

Contributions must adhere to the following criteria:

  1. The forked repository must be publicly visible.
  2. The issues addressed in the request must be associated to an accepted issue.
  3. The build (i.e. ./gradlew build) must pass with no errors or warnings.
  4. All new and existing tests must pass.
  5. The code must adhere to the style guidleines icluded in the checkstyle configuration (i.e. no checkstyle errors).
  6. Newly introduced code must have at least 85% test coverage.
  7. Pull requests must be for the develop branch.
  8. The version number in gradle.properties should match the milestone of the issue its associated with appended with -SNAPSHOT (ex. 0.2-SNAPSHOT)

Anyone may submit an issue, which can be either an enhancement/feature request or a bug to be remediated. If a feature request or a bug is approved (most reasonable ones will be), completed and an associated pull request is submitted that adheres to the above criteria, then the pull request will be merged and the contributor will be added to the list of contributors in the following release.

[Top]

Comments
  • Update GoldenRule exception handling to be more transparent

    Update GoldenRule exception handling to be more transparent

    GoldenRule currently swallows all exceptions. While this may be helpful in some cases (e.g. a rule attempts to use a fact that does not yet exist), it can hide other exceptions that should be thrown. GoldenRule should therefore be updated to throw non-whitelisted exceptions.

    enhancement resolved pending confirmation 
    opened by Clayton7510 20
  • Injecting helper-objects into POJO-rules

    Injecting helper-objects into POJO-rules

    Hey! I have one more question. Imagine the case I need some utility-object (f.e. for db-access) in my POJO-rule. Is there any other way to inject this object into the rule than injecting it as a fact? Because this feels kind of wrong to me, because the object is not really a fact which is needed to handle my business-rules, it's more a helper-object... I guess you know what I mean. In a spring-context f.e. it would be cool if something like that would work (I mean the dao-field in the middle):

    @Rule( name = "SimpleRule")
    public class SimpleRule {
        @Given("status")
        private int status;
        @Given("object")
        private Object object;
        @Result
        private String result;
    
        @Autowired 
        private Dao dao;
    
        @When
        public boolean when() {
            return true;
        }
    
        @Then
        public RuleState then() {
            dao.iWantToLoadSomethingToFigureOutWhatToDoNow();
            result = "Rule was triggered with status=" + status + " and object=" + object;
            return  RuleState.NEXT;
        }
    }
    

    Do you have any suggestion how to do this by not injecting this as a Fact? Regards, Peter

    enhancement question 
    opened by pjotre86 15
  • Stopping the chain if a Rule returns false

    Stopping the chain if a Rule returns false

    Hi

    If I want to add a bunch of rules, where I want the result to contain success ONLY if all rules are successful, is this possible currently. So, lets say that I have two rules, the first one checks if a user is Male, the second checks whether the users age is less than 40. If the user happens to be female, then the first rule will return false, but the second rule will still process. If the second rule returns true (the user is less than 40), then the rulebook result is true, even though I actually want it to be false. Essentially I'd like to be able to stop the chain should a rule fail, but this doesn't look possible. Is there another way of doing this possibly ?

    Thanks

    Darrell

    enhancement question 
    opened by darrellme 13
  • null-Facts suppress rule-execution

    null-Facts suppress rule-execution

    Hey there! First of all: Just discovered RuleBook and I like the concept pretty much. I'm just creating a small test-project to see what's possible and stumbled over a small uncovinient/unexpected behaviour.

    I created some POJO-rules and want to execute them with rulebook.run(facts); The facts I create via:

    NameValueReferableMap facts = new FactMap();
    facts.setValue("oneFact",  "first");
    facts.setValue("anotherFact", someObject);
    

    Everything works fine so far, my rules are behaving like expected, but not when someObject is null. Then nothing happens, no rule is triggered. Is this intended behaviour? I was expecting to be able to check facts to be null inside the rules themselves, but now I never get there. Is there a different way to create the fact-map, so it works with null-values? I helped myself now by sub-classing the FactMap like this:

    private static class NullSafeFactMap extends FactMap {
        @Override
        public void setValue(String name, Object value) {
            if (value != null) {
                super.setValue(name, value);
            }
        }
    }
    

    That works for me, but I was wondering if I really need something like that. Curious for your answers!

    bug 
    opened by pjotre86 12
  • Is there anyway of building rules that have a default result and are also auditable (using asAuditor)?

    Is there anyway of building rules that have a default result and are also auditable (using asAuditor)?

    I can't seem to find a way of building a rulebook that will allow the results to have a default value and be auditable (get a map of rules status).

    Any help? And if there's no way are there any plans to add it?

    Thanks

    bug 
    opened by brunoriscado 11
  • FileSystemNotFoundException on standalone Spring Boot app

    FileSystemNotFoundException on standalone Spring Boot app

    I like where this library is going so I am giving it a shot. The main problem I've encountered that I don't seem to find a workaround for is this FileSystemNotFoundException that occurs when I run the RuleBook standalone (I am using the annotated classes not the Java DSL). Everything works fine in IntelliJ but if I try to just java -jar target/long-jar-name.jar from the command line it fails. I am running this in a standard Spring Boot app.

    It appears that the problem could be how the Paths are created using NIO: https://stackoverflow.com/questions/22605666/java-access-files-in-jar-causes-java-nio-file-filesystemnotfoundexception

    I am using version 0.8 pulled from Maven Central. Here is what the exception looks like:

    2017-09-12 23:18:22 [http-nio-8080-exec-3] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.nio.file.FileSystemNotFoundException] with root cause
    java.nio.file.FileSystemNotFoundException: null
            at com.sun.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:171)
            at com.sun.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:157)
            at java.nio.file.Paths.get(Paths.java:143)
            at com.deliveredtechnologies.rulebook.model.runner.RuleBookRunner.findRuleClassesInPackage(RuleBookRunner.java:120)
            at com.deliveredtechnologies.rulebook.model.runner.RuleBookRunner.run(RuleBookRunner.java:68)
    
    bug 
    opened by ivanbportugal 10
  • When using RuleBookBuilder.create Rules aren't audited even when .asAuditor() is used

    When using RuleBookBuilder.create Rules aren't audited even when .asAuditor() is used

    I've added a new test that is the same as ruleBookAuditorCanAuditRules but instead uses a class (SimpleRuleAdds) which has the same rules but defined in defineRules. This test would fail as the Audit doesn't work. The example test shows that you extend RuleBookAuditor and that you have to create a Runner in the constructor , and then there are some changes to pass the Auditor down the chain.

    awaiting response 
    opened by mikelear 7
  • Re-using a Rulebook

    Re-using a Rulebook

    When I use this construct, resetting the rulebook's result does not remove its "presence" so I have to reset the default result value and test for that condition to know the facts did not satisfy any of my rules. Essentially the reset does not work as I would expect.

    	for(ThreatVector threatVector : threatVectors) {
    		facts.setValue("threat", threatVector);
    		ruleBook.setDefaultResult("NONE");  // should not have to set the default again
    		ruleBook.run(facts);
    		assert ruleBook.getResult() != null;
    		ruleBook.getResult().ifPresent(result ->  {
    			logger.info("{} was returned for {}", result, threatVector.getInstanceId());
    			threatVector.setRulesResult(result.getValue().toString());
    			logger.info("Set rules result to {} for {}", threatVector.getRulesResult(), threatVector.getInstanceId());
    		});
    		ruleBook.getResult().get().reset();
    	}
    
    question 
    opened by thormanrd 6
  • When-condition of rule is not accessed

    When-condition of rule is not accessed

    Hi,

    I'm trying to use RuleBook version 0.5 in my Spring-application. It seems like the when-part will never be evaluated, so I access for each rule the then-part. First there comes my code, then the issue is explained with a little bit more details.

    The Code:

    My Pom contains:

    <dependency>
        <groupId>com.deliveredtechnologies</groupId>
        <artifactId>rulebook-core</artifactId>
        <version>0.5</version>
    </dependency>
    <dependency>
        <groupId>com.deliveredtechnologies</groupId>
        <artifactId>rulebook-spring</artifactId>
        <version>0.5</version>
    </dependency>
    

    Like in the wiki/readme told I created a @Configuration-bean:

    @Configuration
    public class RuleBookSpringConfig {
      @Bean
      public RuleBookFactoryBean ruleBook() throws InvalidClassException {
    	return new RuleBookFactoryBean("my.package.rulebook.myRules"); 
      }
    }
    

    I defined one rule in the package:

    @Rule(order =1)
    public class MyRule {
      public MyRule() {}
    
      @Given("field")
      private myClass field;
      
      @When
      public boolean when() {
    	System.out.println("Evaluate condition");
    	return field.isPropertySet(); 
      }
      
      @Then
      public RuleState then() {
    	System.out.println("Rule is correct");
    	return RuleState.NEXT;
      }
    }
    

    This rule is used here:

    NameValueReferableMap<FieldMetaElement> facts = new FactMap<>();
    facts.setValue("field", instanceOfMyClass);
    this.ruleBook.run(facts);
    

    The issue:

    If I run the code, my output is every time Rule is correct, wether the property is set or not. The output Evaluate condition is never displayed. So I changed the when-part to:

    @When
    public boolean when() {
        return false;
    }
    

    The output is every time Rule is correct again. If I set a breakpoint to when() it does not stop there. So the when-part is not executed at every time. If I set a breakpoint at com.deliveredtechnologies.rulebook.model.runner.RuleAdapter.class line 126 - public Predicate<NameValueReferableMap> getCondition() the application does not stop there. Same at com.deliveredtechnologies.rulebook.runner.RuleAdapter.class line 121 - public Predicate getWhen(). I tried to use a manually instance of RuleBook without spring and have exactly the same situation. Same situation comes if I use a rule with a Result or more rules.

    Additional information:

    The imports at the rule-class are:

    • import com.deliveredtechnologies.rulebook.RuleState;
    • import com.deliveredtechnologies.rulebook.annotation.Given;
    • import com.deliveredtechnologies.rulebook.annotation.Rule;
    • import com.deliveredtechnologies.rulebook.annotation.Then;
    • import com.deliveredtechnologies.rulebook.annotation.When;

    The imports at the rule-usage-class are:

    • import com.deliveredtechnologies.rulebook.FactMap;
    • import com.deliveredtechnologies.rulebook.NameValueReferableMap;
    • import com.deliveredtechnologies.rulebook.model.RuleBook;
    • import com.deliveredtechnologies.rulebook.Result;

    The import at Spring-configuration is:

    • import com.deliveredtechnologies.rulebook.spring.RuleBookFactoryBean;
    bug 
    opened by Gersee 6
  • Add Some More Syntactic Sugar to the DSL

    Add Some More Syntactic Sugar to the DSL

    • Add a Consumer then() method option that continues by default.
    • Add the ability to chain multiple then() statements.
    • Add a stop() method that breaks on then()
    • Add a given() method that accepts a String and an Object of some type T for adding a single Fact
      • Do this for Rules and RuleBooks
    enhancement 
    opened by Clayton7510 6
  • How to reset result

    How to reset result

    Could you tell us how to reset result between every rule execution. We don't want to accumulate results for each rule execution. Below an exemple of the rulebook creation we use.

    RuleBook<RuleBookExecutionContext> ruleBook = (RuleBook) RuleBookBuilder.create()
    			.addRule(new VerboseRuleAdapter(RuleBuilder.create().withFactType(StringLiteralExpr.class)
    					.withResultType(RuleBookExecutionContext.class)
    					.when(StringLiteralsShouldNotBeDuplicatedConfig
    .getWhen(StringLiteralsShouldNotBeDuplicatedConfig.defaultMinCharacters))
    					.then(StringLiteralsShouldNotBeDuplicatedConfig
    .getThen(StringLiteralsShouldNotBeDuplicatedConfig.defaultMinRepetitions))
    					.build(), StringLiteralsShouldNotBeDuplicatedConfig.getViolations()))
    			.build();
    	;
    	ruleBook.setDefaultResult(new RuleBookExecutionContext());
    	return ruleBook;
    

    Thanks

    opened by jlerbsc 5
  • RuleBookRunner4PojoClasses does not run rules according to the order property

    RuleBookRunner4PojoClasses does not run rules according to the order property

    When using RuleBookRunner4PojoClasses class, the rules are not executed in the order defined by order property, in Rule interface I found this issue recently, and I have an easy fix for. Thanks

    opened by pecaetano 0
  • RuleBuilder doesn't create a Rule with action type RuleChainActionType.ERROR_ON_FAILURE

    RuleBuilder doesn't create a Rule with action type RuleChainActionType.ERROR_ON_FAILURE

    I was expecting by using RuleBuilder.create(GoldenRule.class, RuleChainActionType.ERROR_ON_FAILURE) I an create a Rule which will throw RuleException in case of a error while executing the rule. But it didn't work and when I investigate it in detail I found that RuleBuilder will never create a Rule with RuleChainActionType.ERROR_ON_FAILURE action.

    In my understanding the private method private Rule<T, U> newRule() creates a new instance of the Rule. in this method a Rule with a RuleChainActionType is instantiated only in the first condition when action type is STOP_ON_FAILURE (line 168 of RuleBuilder). there is no other place where the Rule is created with RuleChainActionType as second parameter.

    I saw that at line 176 of the RuleBuilder the second parameter in the constructor is resultType return (Rule)constructor.newInstance(this._factType, this._resultType); But the GoldenRule class doesn't have a constructor for this combination.

    Can this be a bug , actionType should be passed as second parameter instead of resultType

    opened by chandmanish 2
  • Lambdas stack error

    Lambdas stack error

    While stress testing Rulebook I found that the lambdas in GoldenRule.java line 176 hits StackOverFlowError. This is observed with Rulebook core 0.12. Using Fluent API When number of objects of Rule created with RuleBuilder.create() exceeds 3000.

    opened by ghost 0
  • Project status

    Project status

    Hello!

    I saw that the last commit was made on October 19, 2020.

    Is the project still being monitored for bug fixes, new features, etc?

    I'm asking because I'm evaluating some validation tools and I really like what the rulebook provides, and I'm worried that I'll get back to you if I run into a problem or question in some situations.

    Thanks

    opened by yvesfso 2
  • Rulebook in Spring Boot - stop execution

    Rulebook in Spring Boot - stop execution

    We have a rukle book in Spring Boot. We have only just started with the first 10 of when will probably be over 100 rules. It's already getting good feedback and allowed us to announce a new product feature. Se we like it!

    One question that has come up from our designers. Is it possible to have a rule executed which if it passes the @When stage, performs its actions and the stops?

    So, if this, then that and don't act on any further rules.

    opened by AdrianChallinorOsiris 0
  • Fact checking before evaluating rules

    Fact checking before evaluating rules

    Hi,

    I am currently trying to use RuleBook to define a bidding system for bridge. It seems well suited for this - typically a bidding system has rules such as "If you have a no trump shape and 12-14 high card points, open One No Trump".

    While testing my rule book I noticed that I had, in the test, forgotten to define some facts that the RuleBook needed to evaluate the hand. For example, maybe I forgot to set the value of the high card points - then the RuleBook has insufficient information to return a result.

    Is there a standard way of checking that all required facts have been defined before a RuleBook call?

    Code example below (where I have forgotten to define a fact "openingSuit" which the RuleBook wants).

    class TestAcolBidRuleBook {
    
        private static RuleBook<Bid> acolBidRuleBook;
        private static final FactMap<Integer> acolBidFacts = new FactMap<>();
    
    
    
        @Test
        void testRules() {
    
            acolBidRuleBook = RuleBookBuilder.create(AcolBidRuleBook.class).withResultType(Bid.class)
                .withDefaultResult(Bid.NO_BID)
                .build();
    
            acolBidFacts.setValue("hcp", 12);
            acolBidFacts.setValue("maxSuitLen", 6);
            acolBidFacts.setValue("handType", HandType.NOTRUMP.getAsInt());
    
            acolBidRuleBook.run(acolBidFacts);
    
            Optional<Result<Bid>> bidResult = acolBidRuleBook.getResult();
            boolean bidPresent = bidResult.isPresent();
    
    
            acolBidRuleBook.getResult().ifPresent(result -> System.out.println("Hand would bid: " + result));
        }
    }
    
    enhancement question 
    opened by AlDante 2
Releases(v0.11)
  • v0.11(Dec 4, 2018)

    • Rules are made injectable using @Autowired with SpringAwareRuleBookRunner
    • ERROR_ON_FAILURE now throws meaningful exceptions
    • @RuleBean is now used to properly decorate Spring POJO rules
    Source code(tar.gz)
    Source code(zip)
  • v0.10(Apr 26, 2018)

    This release includes:

    • Enhanced support for Spring Boot Fat Jars
    • Additional logging in RuleAdapter
    • Fix for mapping facts to instance variables in POJO rules when NameValueReferableMap key is different than NameValueReferable name
    • Corrections to the README
    Source code(tar.gz)
    Source code(zip)
  • v0.9.1(Dec 16, 2017)

  • v0.9(Sep 30, 2017)

  • v0.8.1(Sep 17, 2017)

    • Fixed POJO Results - previously getResults() on RuleBookRunner always returned Optional true; now it only returns Optional true if a Result was set
    • Fixed Spring Boot Bug - when POJO Rules were packaged in a Spring Boot uber jar, RuleBookRunner would fail to scan rules in a package; this has been corrected
    Source code(tar.gz)
    Source code(zip)
  • v0.8(Aug 17, 2017)

    • RuleBooks using the RuleBookBuilder now must specify a default result immediately affter a result type is specified
    • A Rule can break the rule chain when a rule fails if RuleChainActionType.STOP_ON_FAILURE is specified when the Rule is created
    • Errors are no longer logged when a default result is not specified for a RuleBookRunner
    Source code(tar.gz)
    Source code(zip)
  • v0.7(Aug 7, 2017)

    • RuleBooks are made thread safe for POJO and DSL created RuleBooks
    • RuleBooks can now be safely used as singletons with Spring
    • RuleBook results are now unique per thread
    Source code(tar.gz)
    Source code(zip)
  • v0.6.2(May 23, 2017)

    • Result reads are now chained on POJO Rules - fixes Megabank example
    • Conditions omitted from POJO rules now cause actions to always fire (instead of never fire)
    Source code(tar.gz)
    Source code(zip)
  • v0.6.1(May 22, 2017)

    NameValueTypeConvertible is used in the lambda when() and then() functions. Previously, it was decorating the FactMap, but not delegating to its toString method. This meant when filtering facts via the using() method and using the System.out::println function reference, the String value of the fact was not being outputted (e.g. in the Home Loan example in the README).

    The bug was corrected by having NameValueTypeConvertible delegate its toString method to the map it decorates.

    Source code(tar.gz)
    Source code(zip)
  • v0.6(May 7, 2017)

    • added BigDecimal to NameValueTypeConvertibleMap
    • added Boolean to NameValueTypeConvertibleMap

    Now when() and then() methods in the DSL will be able to convert facts of an unspecified type easily to BigDecimal or Boolean types using the methods getBigDeciVal() and getBoolVal, respectively.

    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Apr 29, 2017)

    • Fixed a v0.5 bug that prevented rules from being defined via the defineRules() method in CoRRuleBook subclasses
    • Fixed a v0.5 bug where the @When annotated method in POJO rules evaluated to true without invoking the method
    Source code(tar.gz)
    Source code(zip)
  • v0.5(Apr 12, 2017)

    • Abstracted the model from the DSL expression language (for Rules and RuleBooks)
    • RuleBooks now handle the functionality of RuleBooks + DecisionBooks
    • Chaining is now done through RuleBooks instead of Rules, allowing for different rule chaining algorithms
    • Fluid DSL is more like a language, only allowing words to be used in a specified order
    • Interfaces are now used for all classes, allowing for Facts, RuleBooks, etc to be extended without modification
    • Facts are now supplied to RuleBooks via their run() method, decoupling RuleBooks from Facts
    • Spring support allows for scanning packages for Rule POJO objects (i.e. RuleBookRunner)
    • Old RuleBook Java DSL was maintained but deprecated
    Source code(tar.gz)
    Source code(zip)
  • v0.4(Mar 23, 2017)

    • Multiple then() methods can be chained for readability in the DSL - this also can now be done in POJO Rules
    • then() methods are now consumers; RuleState is no longer returned
    • stop() method was added; the rule chain is presumed to continue unless stop() is invoked
    • POJO Rules @Then methods can be void - in that case, the rule chain is presumed to continue
    • using() method was added to DSL to limit the number of rules supplied to a then() method
    • rules of different generic types can be added to a generic RuleBook/DecisionBook
    • Facts available to then() and when() methods are filtered based on the generic type of the rule
    • FactMap was extended to provide more convenience methods for accessing Fact values
    • FactMap, StandardRule, StandardDecision and RuleAdapter were refactored to use composition instead of inheritance

    BREAKING CHANGES

    then() methods in the DSL are now Consumers, so they can no longer return a value POJO Rules had NO breaking changes in this release

    Source code(tar.gz)
    Source code(zip)
  • v0.3.4(Mar 15, 2017)

    • Support for injection of Map, Set, and FactMap using @Given annotations was added
    • Support for POJO rules to inherit injections based on annotations on parent classes was added
    • RuleBookRunner was updated to recognize @Rule annotations on other included annotations (e.g. @RuleBean)
    • Maven Info was updated
    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Mar 8, 2017)

    defineRules() was called every time the run method in a RuleBook was invoked. This could lead to duplicate rules being added to the rule chain. defineRules() is now only called if no rules were previously added to the RuleBook.

    Note: It is still considered a best practice to only run a RuleBook once per instance.

    Source code(tar.gz)
    Source code(zip)
  • v0.3(Mar 4, 2017)

    • RuleBook was converted to a multi-project build

    • Artifact rulebook became rulebook-core

    • Artifact rulebook-spring was added, providing additional support for Spring

      • Spring specific annotation @RuleBean was created to simplify the creation of Spring enabled rules
      • RuleBookBean class was created to provide a RuleBook that can easily be wired by Spring
    • Bugs & Defects Corrected:

      • DecisionBook.withDefaultResult() method spelling was corrected (thanks, @wheezymustafa)
      • The given method execution on chained rules was moved from the RuleBook class to the Rules implementation(s)
        • Chained rules are now fed the prior rules' Fact(s) - before, this was handled by the RuleBook at the time rules were added
        • This allows rules to be specified before Facts are given - probably something you want when using Spring or other DI containers
    Source code(tar.gz)
    Source code(zip)
  • v0.2.3(Feb 26, 2017)

    Lists in v0.2.2 were previously only being injected from Facts that contained Strings. This updated corrects that bug and allows Lists annotated with @Given to be injected with all objects contained in Facts that match the generic type used by the annotated List.

    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Feb 24, 2017)

    Technical debt was cleaned up by improving the use of some Java8 features [Lambda & Optional]. Additional testing was added to verify ordering worked in POJO rules according to the order property in the @Rule annotation. Maven repo information was updated to conform better to accepted practices.

    Source code(tar.gz)
    Source code(zip)
  • v0.2(Feb 21, 2017)

    This minor release improves upon the existing RuleBook functionality by offering support for POJO rules. Now, rules can be defined as annotated POJOs in a package. A RuleBookRunner object scans the package and creates a RuleBook from the POJOs defined in the specified package.

    Source code(tar.gz)
    Source code(zip)
  • v0.1(Feb 13, 2017)

    The initial release provides a lambda enabled DSL for defining Rules inside RuleBooks. It also provides Decisions and DecisionBooks that can be used to define specialized Rules that have a Result.

    Source code(tar.gz)
    Source code(zip)
Owner
Delivered Technologies Labs
{Simplifying Software}
Delivered Technologies Labs
JSON Library for Java with a focus on providing a clean DSL

JSON Library for Java with a focus on providing a clean DSL

Vaishnav Anil 0 Jul 11, 2022
MapNeat is a JVM library written in Kotlin that provides an easy to use DSL (Domain Specific Language) for transforming JSON to JSON, XML to JSON, POJO to JSON in a declarative way.

MapNeat is a JVM library written in Kotlin that provides an easy to use DSL (Domain Specific Language) for transforming JSON to JSON, XML to JSON, POJ

Andrei Ciobanu 59 Sep 17, 2022
Vert.x jOOQ DSL

jOOQ.x - Vertx jOOQ DSL jooqx leverages the power of typesafe SQL from jOOQ DSL and running on SQL connection in a reactive and non-blocking of SQL dr

zero88 18 Nov 16, 2022
A modern and lightweight library for working with email addresses in Java

JMail A modern, fast, zero-dependency library for working with email addresses and performing email address validation in Java. Built for Java 8 and u

Rohan Nagar 67 Dec 22, 2022
A query language for JSON and a template engine to generate text output.

Josson & Jossons Josson is a query language for JSON. Jossons is a template engine to generate text output. Features and Capabilities of Josson Query

Octomix Software 16 Dec 14, 2022
A low-level lightweight JSON Database for java

Simple JsonDB JsonDB is a fast, light-weight JSON key-value storage engine for Java What is key-value storage? Installation Pre-Requisites: Java 1.8 M

Alexandru Stan 5 Nov 27, 2022
Java Concolic Unit Testing Engine

jCUTE The Java Concolic Unit Testing Engine (jCUTE) automatically generates unit tests for Java programs. Concolic execution combines randomized concr

Open Systems Laboratory 81 Nov 7, 2022
An Engine to run batch request with JSON based REST APIs

JsonBatch An Engine to run batch request with JSON based REST APIs Some usecase for this library: Provide a batch API to your REST API set. Quickly ro

Rey Pham 11 Jan 3, 2022
A simple java JSON deserializer that can convert a JSON into a java object in an easy way

JSavON A simple java JSON deserializer that can convert a JSON into a java object in an easy way. This library also provide a strong object convertion

null 0 Mar 18, 2022
✔️ Simple, pretty and powerful logger for android

Logger Simple, pretty and powerful logger for android Setup Download implementation 'com.orhanobut:logger:2.2.0' Initialize Logger.addLogAdapter(new A

Orhan Obut 13.5k Jan 5, 2023
Simple, efficient Excel to POJO library for Java

ZeroCell ZeroCell provides a simple API for loading data from Excel sheets into Plain Old Java Objects (POJOs) using annotations to map columns from a

Credit Data CRB 68 Dec 8, 2022
A simple java json configuration system built with gson.

Simple Configuration A simple json configuration system built with gson. Setup ?? Using Maven REPOSITORY <repositories> <repository> <id>j

Kacper Horbacz 2 Sep 24, 2022
A simple JAVA HTTP requests tool.

JAVA-HTTP Hello ?? I made this very simple tool to start studying HTTP requests in JAVA. You need JAVA 18 to be able to test it: Download JAVA Functio

Ghost 9 Oct 16, 2022
a simple rating component

rn-rating-component A Simple react-native rating component. Installation Install rn-rating-component and its dependeices with npm npm install --save

Yuva raghav 3 Aug 20, 2021
Simple LaTeX filter for OmegaT

Simple LaTeX filter plugin for OmegaT What is it? This plugin implements a subset of LaTeX syntax as OmegaT file filter. Translated content is expecte

Lev Abashkin 4 May 24, 2022
Weld, including integrations for Servlet containers and Java SE, examples and documentation

Weld Weld is the reference implementation of CDI: Contexts and Dependency Injection for the Java EE Platform which is the Java standard for dependency

Weld 345 Oct 28, 2022
A Java serialization/deserialization library to convert Java Objects into JSON and back

Gson Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to a

Google 21.7k Jan 8, 2023
A universal types-preserving Java serialization library that can convert arbitrary Java Objects into JSON and back

A universal types-preserving Java serialization library that can convert arbitrary Java Objects into JSON and back, with a transparent support of any kind of self-references and with a full Java 9 compatibility.

Andrey Mogilev 9 Dec 30, 2021
Java with functions is a small java tools and utils library.

Java with functions is a small java tools and utils library.

null 4 Oct 14, 2022