Ruby & Rules Engines

Ruby & Rules Engines

Take a look at Treetop. You can define your DSL as a Parsing Expression Grammar and then parse it to create your rules in whatever format you like.

Using Rules Engine

You can do it with nested iterations to the engine.

engine.each "IncidentA", "requires" :_  do |requires|
engine.each :_, "offers", requires.object do |offers|
puts "IncidentA can get #{requires.object} from #{offers.subject}"
end
end

To do it as a rule...

suppliers = engine.rule "service suppliers" do
forall {
has :Company, "offers", :Service
has :Incident, "requires", :Service
}
end

Then using the rule, select for the type of incident and iterate for the information.

suppliers.tokens.select{|s| s[:Incident] == "IncidentA" }.each do |s|
puts "#{s[:Incident} can get #{s[:Service]} from #{s[:Company]}"
end

To do it as a query...

q = engine.query "companies" do
search_on :Incident
forall {
has :Company, "offers", :Service
has :Incident, "requires", :Service
}
end

engine.execute "companies", { Incident: "IncidentA" }
q.tokens.each do |s|
puts "#{s[:Incident} can get #{s[:Service]} from #{s[:Company]}"
end

EDIT adding notes for your question about populating facts...

Assuming you have Company with an attribute name and Incident with an attribute name and Service with an attribute name

Company.joins(:services).select("companies.*, services.name as service_name").each do |company|
engine << [company.name, "offers", company.service_name]
end

Incident.joins(:services).select("incidents.*, services.name as service_name").each do |incident|
engine << [incident.name, "requires", incident.service_name]
end

Dynamic business rules engine for ruby on rails

If you're sure a rule engine is what you need, you will need to find one you can use in Ruby. A quick Google search brought up Rools (http://rools.rubyforge.org/) and Ruby Rules (http://xircles.codehaus.org/projects/ruby-rules). I'm not sure of the status of either project though. Using JRuby with Drools might be your best bet but then again, I'm a Java developer and a big Drools advocate. :)

Without knowing all the details, it's a little hard to say how that should be implemented. It also depends on how you want the rules to be updated. One approach is to write a collection of rules similar to this: "if a store exists with more than 50 sales people and the store hasn't had its weight updated to reflect that, then update the store's weight." However, in some way you could compare that to hardcoding.

A better approach might be to create Weight objects with criteria that need to be met for the weight to apply. Then you could write one rule that matches on both Weights and Stores: "if a Store exists that matches a Weight's criteria and the Store doesn't already have that Weight assigned to it, then add the Weigh to the Store." Then the business folks could just create and update Weights, possibly in a web front-ended database, instead of maintaining rules.

Design patterns/advise on building a Rule engine

In a commercial rules engine e.g. Drools, FlexRule... the pattern matching is handled by RETE algorithm. And also, some of them provide multiple different engines for different logic e.g. procedural, validation, inference, flow, workflow,... and they also provide DSL customization...
Rule sequencing and execution is handled based on agenda and activation that can be defined on the engine. And conflict resolution strategy would help you to find proper activation to fire.

I recommend you to use a commercial product hosting on a host/service. And use simple Json/Xml format to communicate to the rule server and execute your rules. This will be giving you a better result probably than creating your own one. However if you are interested in creating your own one as a pattern matching engine consider RETE algorithm, agenda and activation mechanisms for complex production system.

In RETE algorithm you may consider at least implementing Positive and Negative conditions. In implementing RETE you need to implement beta and alpha memories as well ad join nodes that supports left and right activations.

Specifying and Executing Rules in Ruby

I don't think it would be a major thing to write something yourself to do this, I don't know of any gems which would do this (but it would be good if someone wrote one!)

I would tackle the project in the following way, the way I am thinking is that you don't want to do the rule matching at the point the user saves as it may take a while and could interrupt the user experience and/or slow up the server, so...

  1. Use observers to store a record each time a CRUD event happens, or to make things simpler use the Acts as Audited gem which does this for you.

1.5. Use a rake task, running from your crontab to run through the latest changes, perhaps every minute, or you could use Resque which does a good job of handling lots of jobs


  1. Create a set of tables which define the possible rules a user could select from, perhaps something like

Table: Rule

Name

ForEvent (eg. CRUD)

TableInQuestion

  • FieldOneName

  • FieldOneCondition etc.

MethodToExecute

You can use a bit of metaprogramming to execute your method and since your method knows your table name and record id then this can be picked up.

Additional Notes

The best way to get going with this is to start simple then work upwards. To get the simple version working first I'd do the following ...

  1. Install acts as audited
  2. Add an additional field to the created audit table, :when_processed
  3. Create yourself a module in your /lib folder called something like processrules which roughly does this

    3.1 Grabs all unprocessed audit entries
    3.2 Marks them as processed (perhaps make another small audit table at this point to record events happening)

  4. Now create a rules table which simply has a name and condition statement, perhaps add a few sample ones to get going

    Name: First | Rule Statement: 'SELECT 1 WHERE table.value = something'

  5. Adapt your new processrules method to execute that sql for each changed entry (perhaps you want to restrict it to just the tables you are working with)

  6. If the rule matched, add it to your log file.

From here you can extrapolate out the additional functionality you need and perhaps ask another question about the metaprogramaming side of dynamically calling methods as this question is quite broad, am more than happy to help further.

I tend to think the best way to go about task processing is to setup the process nicely first so it will work with any server load and situation then plug in the custom bits.



Related Topics



Leave a reply



Submit