[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Parametric module or injecting code via class method?

Trans

3/13/2008 1:14:00 AM

Which approach is better: parametric module or an injecting class
method.

# Generates identity/key methods based on specified attributes.
#
# include EquateOn(:a, :b)
#
# is equivalent to including a module containing:
#
# def ==(other)
# self.a == other.a && self.b == other.b
# end
#
# def eql?(other)
# self.a.eql?(other.a) && self.b.eql?(other.b)
# end
#
# def hash()
# self.a.hash ^ self.b.hash
# end
#

def EquateOn(*fields)
code = ""
code << "def ==(o) " << fields.map {|f| "self.#{f} ==
o.#{f}" }.join(" && ") << " end\n"
code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?
(o.#{f})" }.join(" && ") << " end\n"
code << "def hash() " << fields.map {|f|
"self.#{f}.hash" }.join(" ^ ") << " end\n"
mod = Module.new
mod.module_eval( code )
mod
end

- Or -

# Generates identity/key methods based on specified attributes.
#
# equate_on :a, :b
#
# _is equivalent to_
#
# def ==(o)
# self.a == o.a && self.b == o.b
# end
#
# def eql?(o)
# self.a.eql?(o.a) && self.b.eql?(o.b)
# end
#
# def hash()
# self.a.hash ^ self.b.hash
# end

def equate_on(*fields)
code = ""
code << "def ==(o) " << fields.map {|f| "self.#{f} ==
o.#{f}" }.join(" && ") << " end\n"
code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?
(o.#{f})" }.join(" && ") << " end\n"
code << "def hash() " << fields.map {|f|
"self.#{f}.hash" }.join(" ^ ") << " end\n"
class_eval( code )
fields
end

T.

19 Answers

Robert Klemme

3/13/2008 8:43:00 AM

0

2008/3/13, Trans <transfire@gmail.com>:
> Which approach is better: parametric module or an injecting class
> method.

Are you writing a book on best practices? There seem to be quite a
few of these questions recently. :-))

> # Generates identity/key methods based on specified attributes.
> #
> # include EquateOn(:a, :b)
> #
> # is equivalent to including a module containing:
> #
> # def ==(other)
> # self.a == other.a && self.b == other.b
> # end
> #
> # def eql?(other)
> # self.a.eql?(other.a) && self.b.eql?(other.b)
> # end
> #
> # def hash()
> # self.a.hash ^ self.b.hash
> # end
> #
>
> def EquateOn(*fields)
> code = ""
> code << "def ==(o) " << fields.map {|f| "self.#{f} ==
> o.#{f}" }.join(" && ") << " end\n"
> code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?
> (o.#{f})" }.join(" && ") << " end\n"
> code << "def hash() " << fields.map {|f|
> "self.#{f}.hash" }.join(" ^ ") << " end\n"
> mod = Module.new
> mod.module_eval( code )
> mod
> end
>
> - Or -
>
> # Generates identity/key methods based on specified attributes.
> #
> # equate_on :a, :b
> #
> # _is equivalent to_
> #
> # def ==(o)
> # self.a == o.a && self.b == o.b
> # end
> #
> # def eql?(o)
> # self.a.eql?(o.a) && self.b.eql?(o.b)
> # end
> #
> # def hash()
> # self.a.hash ^ self.b.hash
> # end
>
> def equate_on(*fields)
> code = ""
> code << "def ==(o) " << fields.map {|f| "self.#{f} ==
> o.#{f}" }.join(" && ") << " end\n"
> code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?
> (o.#{f})" }.join(" && ") << " end\n"
> code << "def hash() " << fields.map {|f|
> "self.#{f}.hash" }.join(" ^ ") << " end\n"
> class_eval( code )
> fields
> end

I opt for the second solution because the anonymous module does not
have any reuse effects - unless you cache it based on field names. :-)

Kind regards

robert

--
use.inject do |as, often| as.you_can - without end

Trans

3/13/2008 5:09:00 PM

0



On Mar 13, 4:42 am, "Robert Klemme" <shortcut...@googlemail.com>
wrote:
> 2008/3/13, Trans <transf...@gmail.com>:
>
> > Which approach is better: parametric module or an injecting class
> > method.
>
> Are you writing a book on best practices? There seem to be quite a
> few of these questions recently. :-))

Ha... I probably should be! But right now I'm just working through
some old "TODO" questions in Facets.

> > # Generates identity/key methods based on specified attributes.
> > #
> > # equate_on :a, :b
> > #
> > # _is equivalent to_
> > #
> > # def ==(o)
> > # self.a == o.a && self.b == o.b
> > # end
> > #
> > # def eql?(o)
> > # self.a.eql?(o.a) && self.b.eql?(o.b)
> > # end
> > #
> > # def hash()
> > # self.a.hash ^ self.b.hash
> > # end
>
> > def equate_on(*fields)
> > code = ""
> > code << "def ==(o) " << fields.map {|f| "self.#{f} ==
> > o.#{f}" }.join(" && ") << " end\n"
> > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?
> > (o.#{f})" }.join(" && ") << " end\n"
> > code << "def hash() " << fields.map {|f|
> > "self.#{f}.hash" }.join(" ^ ") << " end\n"
> > class_eval( code )
> > fields
> > end
>
> I opt for the second solution because the anonymous module does not
> have any reuse effects - unless you cache it based on field names. :-)

:) And if we do cache based on field names?

T.

Robert Klemme

3/13/2008 5:23:00 PM

0

2008/3/13, Trans <transfire@gmail.com>:
>
>
> On Mar 13, 4:42 am, "Robert Klemme" <shortcut...@googlemail.com>
> wrote:
> > 2008/3/13, Trans <transf...@gmail.com>:
>
> >
> > > Which approach is better: parametric module or an injecting class
> > > method.
> >
> > Are you writing a book on best practices? There seem to be quite a
> > few of these questions recently. :-))
>
>
> Ha... I probably should be! But right now I'm just working through
> some old "TODO" questions in Facets.

LOL

> > > # Generates identity/key methods based on specified attributes.
> > > #
> > > # equate_on :a, :b
> > > #
> > > # _is equivalent to_
> > > #
> > > # def ==(o)
> > > # self.a == o.a && self.b == o.b
> > > # end
> > > #
> > > # def eql?(o)
> > > # self.a.eql?(o.a) && self.b.eql?(o.b)
> > > # end
> > > #
> > > # def hash()
> > > # self.a.hash ^ self.b.hash
> > > # end
> >
> > > def equate_on(*fields)
> > > code = ""
> > > code << "def ==(o) " << fields.map {|f| "self.#{f} ==
> > > o.#{f}" }.join(" && ") << " end\n"
> > > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?
> > > (o.#{f})" }.join(" && ") << " end\n"
> > > code << "def hash() " << fields.map {|f|
> > > "self.#{f}.hash" }.join(" ^ ") << " end\n"
> > > class_eval( code )
> > > fields
> > > end
> >
> > I opt for the second solution because the anonymous module does not
> > have any reuse effects - unless you cache it based on field names. :-)
>
>
> :) And if we do cache based on field names?

Aw, com' on. Interesting additional question: should order matter?
I'd say probably yes, but that will reduce reuse of cached entries.

Kind regards

robert


PS: if you run out of tasks (I know, won't happen) - I just discovered
this site:
http://www.virginmedia.com/movies/dvd/gangsterrapgenerator...

--
use.inject do |as, often| as.you_can - without end

Trans

3/13/2008 6:06:00 PM

0



On Mar 13, 1:23 pm, "Robert Klemme" <shortcut...@googlemail.com>
wrote:
> 2008/3/13, Trans <transf...@gmail.com>:
>
>
>
>
>
> > On Mar 13, 4:42 am, "Robert Klemme" <shortcut...@googlemail.com>
> > wrote:
> > > 2008/3/13, Trans <transf...@gmail.com>:
>
> > > > Which approach is better: parametric module or an injecting class
> > > > method.
>
> > > Are you writing a book on best practices? There seem to be quite a
> > > few of these questions recently. :-))
>
> > Ha... I probably should be! But right now I'm just working through
> > some old "TODO" questions in Facets.
>
> LOL
>
>
>
> > > > # Generates identity/key methods based on specified attributes.
> > > > #
> > > > # equate_on :a, :b
> > > > #
> > > > # _is equivalent to_
> > > > #
> > > > # def ==(o)
> > > > # self.a == o.a && self.b == o.b
> > > > # end
> > > > #
> > > > # def eql?(o)
> > > > # self.a.eql?(o.a) && self.b.eql?(o.b)
> > > > # end
> > > > #
> > > > # def hash()
> > > > # self.a.hash ^ self.b.hash
> > > > # end
>
> > > > def equate_on(*fields)
> > > > code = ""
> > > > code << "def ==(o) " << fields.map {|f| "self.#{f} ==
> > > > o.#{f}" }.join(" && ") << " end\n"
> > > > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?
> > > > (o.#{f})" }.join(" && ") << " end\n"
> > > > code << "def hash() " << fields.map {|f|
> > > > "self.#{f}.hash" }.join(" ^ ") << " end\n"
> > > > class_eval( code )
> > > > fields
> > > > end
>
> > > I opt for the second solution because the anonymous module does not
> > > have any reuse effects - unless you cache it based on field names. :-)
>
> > :) And if we do cache based on field names?
>
> Aw, com' on. Interesting additional question: should order matter?
> I'd say probably yes, but that will reduce reuse of cached entries.

I explored the cache idea a bit more, and it made me see why a Module
approach appealed to me over the injection method, but generating
parametric modules, even if cached seemed somehow wrong too. I derived
this instead:

module EquateOn

def ==(o)
equate_fields.all?{ |f| send(f) == o.send(f) }
end

def eql?(o)
equate_fields.all?{ |f| send(f).eql?(o.send(f)) }
end

def hash
equate_fields.inject(0){ |memo, f| memo ^ send(f).hash }
end

end

def EquateOn(*fields)
define_method(:equate_fields){ fields }
return EquateOn
end

Much better, but I still wonder if it is enough to be preferable over
the a helper method.

T.



ara.t.howard

3/13/2008 7:36:00 PM

0


On Mar 12, 2008, at 7:14 PM, Trans wrote:

> Which approach is better: parametric module or an injecting class
> method.

i would say neither and both. it's saner to decouple them and then
recouple - giving the best of both worlds with the same amount of code:


cfp2:~ > cat a.rb
module Equate
module Methods
def equate a, b
module_eval <<-code
def ==(other)
self.#{ a } == other.#{ a } && self.#{ b } == other.#{ b }
end
def eql?(other)
self.#{ a }.eql?(other.#{ a }) && self.#{ b }.eql?
(other.#{ b })
end
def hash()
self.#{ a }.hash ^ self.#{ b }.hash
end
code
end
end

def Equate.included other
other.send :extend, Methods
end

def Equate.on a, b
Module.new{
include Equate
equate a, b
}
end
end

class C < Struct.new(:a, :b)
include Equate.on(:a, :b)
end

p C[4,2] == C[4,2] #=> true
p C[4,2] == C[4,3] #=> false

class D < Struct.new(:a, :b)
include Equate
equate :a, :b
end

p D[4,2] == D[4,2] #=> true
p D[4,2] == D[4,3] #=> false



cfp2:~ > ruby a.rb
true
false
true
false



a @ http://codeforp...
--
share your knowledge. it's a way to achieve immortality.
h.h. the 14th dalai lama



Robert Klemme

3/13/2008 10:18:00 PM

0

On 13.03.2008 20:35, ara howard wrote:
> On Mar 12, 2008, at 7:14 PM, Trans wrote:
>
>> Which approach is better: parametric module or an injecting class
>> method.
>
> i would say neither and both. it's saner to decouple them and then
> recouple - giving the best of both worlds with the same amount of code:
>
>
> cfp2:~ > cat a.rb
> module Equate
> module Methods
> def equate a, b
> module_eval <<-code
> def ==(other)
> self.#{ a } == other.#{ a } && self.#{ b } == other.#{ b }
> end
> def eql?(other)
> self.#{ a }.eql?(other.#{ a }) && self.#{ b }.eql?
> (other.#{ b })
> end
> def hash()
> self.#{ a }.hash ^ self.#{ b }.hash
> end
> code
> end
> end
>
> def Equate.included other
> other.send :extend, Methods
> end
>
> def Equate.on a, b
> Module.new{
> include Equate
> equate a, b
> }
> end
> end
>
> class C < Struct.new(:a, :b)
> include Equate.on(:a, :b)
> end
>
> p C[4,2] == C[4,2] #=> true
> p C[4,2] == C[4,3] #=> false
>
> class D < Struct.new(:a, :b)
> include Equate
> equate :a, :b
> end
>
> p D[4,2] == D[4,2] #=> true
> p D[4,2] == D[4,3] #=> false
>
>
>
> cfp2:~ > ruby a.rb
> true
> false
> true
> false

Using a Struct generated class as base class is a bad example IMHO
because those classes do already have the equation properties.

At the moment the only advantage I see in using modules is avoidance of
namespace pollution. Personally I'd just have a method in class Module
equate_on which defines methods as shown.

Btw, while we're at it, I'd also like order_on which defines <=> based
on fields given. :-)

Kind regards

robert

ara.t.howard

3/13/2008 11:43:00 PM

0


On Mar 13, 2008, at 4:20 PM, Robert Klemme wrote:

> Using a Struct generated class as base class is a bad example IMHO
> because those classes do already have the equation properties.

heh - yeah bad example. i just used struct to avoid defining atts 'a'
and 'b' and an initializer - i think the example stands with that
though.

> At the moment the only advantage I see in using modules is avoidance
> of namespace pollution. Personally I'd just have a method in class
> Module equate_on which defines methods as shown.


well namspace pollution is no small thing! the other huge advantage
is that running rdoc over something like

class C
module ClassMethods
end
end

is about a billion times better than running it over

class C
singleton_class.module_eval ....

not to mention grep et all.


the other huge advantage of using modules is subtle: if you start out
with code such as

module M

def self.included other
add_equate_method_to other
end

end

and, over the course of your project end up with

module M
X = 4
Y = 2
end


then the result of

class C
include M
end

is that X and Y are dumped into C *even though the included block was
purposely setup to define what happens logically for module
inclusion*. by carving out the target of inclusion one can do

module M
module Methods
end

def self.included other
other.extend Methods
end
end

and dump constants into M to your heart's content without
inadvertently dumping them into every class that wanted a little class
method.

this last bit i just recently have been using - but born out from long
debugging session ;-)

cheers.

a @ http://draw...
--
sleep is the best meditation.
h.h. the 14th dalai lama




Robert Klemme

3/16/2008 9:27:00 PM

0

On 14.03.2008 00:42, ara howard wrote:
> On Mar 13, 2008, at 4:20 PM, Robert Klemme wrote:
>
>> Using a Struct generated class as base class is a bad example IMHO
>> because those classes do already have the equation properties.
>
> heh - yeah bad example. i just used struct to avoid defining atts 'a'
> and 'b' and an initializer - i think the example stands with that
> though.
>
>> At the moment the only advantage I see in using modules is avoidance
>> of namespace pollution. Personally I'd just have a method in class
>> Module equate_on which defines methods as shown.
>
>
> well namspace pollution is no small thing! the other huge advantage
> is that running rdoc over something like
>
> class C
> module ClassMethods
> end
> end
>
> is about a billion times better than running it over
>
> class C
> singleton_class.module_eval ....
>
> not to mention grep et all.

The alternative - at least as far as I see it - is

class Module
def equate_on(fields)
...
end
end

class AnyClass
attr_accessor :foo, :bar
equate_on :foo
end

> the other huge advantage of using modules is subtle: if you start out
> with code such as
>
> module M
>
> def self.included other
> add_equate_method_to other
> end
>
> end
>
> and, over the course of your project end up with
>
> module M
> X = 4
> Y = 2
> end
>
>
> then the result of
>
> class C
> include M
> end
>
> is that X and Y are dumped into C *even though the included block was
> purposely setup to define what happens logically for module
> inclusion*. by carving out the target of inclusion one can do
>
> module M
> module Methods
> end
>
> def self.included other
> other.extend Methods
> end
> end
>
> and dump constants into M to your heart's content without
> inadvertently dumping them into every class that wanted a little class
> method.
>
> this last bit i just recently have been using - but born out from long
> debugging session ;-)

