Steve Robinson Steve Robinson - 6 months ago 24
Ruby Question

Using Rules Engine

I have the following situation -

I have

Company
and I have
Incident
.

A
Company
offers many services (
Service
) and hence -
has_and_belongs_to_many :services
.

An
Incident
needs some services (
Service
) and hence -
has_and_belongs_to_many :services


Now for a given incident I need to find all the companies that offer the services I need. I am looking to use a rules engine to do this because I have a lot more complex conditions to apply to pick the right set of companies.

I looked into this gem called -
Wongi-engine
(github.com/ulfurinn/wongi-engine), but its too hard to wrap my head around it and write rules.

Can you provide any pointers for me? Thanks!

I can come up with following
facts
:

engine << [ "CompanyA", "offers", "serviceA" ]
engine << [ "CompanyB", "offers", "serviceB" ]
engine << [ "CompanyA", "offers", "serviceB" ]
engine << [ "CompanyB", "offers", "serviceC" ]
engine << [ "IncidentA", "requires", "serviceC" ]
engine << [ "IncidentA", "requires", "serviceA" ]


EDIT:
Also once I write these rules, how do I use Rails models in place of these strings? And are there any best practices for loading the facts into the engine in the context of a Ruby on Rails application?

My usecase is, user selects an incident and asks for companies that can service the incident. And I need to ask the Rules engine for a list of companies that can do the trick :)

Answer

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