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 White

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]

Ian White wrote:

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

  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

···

--
Vincent Fourmond, PhD student
http://vincent.fourmond.neuf.fr/

Hi --

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?

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

···

On Fri, 22 Dec 2006, Ian White wrote:

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

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

Cheers,
Ian