Parametric module or injecting code via class method?

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.

2008/3/13, Trans [email protected]:

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. :-))

def eql?(other)

code << "def ==(o) "   << fields.map {|f| "self.#{f} ==
  • Or -

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. :slight_smile:

Kind regards

robert

On Mar 13, 4:42 am, “Robert K.” [email protected]
wrote:

2008/3/13, Trans [email protected]:

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.

def eql?(o)

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. :slight_smile:

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

T.

On Mar 13, 1:23 pm, “Robert K.” [email protected]
wrote:

Which approach is better: parametric module or an injecting class

code << "def ==(o) "   << fields.map {|f| "self.#{f} ==

have any reuse effects - unless you cache it based on field names. :slight_smile:

:slight_smile: 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.

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://codeforpeople.com/

On 13.03.2008 20:35, ara howard wrote:

module Equate
def hash()
def Equate.on a, b

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. :slight_smile:

Kind regards

robert

2008/3/13, Trans [email protected]:

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

def eql?(o)

o.#{f}" }.join(" && “) << " end\n”

:slight_smile: 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:

On 14.03.2008 00:42, ara howard wrote:

of namespace pollution. Personally I’d just have a method in class

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

class C

debugging session :wink:
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

On Sun, Mar 16, 2008 at 9:30 PM, Robert K.
[email protected] 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 :slight_smile:
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

On Mar 13, 2008, at 4:20 PM, Robert K. 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 :wink:

cheers.

a @ http://drawohara.com/

On 17.03.2008 00:12, Sean O’Halpin wrote:

On Sun, Mar 16, 2008 at 9:30 PM, Robert K.
[email protected] 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 :slight_smile:

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

On Mon, Mar 17, 2008 at 9:14 AM, Robert K.
[email protected] wrote:

On 17.03.2008 00:12, Sean O’Halpin wrote:

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…)

Tuppence = “two pence” (GBP) - see the song in “Mary Poppins” about
feeding the birds rather than investing in banks for an example -

:wink:

On 17.03.2008 11:35, James A. wrote:

On Mon, Mar 17, 2008 at 9:14 AM, Robert K.
[email protected] wrote:

On 17.03.2008 00:12, Sean O’Halpin wrote:

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…)

Tuppence = “two pence” (GBP)

Yes, that’s what I gathered.

  • see the song in “Mary Poppins” about
    feeding the birds rather than investing in banks for an example -
    Feed the Birds - Wikipedia

Thanks!

Kind regards

robert

On Mar 18, 12:59 pm, “Sean O’Halpin” [email protected] wrote:

