- An optional
Symbolthat is a unique identifier for the rule - An optional
Hashthat contains rule attributes (such as priority) - A variable number of
Arraysrepresenting patterns (the “left hand side”) - A block that will be executed if the rule is satisfied (the “right hand side”)
The following is an example of a rule using this syntax:
rule :hello, [Message, :m, method.status == :HELLO] do |context|
puts context[:m].message
end
This rule defines a pattern that looks for the existence of a Message object with a status property that is equal to :HELLO. The block parameter will be executed for each object that this pattern matches.
The Left Hand Side
The most important part of this example is the content of the Array parameter, which represents the left hand side of the rule. Each Array begins with a class type, and is followed by an optional handle for the object (so that it can be referenced in the block). Next, the Array contains any number of conditions that must be true in order for the pattern to match. The example above contains only one condition:
method.status == :HELLO
The method keyword is used to identify that we are checking the value of the status method. However, this is the explicit form. The condition above could be shortened to:
m.status == :HELLO
There are several shortcuts like this one in Ruleby. The reason for having both the long and short versions is give the developer the ability to make the condition more human readable.
Bindings
In the example above, we could have bound our status value to a variable name. To do this we create a Hash with our condition as the key, and the variable name as the value:
{ m.status == :HELLO => :s }
This allows the :s variable to be referenced later on in the rule.
Conditions
There are a number of ways to write conditions in Ferrari. The example above is the simplest as it uses the== operator. Ferrari supports the following operators:
==andnot==<and<=>and>=
The not== operator is necessary because overriding the != operator in Ruby is not allowed. All of these are operators are ‘shortcuts’ for conditions. If we have a condition that is more complicated, we can pass a block parameter.
m.status( &condition{ |s| s + ‘my_suffix’ = @some_var } )
The only requirement is that the block must evaluate to true or false. The condition method is really just an alias for the ‘lambda’ method in Kernel. But the developer can shorten this to just the c method if desired (this is demonstrated below).
We can also reference a bound variable:
m.status == binding(:some_var)
In this example, we use the binding method to reference the variable we bound to the :name symbol. The short hand version of this is:
m.status == b(:some_var)
Or we can do both:
m.status( :some_var, &c{ |s,v| v == s + 'my_suffix' } )
The Right Hand Side
The block parameter to the rule method is less complicated that the left hand side. It is simply a block of Ruby code that will be executed each time the rule is matched. This block has two parameters:- engine – a handle to the Engine object (used for asserting and retracting facts)
- context – a Hash of bound variables that can be used in the block.
Of course, these parameters can be named whatever the developer desires to call them.
Rule Attributes
The Hash that contains the rule attributes typically looks something like the following example:
{ :priority => 5 }
At this time, the only rule attribute that Ruleby supports is :priority (a.k.a. ‘salience’).
Quantifiers
The class value in the Array (left hand side parameter) can optionally be preceded by a quantifier. Valid quantifiers are as follows:
- :~
- :not
- :exists
The :~ and :not quantifiers are semantically the same. They are existential quantifiers that check for the non existence of a fact in working memory.
The :exists quantifier is an existential quantifier that checks for the existence of something in working memory. With this qualifier, the rule’s action will only execute once regardless of how many facts in working memory match the pattern.
In no quantifier is specified, the default action is for the rule to execute the right hand side for every fact in working memory that matches the pattern.


RSS feeds