Build Status Code Climate Gem Version

rulezilla

This provides a DSL to implement rules for various tasks. In the current version we are still relying on user to have a certain level of Ruby knowledge in order to be able to use this DSL. The ultimate goal is for people without prior Ruby knowledge to be able to change and even write the Rule.

Installation

Using Gemfile

Add the following line to your Gemfile:

gem 'rulezilla'

Then run:

bundle install

Without Gemfile

On your command line run the following:

gem install 'rulezilla'

Usage

Rules

Rules can be defined either using Gherkin or pure Ruby. In either case, rules are classes that include the Rulezilla::DSL.

Gherkin (Beta)

Note: Currently, rulezilla Gherkin has only very limited support.

Rules are defined inside .feature files which should be organized under a specific directory. In order to be able to use these rules, you need to first set the path that rulezilla can use in order to load them.

Rulezilla.gherkin_rules_path = 'absolute path to folder holding your feature files'

Rulezilla will load all the feature files and for each one will create a rule class. The filename will be used to build the name of the rule class. For example, the file with name invalid_number_rule.feature will generate rule class Rulezilla::InvalidNumberRule.

We currently support a very limited type of steps. Please refer to:

True / False

Duration

Ruby

You can use plain Ruby to define the rule classes. But you will need to include the Rulezilla::DSL module. That will give you access to the DSL used to define rules.

Here is an example:

class RoboticsRule
  include Rulezilla::DSL

  group :may_not_injure_human do
    condition { not_injure_human? }

    group :obey_human do
      condition { do_as_human_told? }

      define :protect_its_own_existence do
        condition { protect_itself? }
        result(true)
      end
    end
  end

  default(false)

end

Please refer to the feature for further details of the DSL.

Support Module

The support module will be automatically included if its name is "#{rule_class_name}Support"

e.g. if the rule class name is RoboticsRule, then the support would be RoboticsRuleSupport

module RoboticsRuleSupport
  def protect_itself?
    in_danger? && not_letting_itself_be_detroyed?
  end
end

How to execute the rule

If the entity is:

entity = {
  not_injure_human?:               true,
  do_as_human_told?:               true,
  in_danger?:                      true,
  not_letting_itself_be_detroyed?: true
}

To get the first matching result output

RoboticsRule.apply(entity) #=> true

To get all matching result outputs

RoboticsRule.all(entity) #=> [true, false]

Note that false is the result outcome coming out from default(false) on top level, which is also called root node. The root node does not have any condition and hence it is considered to be matching. This means, by consequence, that its result (default(false)) is included in the list of matching result outputs which #all(entity) above returns.

To get the trace of all nodes

RoboticsRule.trace(entity)
#=> all the nodes instance: [root, may_not_injure_human, obey_human, protect_its_own_existence] in sequence order.

To get all results from the Rule

RoboticsRule.results #=> [true, false]

Syntax

Please refer to the features for DSL syntax:

DSL Feature,

Default Support Methods Feature