defining condititions

F

Florian Weber

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?

thanks a lot for any feedback!

ciao!
florian
 
R

Ryan Paul

my_set.add(NotNamedRule("orange").new)
my_set.add(HasPropertyRule("color").new)
my_set.add(OrRule(HasPropertyRule("size").new,
HasPropertyRule("weight").new))

that looks a lot like prolog.
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?

python has a first-order logic library called pylog that lets you do this
kind of thing easily. I'm unaware of any comparable library in ruby, but I
would really like to know if one exists.
 
M

Mark Hubbart

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, :eek:r, 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 :eek:r
@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]
 
F

Florian Weber

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

basically i wanna define aop pointcuts in ruby.. something like

execution("*.someMe*") && (this("SomeCl*") && !this("SomeClazz"))
 
R

Robert Klemme

Florian Weber said:
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"))

You won't be able to make this work as rule creation because of short
circuit evaluation of '&&' and '||'.
can anybody think of a nice way to do this?

You could do:

#!/#!/usr/bin/ruby

module Rule
def self.create(&b)
def b.===(obj)
call obj
end
b
end

def self.and(*rules)
def rules.===(obj)
all? {|r| r === obj}
end
rules
end

def self.or(*rules)
def rules.===(obj)
any? {|r| r === obj}
end
rules
end
end

r1 = Rule.create {|o| o.name != "orange" && o.color && (o.size ||
o.weight) }
r2 = Rule.create {|o| o.name == "orange" && o.color && (o.size ||
o.weight) }

require 'ostruct'
obj = OpenStruct.new
obj.name = "green"
obj.color = "yellow"
obj.size = 10

p r1 === obj
p Rule.and( r1, r2 ) === obj
p Rule.or( r1, r2 ) === obj

R#!/usr/bin/ruby

module Rule
def self.create(&b)
def b.===(obj)
call obj
end
b
end

def self.and(*rules)
def rules.===(obj)
all? {|r| r === obj}
end
rules
end

def self.or(*rules)
def rules.===(obj)
any? {|r| r === obj}
end
rules
end
end

r1 = Rule.create {|o| o.name != "orange" && o.color && (o.size ||
o.weight) }
r2 = Rule.create {|o| o.name == "orange" && o.color && (o.size ||
o.weight) }

# testing...
require 'ostruct'
obj = OpenStruct.new
obj.name = "green"
obj.color = "yellow"
obj.size = 10

p r1 === obj
p Rule.and( r1, r2 ) === obj
p Rule.or( r1, r2 ) === obj

Regards

robert
 
A

Ara.T.Howard

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?

thanks a lot for any feedback!

ciao!
florian

if you like dangerous code, you might try something __like__ this:

~ > cat a.rb
class Rule
def initialize rule; @rule = rule.to_str; end

def apply? obj
safe = $SAFE
thread = nil
begin
thread =
Thread.new(obj) do |obj|
$SAFE = 12
obj.instance_eval @rule
end
thread.join
ensure
$SAFE = safe
end
thread.value
end

class << self; alias [] new; end

module Obeyer
def obey?(*rules)
rules.flatten.select{|rule| not rule.apply?(self)}.empty? ? true : false
end
end
end



class Agent
include Rule::Obeyer
attr :color
attr :size
def initialize opts = {}
@color, @size = opts.values_at 'color', 'size'
end
end


agent = Agent.new 'color'=>'red', 'size'=>42


rules =
Rule[ 'color == "red" or color == "blue"' ],
Rule[ 'size == 42' ]

p(agent.obey?(rules)) # => true


rules =
Rule[ 'color == "purple"' ],
Rule[ 'size == 42' ]

p(agent.obey?(rules)) # => false


~ > ruby a.rb
true
false


-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
| URL :: http://www.ngdc.noaa.gov/stp/
| "640K ought to be enough for anybody." - Bill Gates, 1981
===============================================================================
 
N

nobu.nokada

Hi,

At Wed, 26 May 2004 06:28:44 +0900,
Ara.T.Howard wrote in [ruby-talk:101411]:
if you like dangerous code, you might try something __like__ this:

~ > cat a.rb
class Rule
def initialize rule; @rule = rule.to_str; end

def apply? obj
safe = $SAFE
thread = nil
begin
thread =
Thread.new(obj) do |obj|
$SAFE = 12
obj.instance_eval @rule
end
thread.join
ensure
$SAFE = safe
end
thread.value
end

You don't need save $SAFE, which is always thread local. And,
Proc also preserves its $SAFE value. Though this was an
unofficial feature in 1.6, guaranteed from 1.8.
 
J

Joel VanderWerf

Hi,

At Wed, 26 May 2004 08:13:25 +0900,
Joel VanderWerf wrote in [ruby-talk:101419]:
Hm. Didn't know that. Also, methods preserve $SAFE:

def foo; $SAFE=3; end
# => nil
$SAFE
# => 0
foo
# => 3
$SAFE
# => 0


Do you mean Method object?

$ ruby19 -ve 'def foo;$SAFE=3;$SAFE;end;p method:)foo).call;p $SAFE'
ruby 1.9.0 (2004-05-25) [i686-linux]
3
0

