[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Need a ruby approach

Fredrik Jagenheim

9/9/2003 8:59:00 AM

Hi,

First a little warning, this post is more of a rubberducking session
than a proper question.

I have a rather trivial problem, but since I'm spending 8 hours a day
cursing C++ and far coding in Ruby, I'm afraid I can't think of a
proper Ruby solution, and instead come up with 'this is how it
could've been done in C++'-solutions.

The problem: I have a data set. I'd like to see if this data set
fullfills certain properties, so I did a couple of classes which will
do the matching against these properties:

def MatchA
def MatchA.match?(set)
# Complex calculations on set
true
end
end

def MatchB
def MatchB.match(set)
# More complex calculations on set
true
end
end

Then I'd just do:
puts "It matched A" if MatchA.match?(set)
puts "It matched B" if MatchB.match?(set)

Ok, but now I have 50 of these match classes, so I figured I should
make an array of them:

array_of_match_objects.each{ |match|
puts "It matched #{match.name}" if match.match(set)
}

I would have to change the methods from class to object first, I
guess...

Anyway, then I started to think about using a superclass for these
classes and use Ruby introspection to find all the subclasses and call
match from there. That's when I realized there must be simpler
solution I just can't see...

Perhaps extending the set-class with the methods;

def MatchB
def match_b?
# Complex calculation
end
end

set.extend(MatchB)

puts "Matched B" if set.match_b?

It probably be the best way OO-wise, since it's a property of the Set.

Or am I really wrong here?

//F


8 Answers

Martin DeMello

9/9/2003 9:15:00 AM

0

Fredrik Jagenheim <fredde@pobox.com> wrote:

> Anyway, then I started to think about using a superclass for these
> classes and use Ruby introspection to find all the subclasses and call
> match from there. That''s when I realized there must be simpler
> solution I just can''t see...

Here''s one way (untested)

class SetMatcher
def initialize
@tests = []
end

def add(&test)
@tests << test
end

def match?(set)
@tests.all? {|test| test.call(set)}
end
end

m = SetMatcher.new
m.add {|set|
#complex test
}

m.add {|set|
#complex test
}

s = DataSet.new(datasource)
if m.match?(s)
...
end

#---------------------------------------------------------

You could also rewrite it slightly to be a module mixed into your set
class, in which case match? wouldn''t need a parameter, as wouldn''t the
matching procs.

martin

Robert Klemme

9/9/2003 9:59:00 AM

0



"Fredrik Jagenheim" <fredde@pobox.com> schrieb im Newsbeitrag
news:20030909085851.GB28065@pobox.com...
> Hi,
>
> First a little warning, this post is more of a rubberducking session
> than a proper question.
>
> I have a rather trivial problem, but since I''m spending 8 hours a day
> cursing C++ and far coding in Ruby, I''m afraid I can''t think of a
> proper Ruby solution, and instead come up with ''this is how it
> could''ve been done in C++''-solutions.
>
> The problem: I have a data set. I''d like to see if this data set
> fullfills certain properties, so I did a couple of classes which will
> do the matching against these properties:

My first question would be, do you want to be able to do the checks
individually or do you want to always apply all checks?

[snip]

> Anyway, then I started to think about using a superclass for these
> classes and use Ruby introspection to find all the subclasses and call
> match from there.

That''s what I''d do. It gives you the flexibility to execute each
individual test if you need it and provides for easy finding of all tests.

> That''s when I realized there must be simpler
> solution I just can''t see...
>
> Perhaps extending the set-class with the methods;
>
> def MatchB
> def match_b?
> # Complex calculation
> end
> end
>
> set.extend(MatchB)
>
> puts "Matched B" if set.match_b?

No, this seems overly complex to me and (more important) these tests have
nothing to do with Sets per se. You should then create a sub class of Set
that contains all match methods. But the other approach (individual
classes per test) is more modular, flexible and easier to extend. If you
hava a single Set sub class that contains all tests you''d have to use
something like this for all matches

def all_matches?
public_methods.grep( /^match/ ).each do |m|
return false unless self.send m
end
true
end

> It probably be the best way OO-wise, since it''s a property of the Set.

I regard the single test classes better OO wise, since they all implement
a single interface you you don''t have to do method name tricks to find all
tests.

> Or am I really wrong here?

No, I think you picked the reasonable solutions.

Cheers

robert

Robert Klemme

9/9/2003 10:11:00 AM

0

An example:

require "Set"

class SetTest
def self.inherited(cl)
( @test_classes ||=[] ) << cl
end

def self.all_tests(set)
@test_classes.each do |cl|
return false unless cl.new.match(set)
end

true
end
end


class Test1 < SetTest
def match(set)
puts "Test: #{self.class.name}"
true
end
end

SetTest.all_tests( Set.new )

robert

Joe Cheng

9/9/2003 2:04:00 PM

0

> ( @test_classes ||=[] ) << cl

Cool trick, Robert... I hadn''t seen that one...


Robert Klemme

9/9/2003 7:10:00 PM

0


"Joe Cheng" <code@joecheng.com> schrieb im Newsbeitrag
news:25l7b.1267$c35.358@newsread1.news.atl.earthlink.net...
> > ( @test_classes ||=[] ) << cl
>
> Cool trick, Robert... I hadn''t seen that one...

Thanks! In fact it is quite common among ruby sources - at least it seemed
so to me.

Cheers

robert

Martin DeMello

9/9/2003 10:08:00 PM

0

Robert Klemme <bob.news@gmx.net> wrote:

> No, this seems overly complex to me and (more important) these tests have
> nothing to do with Sets per se. You should then create a sub class of Set
> that contains all match methods. But the other approach (individual
> classes per test) is more modular, flexible and easier to extend. If you
> hava a single Set sub class that contains all tests you''d have to use
> something like this for all matches
>
> def all_matches?
> public_methods.grep( /^match/ ).each do |m|
> return false unless self.send m
> end
> true
> end

This seems rather heavy and, well, Java-like (where you''re forced to
create a class because you can''t have unattached procs). A test is,
conceptually, an unbound function, and Ruby does indeed give us a
mechanism to create such.

martin

Robert Klemme

9/10/2003 8:22:00 AM

0


"Martin DeMello" <martindemello@yahoo.com> schrieb im Newsbeitrag
news:Zas7b.80408$_5.1349095@news1.telusplanet.net...
> Robert Klemme <bob.news@gmx.net> wrote:
>
> > No, this seems overly complex to me and (more important) these tests
have
> > nothing to do with Sets per se. You should then create a sub class of
Set
> > that contains all match methods. But the other approach (individual
> > classes per test) is more modular, flexible and easier to extend. If
you
> > hava a single Set sub class that contains all tests you''d have to use
> > something like this for all matches
> >
> > def all_matches?
> > public_methods.grep( /^match/ ).each do |m|
> > return false unless self.send m
> > end
> > true
> > end
>
> This seems rather heavy and, well, Java-like (where you''re forced to
> create a class because you can''t have unattached procs).

Hm, I find this clearer - just because certain things can be done does
necessarily mean that''s the best way to do it.

> A test is,
> conceptually, an unbound function, and Ruby does indeed give us a
> mechanism to create such.

I assume you mean blocks / procs. The OP stated that the tests are fairly
complex, that''s why IMHO a class per test type seems more appropriate
since then handling of state (i.e. instance vars) is handled more
gracefully than in a block. Of course it can be done there, too, but the
complexity calls for separate classes.

The other solution that I find appropriate is to create a module that is
used for extending specific set instances (those that carry the data which
I want to apply the tests to). My 2 cent...

Regards

robert

Fredrik Jagenheim

9/10/2003 8:06:00 PM

0

On Wed, Sep 10, 2003 at 05:48:13PM +0900, Robert Klemme wrote:
> I assume you mean blocks / procs. The OP stated that the tests are fairly
> complex, that''s why IMHO a class per test type seems more appropriate
> since then handling of state (i.e. instance vars) is handled more
> gracefully than in a block. Of course it can be done there, too, but the
> complexity calls for separate classes.

I went with the subclassing, and from your excellent example it worked
perfectly. Many thanks.

//F