Yeah, but you can achieve the functionality without modules at all - at
the small cost of defining a single instance method in class Module. I
guess you have some other code in mind (at least the one that you had to
debug) but I am still not convinced that using modules is the best
approach for such a basic thing as defining equivalence between
instances of a class. My .02 EUR...

Kind regards

robert

Sean O'Halpin

3/16/2008 11:13:00 PM

0

On Sun, Mar 16, 2008 at 9:30 PM, Robert Klemme
<shortcutter@googlemail.com> wrote:
> Yeah, but you can achieve the functionality without modules at all - at
> the small cost of defining a single instance method in class Module.

I suspect that Trans is looking for a general principle to follow and
is using equality as an example (but I'm open to be corrected of
course :)
While adding a single method to Module may seem like a small cost if
you're only using it in a single program,
it becomes unsustainable when you're managing a large public library
like Facets which is trying to play nice with frameworks like Rails
(which doesn't go out of its way to be polite).

With the unbridled rise in Ruby 'metaprogramming', a zillion monkeys
are leaving their cr**p all over Object, Module and Class.
I thoroughly endorse any effort to avoid namespace pollution.

Also, Ara's point about unintended constant propagation is a very good
one. I recently got stung by an otherwise innocent VERSION constant
which screwed up all manner of unrelated things.

So my tuppence worth goes towards something like Ara's version of the
module inclusion method.

Kind regards,
Sean

Robert Klemme

3/17/2008 9:12:00 AM

0

On 17.03.2008 00:12, Sean O'Halpin wrote:
> On Sun, Mar 16, 2008 at 9:30 PM, Robert Klemme
> <shortcutter@googlemail.com> wrote:
>> Yeah, but you can achieve the functionality without modules at all - at
>> the small cost of defining a single instance method in class Module.
>
> I suspect that Trans is looking for a general principle to follow and
> is using equality as an example (but I'm open to be corrected of
> course :)

Maybe. IIRC he did not state this.

> While adding a single method to Module may seem like a small cost if
> you're only using it in a single program,
> it becomes unsustainable when you're managing a large public library
> like Facets which is trying to play nice with frameworks like Rails
> (which doesn't go out of its way to be polite).

Agreed. But that's a different story - I reacted to a very concrete
question. Maybe it was my fault to not read it as a more general
question - OTOH the original question was rather short and you can read
it both ways IMHO.

> With the unbridled rise in Ruby 'metaprogramming', a zillion monkeys
> are leaving their cr**p all over Object, Module and Class.
> I thoroughly endorse any effort to avoid namespace pollution.

Agreed.

> Also, Ara's point about unintended constant propagation is a very good
> one. I recently got stung by an otherwise innocent VERSION constant
> which screwed up all manner of unrelated things.

Yeah, but happens only if you include modules. My point here was that
the same behavior can be achieved by a single method in class Module.
And in this particular case (defining equivalence) it is so general that
it should even be part of the std lib. No module inclusion, no constant
propagation.

> So my tuppence worth goes towards something like Ara's version of the
> module inclusion method.

"tuppence" LOL - was this a typo or is it a common idiom I haven't seen
before? (Sounds a bit like these plastic boxes...)

Kind regards

robert