$ ruby19 -ve 'def foo;$SAFE=3;$SAFE;end;p foo;p $SAFE'
ruby 1.9.0 (2004-05-25) [i686-linux]
3
3

$ ruby18 -ve 'def foo;$SAFE=3;$SAFE;end;p foo;p $SAFE'
ruby 1.8.2 (2004-05-24) [i686-linux]
3
3

I was confused by irb:

$ irb
irb(main):001:0> def foo; $SAFE=3; end; foo; $SAFE
=> 3
irb(main):002:0> $SAFE
=> 0
irb(main):003:0> $SAFE = 3
=> 3
irb(main):002:0> $SAFE
=> 0

So it is really irb that is saving and restoring $SAFE around each input
line. Maybe this is because irb is wrapping the input into a Proc.
 
M

Martin DeMello

Florian Weber said:
can anybody think of a nice way to do this?

thanks a lot for any feedback!

Note that while || and && cannot be overloaded, | and & can.

martin
 
S

Sascha Doerdelmann

Florian Weber said:
my_set.rules = !name("orange") && hasProperty("color") &&
(hasProperty("size") || hasProperty("weight"))

Apply the composite pattern, see
http://c2.com/cgi/wiki?CompositePattern
or
http://unicoi.kennesaw.edu/~jbamford/csis4650/uml/GoF_Patterns/composite.htm

Forget about the addComponent, removeComponent messages. You need a
different way to build composite rules. As in the pattern you need an
abstract class for all components. Call it AbstractRule and build
subclasses for composed rules and concrete rules. Implement messages
like "and", "or", etc. in AbstractRule, each of them should return a
composed rule. Depending on your implementation of the composed rules
you might overwrite these messages in composed rules.

Cheers
Sascha
 
F

Francis Hwang

Guillaume Marcais said:
Kansas, written by Kirk Haines, has such a conditions system to write
SQL queries. You may want to check it out:

http://enigo.com/projects/kansas/index.html

Guillaume.

Other examples to check out would include:

- Criteria, for querying databases and other data stores:
http://mephle.org/Criteria/
- Lafcadio, an O-R mapping layer for MySQL:
http://lafcadio.rubyforge.org/
- Runt, for defining time-based rulesets for defining calendars
without explicitly choosing dates: http://runt.rubyforge.org/

Probably this sort of thing is easier if you have an object that's
explicitly the receiver of the various methods. So instead of what
you'd originally asked:

my_set.rules = !name("orange") && hasProperty("color") &&
(hasProperty("size") || hasProperty("weight"))

It might be easier to define something that looks like:

my_set.createRules { |set|
set.name('orange').not & set.hasProperty('color') &
( set.hasProperty('size') | set.hasProperty('weight')
}

Inside the block, the variable "set" is actually a special object that
pretends to be a set but really has funny methods defined to create
the various Rule primitives you've already defined. This is how I
implemented it in Lafcadio and it's worked well so far.

One other note: Beyond the fact that you can't override && and ||, you
also cannot override unary negation, so you get no control over what
!name('orange') means. That's why in the example code I wrote it calls
set.name('orange').not instead ... if you make it a method you can do
whatever's necessary.

Francis
 
B

Bill Atkins

How about just a simple proc object?

my_set.rules = proc { !name("orange") && hasProperty("color") &&
(hasProperty("size") || hasProperty("weight")) }

Then just do something like obj.instance_eval rules for whatever
object has the methods in your example.

Bill
 
A

Alex McHale

Wow. I liked that idea so much that I whipped together a script to
prove to myself it works. Damn do I love Ruby... This is of course
with just a single class, but it is easy to see the power in something
along these lines, being able to have a mixin module or parent class
which defines an interface, and children that implement different
methods for different rules.


class Test
attr_accessor :properties

def initialize default_properties=Hash.new
@properties = default_properties
end

def has_property? key
@properties.has_key? key
end

def property_is? key, value
has_property?(key) && @properties[key] == value
end

def evaluate_condition(closure = nil, &block)
return instance_eval(&closure) if closure.kind_of? Proc
return instance_eval(&block) if block_given?
nil
end
end

test1 = Test.new({'color' => 'red', 'shape' => 'apple', 'taste' =>
'yum', 'fans' => 'none'})
test2 = Test.new({'color' => 'black', 'shape' => 'car', 'engine' =>
'ass kickin', 'fans' => 'several'})
test3 = Test.new({'color' => 'white', 'shape' => 'ibook', 'engine' =>
'g3', 'fans' => 'none'})
test_objects = [test1, test2, test3]

car_test = proc {has_property?('engine') && property_is?('shape', 'car')}
apple_test = proc {property_is?('shape', 'apple')}
corporeal_test = proc {has_property?('shape')}

puts 'testing for a car...'
test_objects.each do |object| puts
object.evaluate_condition(car_test).inspect end

puts 'testing for an apple...'
test_objects.each do |object| puts
object.evaluate_condition(apple_test).inspect end

puts 'testing for corporeal objects...'
test_objects.each do |object| puts
object.evaluate_condition(corporeal_test).inspect end

puts 'block test...'
test_objects.each do |object| puts (object.evaluate_condition
{has_property?('color') && property_is?('fans', 'none')}).inspect end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,146
Messages
2,570,832
Members
47,374
Latest member
anuragag27

Latest Threads

Top