is using equality as an example (but I’m open to be corrected of
course :slight_smile:

Maybe. IIRC he did not state this.

Indeed. Perhaps Trans can enlighten us as to the real application?
(Are you listening Tom?)

There is the specific application which applies to equate_on,
compare_on (aliased also as sort_on) and possibly initialize_on.
However, seeing that’s already three cases, a general principle does
come into consideration.

True enough. As I said, I’m only surmising what Trans is after. I’m
not criticising your response to the concrete example (after all,
it is both pragmatic and to the point). Rather, I’m taking the
question Trans posed as an opportunity to discuss what I consider best
practice in the more general case.

In the above cases, with the exception of initialize_on (I may just
dump that one), I went with the capitlaize method approach, but used a
little bit of Ara’s concept. Eg.

module Equatable

def self.identify(base, *accessors)
  base.send(:define_method, :identity){ accessors }
  self
end

def ==(o)
  identity.all?{ |a| send(a) == o.send(a) }
end

def eql?(o)
  identity.all?{ |a| send(a).eql?(o.send(a)) }
end

def hash
  identity.inject(0){ |memo, a| memo ^ send(a).hash }
end

end

class Module
def Equatable(*fields)
Equatable.identify(self, *accessors)
end
end

I did something similar with Comparable, albeit a little different b/c
Comparable already exists in the standard library. I figure this is a
good way to go b/c the method doesn’t really take up any new name
space because the module already exists.

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.

Agreed. (Lots of other things I’d like to see there too, and some I’d
rather see not there - but that’s a different story :slight_smile:

I’d like to dig into these questions a bit more as well --except for
the fact that I am sick of them. To be straight forward about it, Ruby
itself is torn. It makes method extensions a snap, which we all know
has proven itself as a distinct advantage of Ruby. But in turn it’s
inheritance system does nothing to make it easy to do it in a robust
way. In short, I’ve come to the (long and drawn out) conclusion that
Ruby’s mixin system is crap.

T.

On Mar 18, 12:59 pm, “Sean O’Halpin” [email protected] wrote:

True enough. As I said, I’m only surmising what Trans is after. I’m
not criticising your response to the concrete example (after all,
it is both pragmatic and to the point). Rather, I’m taking the
question Trans posed as an opportunity to discuss what I consider best
practice in the more general case.

I’d like to present another example --a simpler one in a sense, of
direct extension vs. using modules. I will leave out most the code for
brevity sakes but consider, but here is an example where I took code
that was straight core extensions and “modulized” it:

– random.rb

Can’t modulize Kernel b/c of double module inclusion problem.

module Kernel

def maybe(chance = 0.5, &block)
  if block then
    yield if rand < chance
  else
    rand < chance
  end
end

...

end

module Random

def self.append_features(mod)
  if mod == ::Kernel # not needed
    mod.send(:include, Random::Kernel)
  elsif mod == ::Array
    mod.send(:include, Random::Array)
  elsif mod == ::Hash
    mod.send(:include, Random::Hash)
  elsif mod == ::String
    mod.send(:include, Random::String)
  else
    raise TypeError
  end
end

#

module Array

  def at_rand
    self.at( rand( size ) )
  end

  ...
end

#

module Hash

  def rand_key
    keys.at( rand(keys.size) )
  end

  ...
end

#

module String

  def self.included(base)
    base.extend(Self)
  end

  module Self

    def random(max_length = 8, char_re = /[\w\d]/)
      # gmosx: this is a nice example of input parameter checking.
      # this is NOT a real time called method so we can add this
      # check. Congrats to the author.
      raise ArgumentError.new('char_re must be a regular

expression!') unless char_re.is_a?(Regexp)
string = “”
while string.length < max_length
ch = rand(255).chr
string << ch if ch =~ char_re
end
return string
end

    def rand_letter
      (rand(26) + (rand(2) == 0 ? 65 : 97) ).chr
    end

  end

  def at_rand( separator=// )
    #separator = self.class.patterns( separator )
    self.split(separator,-1).at_rand
  end

  ...

end

end

class Array
include Random
end

class Hash
include Random
end

class String
include Random
end

Other Ruby coder’s might look at this and think, “clever”. I look at
it and think, “obfuscated”. First off there is Kernel, b/c of the
double module inclusion problem it can’t participate in the whole
“clever” affair. Then there is Self and the included callback hack to
get the class-level methods in there. And on top of it all, there is
the simple fact that I had to create and include a bunch of new
modules and all the overhead that comes with them – when all I want
to do add a few methods to the core classes/modules. To throw in a
little irony as well, the main reason I even bothered to do this, was
not to help avoid method clash (a fairly unavoidable problem really),
but to get better results for RDoc --and that fact really makes me
sick.

T.

On Mar 18, 5:30 pm, Trans [email protected] wrote:

I’d like to dig into these questions a bit more as well --except for
the fact that I am sick of them. To be straight forward about it, Ruby
itself is torn. It makes method extensions a snap, which we all know
has proven itself as a distinct advantage of Ruby. But in turn it’s
inheritance system does nothing to make it easy to do it in a robust
way. In short, I’ve come to the (long and drawn out) conclusion that
Ruby’s mixin system is crap.

Okay, that’s overly harsh. (Can you tell I had a bad day yesterday?)
“Weak” is a better description.

T.

On Mon, Mar 17, 2008 at 9:14 AM, Robert K.
[email protected] wrote:

Maybe. IIRC he did not state this.
Indeed. Perhaps Trans can enlighten us as to the real application?
(Are you listening Tom?)

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.

True enough. As I said, I’m only surmising what Trans is after. I’m
not criticising your response to the concrete example (after all,
it is both pragmatic and to the point). Rather, I’m taking the
question Trans posed as an opportunity to discuss what I consider best
practice in the more general case.

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.

Agreed. (Lots of other things I’d like to see there too, and some I’d
rather see not there - but that’s a different story :slight_smile:

No module inclusion, no constant
propagation.

True.

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…)

Just showing my age & nationality (old, British :slight_smile: Tuppence = 2 pence,
3 pence was ‘thruppence’, the coin ‘a thruppenny bit’ and we used to
call the six pence coin ‘a tanner’.)

Kind regards

    robert

All the best,
Sean

On Wed, Mar 19, 2008 at 12:55 PM, Trans [email protected] wrote:

Okay, that’s overly harsh. (Can you tell I had a bad day yesterday?)
“Weak” is a better description.
Not really :wink:
But it is not Ruby’s fault it is the way Mixins are, that’s why I am
so fond of traits.

T.


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

On Thu, 13 Mar 2008 17:42:45 +0900, Robert K. wrote:

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

I’d like to see a book devoted to inject.

-Thufir

2008/3/18, Trans [email protected]:

direct extension vs. using modules. I will leave out most the code for
brevity sakes but consider, but here is an example where I took code
that was straight core extensions and “modulized” it:

– random.rb

100+ lines

Did you say “brevity”? LOL

but to get better results for RDoc --and that fact really makes me
sick.

Indeed. It sounds rather RDoc should be fixed instead of code tweaked
to get desired results.

Kind regards

robert