On May 25, 2004, at 8:04 AM, Florian Weber wrote:

> hi!
>
> im trying to define a set of rules with ruby, however i cant find a 
> more
> ruby-like way to do so..
>
> instead of doing something like
>
> my_set.add(NotNamedRule("orange").new)
> my_set.add(HasPropertyRule("color").new)
> my_set.add(OrRule(HasPropertyRule("size").new, 
> HasPropertyRule("weight").new))
>
> (i know this is a horrible example. excuse the awful 'design'. its 
> justs to illustrate
> what i not wanna have ; )
>
> i wanna do something like
>
> my_set.rules = !name("orange") && hasProperty("color") && 
> (hasProperty("size") || hasProperty("weight"))
>
> can anybody think of a nice way to do this?

Assuming you have a specific class you created that needs to be 
compared to a set of rules, here's one way:

- Create a class, Rule. Implement === in it, so you can compare members 
of your class to it as a test.
- Create a class, Ruleset. It acts like a rule, compares using ===. You 
can create Ruleset instances by using the bitwise operators on Rule 
instances.

A simplistic example:
----
# file: dogrules.rb

class Dog
   attr_reader :age, :breed, :name
   def initialize(breed, age, name)
     @breed, @age, @name = breed, age, name
   end
end

class Rule
   def initialize(rulename, *args)
     @rulename, @args = rulename, args
   end

   def ===(other)
     if other.kind_of? Dog
       case @rulename
       when :breed_is
         @args[0] == other.breed
       when :age_is
         @args[0] == other.age
       when :name_is_one_of
         @args.any?{|a| a == other.name }
       end
     end
   end
   # compare with or: rule1 | rule2
   def |(other)
     Ruleset.new(self, :or, other)
   end
   # compare with and: rule1 & rule2
   def &(other)
     Ruleset.new(self, :and, other)
   end
   # negate this rule: rule1.not
   def not
     Ruleset.new(self, :not)
   end
end

class Ruleset < Rule
   def initialize(one, op, two = nil)
     @one, @op, @two = one, op, two
   end

   def ===(other)
     case @op
     when :or
       @one === other or @two === other
     when :and
       @one === other and @two === other
     when :not
       not @one === other
     end
   end
end


dog1 = Dog.new("black lab", 3, "Rufus")
dog2 = Dog.new("beagle",    2, "Bowser")
dog3 = Dog.new("black lab", 4, "Blackie")

rule1 = Rule.new(:breed_is, "black lab")
rule2 = Rule.new(:name_is_one_of, "Bowser", "Blackie")

[dog1, dog2, dog3].map{|dog| rule1 === dog }
     #=> [true, false, true]
[dog1, dog2, dog3].map{|dog| rule2 === dog }
     #=> [false, true, true]
[dog1, dog2, dog3].map{|dog| (rule1 & rule2) === dog }
     #=> [false, false, true]

----

HTH,
Mark