views:

147

answers:

1

This question is best summed up with a code example:

module TestOne
  module Foo
    def foo
      42
    end
  end

  module Bar
    include Foo
  end

  class Quux
    include Bar
  end
end

TestOne::Bar.ancestors # => [TestOne::Bar, TestOne::Foo]
TestOne::Quux.ancestors # => [TestOne::Quux, TestOne::Bar, TestOne::Foo, Object, Kernel]
TestOne::Quux.new.foo # => 42

module TestTwo
  class Quux
  end

  module Bar
  end

  module Foo
    def foo
      42
    end
  end
end

TestTwo::Quux.send :include, TestTwo::Bar
TestTwo::Bar.send :include, TestTwo::Foo

TestTwo::Bar.ancestors # => [TestTwo::Bar, TestTwo::Foo]
TestTwo::Quux.ancestors # => [TestTwo::Quux, TestTwo::Bar, Object, Kernel]
TestTwo::Quux.new.foo # => 
# ~> -:40: undefined method `foo' for #<TestTwo::Quux:0x24054> (NoMethodError)

I thought that when you include a module (e.g. Bar inside a class Foo) all that Ruby stores is the fact that Foo includes Bar. So, when you call a method on Foo it looks in Bar for the method.

If that was true, by the time TestTwo::Quux.new.foo is called I've mixed the foo method into TestTwo::Bar so it should work, right?

+3  A: 

The docs say that append_features (which is called by include) mixes the methods into the caller. So when TestTwo::Quux includes TestTwo::Bar, no methods are added to TestTwo::Quux. The next line adds methods to TestTwo::Bar, but not to TestTwo::Quux.

Matt Burke
That's a shame because in my real case the first include happens out of my control, I'm just trying to mix something into anything which includes my target module. Ah well, thanks :)
Gareth