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.