Defining RuleBooks

Write a RuleBook class, run it, and read the result.

On this page

Defining RuleBooks

A RuleBook is a class that holds a list of rules. Extend rulebox.models.RuleBook and add your rules in a defineRules() method.

A HelloWorld example

class extends="rulebox.models.RuleBook"{

	function defineRules(){
		addRule(
			newRule( "SayHello" )
				.then( ( facts, result ) => result.setValue( "Hello " & facts.name ) )
		)
	}

}

newRule() takes an optional name, which you can use to identify the rule later, for auditing. Now run it:

var greeting = getInstance( "HelloWorld" )
	.run( { name : "World" } )
	.getResult()
	.getValue()
// "Hello World"

getInstance( "HelloWorld" ) finds the class in your app's models folder by name. Use the full path (path.to.HelloWorld) for a class that lives somewhere else.

Passing facts

run( facts ) takes a struct of facts directly. It is a shortcut for calling given() once per key and then run(). Facts you pass replace any fact of the same name; pass false as the second argument (run( facts, false )) to keep the ones already set. See Facts & Results.

Reading the outcome

run() returns the RuleBook itself, not the answer. That keeps calls chainable. To get the answer, chain .getResult().getValue() after run(), as the example above does. If you are not sure any rule set a value, check getResult().isPresent() first. More in Facts & Results.

Why rules are defined on the first run

defineRules() does not run when the RuleBook is created. It runs the first time you call run() (or dryRun()), and only when the RuleBook has no rules yet. By then WireBox has finished injecting your dependencies, so you can use them inside defineRules(). Later runs on the same instance reuse the rules instead of building them again.

Other ways to add a rule

You can also pass a closure to addRule() and configure the rule inside it:

addRule( ( rule ) => {
	rule
		.setName( "SayHello" )
		.then( ( facts, result ) => result.setValue( "Hello " & facts.name ) )
} )

A RuleBook has a name too, which you can set with setName( name ). It is handy for auditing and the Visualizer. You can also build rulebooks on the fly, with no class at all, using The Builder.

Rule names must be unique within a RuleBook, because the audit trail is keyed by name. Adding a rule whose name is already taken throws a RuleBox.DuplicateRuleNameException; unnamed rules are never affected.

Describing a RuleBook

Give a RuleBook a description to say what it decides. It is shown in the Rule Visualizer and returned by getDescription(), and it has no effect on how the rulebook runs. The simplest way is the class's docblock:

/**
 * Decides a loan application from the applicant's credit score.
 */
class extends="rulebox.models.RuleBook"{
	// ...
}

getDescription() looks in this order and uses the first one it finds:

  1. a description set with withDescription( text ), a rule file or config
  2. the class's @description annotation, then its @hint annotation
  3. the class's docblock, with its line breaks folded into spaces
@description( "Decides a loan application from the applicant's credit score" )
class extends="rulebox.models.RuleBook"{
}

// Or on any instance, including one from the Builder
getInstance( "LoanApproval" ).withDescription( "Decides loans for the spring promotion" )

A plain RuleBook, such as one from the Builder, has no class hint, so its description is empty until you set one. Rules can be described too, with withDescription().

Retrieving a RuleBook

A RuleBook class is a normal WireBox-mapped class, so you retrieve it like any other transient:

var homeLoans = getInstance( "HomeLoanRateRuleBook" )

RuleBooks are transient: every getInstance() call returns a new one, and each one remembers its own facts. Ask for a new RuleBook each time you need one, and do not share one between requests or threads. See Thread Safety.

Next

Edit this page Download Markdown Last updated Oct 13, 2026, 8:42:39 PM