Extend weirdness?

Hi Group,

This code:
module Foo
def self.included(base)
base.class_eval <<-end_eval
def self.meth
“Foo”
end
end_eval
end
end

module Bar
  def self.included(base)
    base.class_eval do
      extend ClassMethods
    end
  end

  module ClassMethods
    def meth
      "Bar"
    end
  end
end

class A
  include Foo
  include Bar
end

puts A.meth

produces:
Foo

whereas I would expect it to produce “Bar”

Can anyone explain what’s going on here? i.e. Why the latter extend
with Bar doesn’t over-write the class method introduced by Foo

Cheers,
Ian W.

The previous post is using a rails-style idiom, which is unnecessarily
complex for the problem at hand.
Here’s a simpler example to the same effect:

module Foo
  def self.included(base)
    class<<base
      def meth
        "Foo"
      end
    end
  end
end

module Bar
  def meth
    "Bar"
  end
end

class A
  include Foo
  extend Bar
end

puts A.meth

Which produces “Foo”

There’s a workaround (remove the method by hand):

module Bar
  def self.extended(base)
    class<<base
      remove_method :meth
    end
  end
end

But this workaround does what I’d expect Ruby to do (overwrite any
existing methods when extending)

I’m confused - is this expected behaviour?

Cheers,
Ian

ps.

$ ruby --version
ruby 1.8.5 (2006-08-25) [powerpc-darwin8.7.0]

Hi –

On Fri, 22 Dec 2006, Ian W. wrote:

   end
 include Foo
 def self.extended(base)
   class<<base
     remove_method :meth
   end
 end

end

But this workaround does what I’d expect Ruby to do (overwrite any
existing methods when extending)

I’m confused - is this expected behaviour?

Yes. When you send a message to A, it searches for a
corresponding method in:

its singleton class
modules mixed into its singleton class
its “birth class” (Class in this case)
modules mixed into its birth class
etc. (up to Object and Kernel)

in that order. When you do “extend Bar”, you’ve inserted Bar as one
of the modules mixed into the singleton class of A (line 2). But when
you do include Foo, you insert the method “meth” directly into the
singleton class (line 1). Therefore, that version of “meth” will
always take priority, no matter how many modules you mix in or in what
order.

David

Ian W. wrote:

  end
    def meth
puts A.meth

produces:
Foo

whereas I would expect it to produce “Bar”

Can anyone explain what’s going on here? i.e. Why the latter extend
with Bar doesn’t over-write the class method introduced by Foo

The answer is simple: extend doesn’t overwrite methods, it just
includes the module for the singleton object. As there is already a
singleton method ‘meth’ defined in the singleton object A, this method
is used systematically in method calls.

Does that explain your result ? For information, extend is

rb_extend_object(obj, module)
VALUE obj, module;
{
rb_include_module(rb_singleton_class(obj), module);
}

It is basically like

class << self
include Something
end

Cheers,

Vince

Thanks David for that very clear explanation, I am no longer confused.

Cheers,
Ian