Permalink
Browse files

numerous AND/OR bug fixes, and new specs

  • Loading branch information...
jkutner committed Jun 24, 2011
1 parent 45e3558 commit 7c70691ad926017237e8f23e8303f3e3864fa450
Showing with 227 additions and 82 deletions.
  1. +65 −81 lib/dsl/ferrari.rb
  2. +161 −0 spec/and_or_spec.rb
  3. +1 −1 tests/or_patterns.rb
View
@@ -52,82 +52,26 @@ def self.parse_containers(args, container=Container(:and), parent=nil)
return container
end
class RulesContainer < Array
def transform_or(parent)
ors = []
others = []
permutations = 1
index = 0
parent.each do |child|
if(child.or?)
permutations *= child.size
ors << child
else
others[index] = child
end
index = index + 1
end
# set parent type to or and clear
parent.kind = :or
parent.clear
indexes = []
# initialize indexes
ors.each do |o|
indexes << 0
end
# create children
(1.upto(permutations)).each do |i|
and_container = Container.new(:and)
mod = 1
(ors.size - 1).downto(0) do |j|
and_container.insert(0,ors[j][indexes[j]])
if((i % mod) == 0)
indexes[j] = (indexes[j] + 1) % ors[j].size
end
mod *= ors[j].size
end
others.each_with_index do |other, k|
if others[k] != nil
and_container.insert(k, others[k])
end
end
# add child to parent
parent.push(and_container)
end
parent.uniq!
end
def handle_branching(container)
class RulesContainer < Array
def handle_branching
ands = []
container.each do |x|
if x.or?
x.each do |branch|
ands << branch
each do |x|
f = x.flatten_patterns
if f.or?
f.each do |o|
ands << o
end
elsif x.and?
ands << x
else
new_and = Container.new(:and)
new_and << x
ands << new_and
end
ands << f
end
end
return ands
ands
end
def build(name,options,engine,&block)
rules = []
self.each do |x|
x.process_tree do |c|
transform_or(c)
end
def build(name, options, engine, &block)
handle_branching.map do |container|
build_rule(name, container, options, &block)
end
handle_branching(self).each do |a|
rules << build_rule(name, a, options, &block)
end
return rules
end
def build_rule(name, container, options, &block)
@@ -143,8 +87,54 @@ def build_rule(name, container, options, &block)
class Container < Array
attr_accessor :kind
def initialize(kind)
def initialize(kind, *vals)
@kind = kind
self.push(*vals)
end
def flatten_patterns
if or?
patterns = []
each do |c|
f = c.flatten_patterns
if f.and?
patterns << f
else
f.each do |o|
# i hope this is safe... not entirely sure
patterns << (o.size == 1 ? o.first : o)
# patterns << o
end
end
end
Container.new(:or, *patterns)
elsif and?
patterns = []
or_patterns = []
each do |c|
child_patterns = c.flatten_patterns
if child_patterns.or? and child_patterns.size > 1
child_patterns.each do |o|
or_patterns << o
end
else
patterns.push(*child_patterns)
end
end
if or_patterns.empty?
flat = Container.new(:and)
flat.push(*patterns)
else
flat = Container.new(:or)
or_patterns.each do |op|
c = Container.new(:and)
c.push(op, *patterns)
flat << c
end
end
return flat
end
end
def build(builder)
@@ -165,23 +155,17 @@ def or?
def and?
return kind == :and
end
def process_tree(&block)
has_or_child = false
uniq!
each do |c|
has_or_child = true if (c.process_tree(&block) or c.or?)
end
yield(self) if (has_or_child)
return has_or_child
end
end
class PatternContainer
def initialize(condition)
@condition = condition
end
def flatten_patterns
Container.new(:and, self)
end
def build(builder)
builder.when(*@condition)
end
View
@@ -0,0 +1,161 @@
require 'spec_helper'
class AndOrFact
attr :value, true
def initialize(v=nil); @value = v; end
end
class AndOrFact2
attr :value, true
def initialize(v=nil); @value = v; end
end
include Ruleby
class AndOrRulebook < Rulebook
def rules
rule AND(
OR([AndOrFact, m.value > 0]),
OR(
OR([AndOrFact, m.value == 1]),
AND(
AND([AndOrFact, m.value < 1]),
OR([AndOrFact, m.value == nil], [:not, AndOrFact])))) do
assert Success.new
end
# rule [AndOrFact, m.value > 0],
# OR(
# [AndOrFact, m.value == 1],
# AND(
# [AndOrFact, m.value < 1],
# OR([AndOrFact, m.value == nil], [:not, AndOrFact]))) do
# assert Success.new
# end
end
def rules2
rule OR(AND(OR(OR([AndOrFact, m.value == 1])))) do |v|
assert Success.new
end
end
def rules3
rule OR([AndOrFact, m.value == 1], [AndOrFact, m.value == 2], [AndOrFact, m.value == 3]), [AndOrFact, m.value == 4] do |v|
assert Success.new
end
end
def rules4
rule AND([AndOrFact, :a, m.value == 1], [AndOrFact2, :a2, m.value == 2]) do |v|
raise "nil" if v[:a].nil?
raise "nil" if v[:a2].nil?
assert Success.new
end
end
def rules5
rule OR(AND([AndOrFact, :a, {m.value == 1 => :x}], [AndOrFact2, m.value == b(:x)])) do |v|
assert Success.new
end
end
end
describe Ruleby::Core::Engine do
# describe "AND/OR" do
context "crazy AND/OR rules" do
subject do
engine :engine do |e|
AndOrRulebook.new(e).rules
end
end
before do
subject.assert AndOrFact.new(1)
subject.match
end
it "should have matched" do
subject.errors.should == []
subject.retrieve(Success).size.should == 1
end
end
context "nested AND/OR rule" do
subject do
engine :engine do |e|
AndOrRulebook.new(e).rules2
end
end
before do
subject.assert AndOrFact.new(1)
subject.match
end
it "should have matched" do
subject.errors.should == []
subject.retrieve(Success).size.should == 1
end
end
context "multi OR rule" do
subject do
engine :engine do |e|
AndOrRulebook.new(e).rules3
end
end
context "with one 1 and one 4" do
before do
subject.assert AndOrFact.new(1)
subject.assert AndOrFact.new(4)
subject.match
end
it "should have matched" do
subject.errors.should == []
subject.retrieve(Success).size.should == 1
end
end
end
context "nested AND/OR rule" do
subject do
engine :engine do |e|
AndOrRulebook.new(e).rules4
end
end
before do
subject.assert AndOrFact.new(1)
subject.assert AndOrFact2.new(2)
subject.match
end
it "should have matched" do
subject.errors.should == []
subject.retrieve(Success).size.should == 1
end
end
context "nested AND/OR rule" do
subject do
engine :engine do |e|
AndOrRulebook.new(e).rules5
end
end
before do
subject.assert AndOrFact.new(1)
subject.assert AndOrFact2.new(1)
subject.match
end
it "should have matched" do
subject.errors.should == []
subject.retrieve(Success).size.should == 1
end
end
# end
end
View
@@ -131,7 +131,7 @@ def test_0
assert_equal 0, ctx.get(:rule19)
assert_equal 1, ctx.get(:rule18)
assert_equal 2, ctx.get(:rule17)
assert_equal 2, ctx.get(:rule16)
assert_equal 2, ctx.get(:rule16)
assert_equal 1, ctx.get(:rule14b)
assert_equal 1, ctx.get(:rule14a)
assert_equal 1, ctx.get(:rule13b)

0 comments on commit 7c70691

Please sign in to comment.