Gareth
Gareth

Reputation: 138210

Why does the order of including modules make a difference in Ruby?

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?

Upvotes: 2

Views: 2021

Answers (1)

Matt Burke
Matt Burke

Reputation: 3326

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.

Upvotes: 5

Related Questions