require "spec_helper" describe "rules" do context "functionality" do before :all do class Person attr_accessor :name, :age, :job include Rulz def initialize(name, age, job = nil) @name = name @age = age @job = job end define_rulz do condition "older than" do |age| its.age > age end condition "younger than" do |age| opposite_of "older than", age end condition "old" do condition "older than", 50 end condition "elderly" do condition "older than", 80 end condition "name is" do |name| its.name == name end condition "job is" do |job| its.job == job end action "add prefix" do |prefix| its.name = "#{prefix} #{its.name}" end action "add suffix" do |suffix| its.name += " #{suffix}" end action "add job prefix" do |prefix| its.job = "#{prefix} #{its.job}" end rule do where("old") { apply! "add prefix", "Grandpa" } end rule do where("elderly") { apply! "add suffix", "Sr."} end rule do where("younger than", 20) { apply! "add suffix", "Jr."} end rule do where(["name is", "Prometheous", "OR", "name is", "Wisdom"]) { apply! "add suffix", "the wise" } end rule do where(["job is", "Contractor", "AND", "younger than", 40]) { apply! "add job prefix", "Jr."} end rule do where ["job is", "Electrician", "OR", "name is", "Bobby", "AND", "job is", "Contractor"] do apply! "add prefix", "The skilled" end end end end end it "should apply when the condition is true" do bobby = Person.new("Bobby", 52) bobby.apply_rules! bobby.name.should == "Grandpa Bobby" end it "should not apply when the condition is false" do bobby = Person.new("Bobby", 32) bobby.apply_rules! bobby.name.should == "Bobby" end it "should accept conditions with arguments" do bobby = Person.new("Bobby", 17) bobby.apply_rules! bobby.name.should == "Bobby Jr." end it "should be combined to produce a combined effect" do bobby = Person.new("Bobby", 87) bobby.apply_rules! bobby.name.should == "Grandpa Bobby Sr." end it "accepts 'OR' syntax" do wisdom = Person.new("Wisdom", 32) wisdom.apply_rules! wisdom.name.should == "Wisdom the wise" prometheous = Person.new("Prometheous", 32) prometheous.apply_rules! prometheous.name.should == "Prometheous the wise" end it "accepts 'AND' syntax" do worker = Person.new("Worker", 32, "Contractor") worker.apply_rules! worker.job.should == "Jr. Contractor" worker = Person.new("Worker", 32, "Builder") worker.apply_rules! worker.job.should == "Builder" end it "accepts both 'AND' and 'OR' syntax together" do electrician = Person.new("Electrician", 32, "Electrician") electrician.apply_rules! electrician.name.should == "The skilled Electrician" end end context "errors" do before :all do class Cactus include Rulz attr_accessor :name, :age def initialize(name, age) @name = name @age = age end define_rulz do condition "older than" do |age| its.age > age end condition "younger than" do |age| opposite_of "older than", age end condition "middle aged" do condition("older than", 40) and condition("younger than", 50) end rule do where("old") { apply! "add prefix", "Grandpa" } # "old" does not exist end end end end it "should be raised if a condition is not found" do bobby = Cactus.new("Bobby", 42) expect { bobby.apply_rules! }.to raise_error ArgumentError end end context "attributes" do before :all do class Watermelon include Rulz attr_accessor :length, :weight, :width def initialize(length, weight, width=0) @length = length @weight = weight @width = width end define_rulz do attribute :length do condition "greater than" do |other_length| length > other_length end condition "less than" do |other_length| length < other_length end end attribute :weight do condition "greater than" do |other_weight| weight > other_weight end end condition "lopsided" do its.width > its.length end action "cut in half" do its.length /= 2 end action "scoop out inside" do action "cut in half" its.weight /= 3 end action "rotate" do its.length, its.width = its.width, its.length end rule do where :length => [ "greater than", 12 ] do apply! "cut in half" end end rule do where [{:length => [ "less than", 6]}, "AND", {:weight => ["greater than", 15]}] do apply! "scoop out inside" end end rule do where [{:length => ["less than", 6]}, "AND", "lopsided"] do apply! "rotate" end end end end end it "should recognize attribute conditions" do w = Watermelon.new(13, 11) w.apply_rules! w.length.should == 6 m = Watermelon.new(11, 11) m.apply_rules! m.length.should == 11 end it "should allow attribute conditions to be combined" do w = Watermelon.new(4, 18) w.apply_rules! w.length.should == 2 w.weight.should == 6 end it "should allow attribute conditions to be combined with normal conditions" do w = Watermelon.new(4, 4, 6) w.apply_rules! w.length.should == 6 w.width.should == 4 end end context "using predefined conditions" do class Honeydew include Rulz attr_accessor :diameter define_rulz do attribute :diameter do type :integer end action "cut in half" do its.diameter /= 2 end rule do where(diameter: ["greater than", 5]) { apply! "cut in half" } end end def initialize(diameter) @diameter = diameter end end it "should not require conidions to be defined" do h = Honeydew.new(6) h.apply_rules! h.diameter.should == 3 end end end