Including a module in Ruby

18 11 2007

If we define two modules, with methods that have the same names

module M1
  def foo
    puts "M1"
  end
end

module M2
  def foo
    puts "M2"
  end
end

and then include them in a class in a specific order

class C
  include M1
  include M2
end

method from the last included module will be used.

C.new.foo # => "M2"

So it looks like the methods are “copied” into the including class, so that the last definition of “foo” gets precedence. That’s how I thought about including Ruby modules initially.

But if that was the case the following example

module M
  def foo
    puts "M"
  end
end

class C
  def foo
    puts "C"
  end
  include M
end

should print “M”. But it doesn’t.

C.new.foo # => "C"

It calls the class “C” version of the “foo”, so the method can’t be redefined during the include.

What actually happens (and what I learned from “Include” part of Chapter 4 of Ruby Hacking Guide) is that the included module gets injected into the class hierarchy right above “C”.

class C
  def foo
    puts "C"
    super # Calling to see what the superclass defined.
  end
end

Let’s check what the hierarchy looks like before the inclusion of M.

C.ancestors # => [C, Object, Kernel]

Now let’s define “M”

module M
  def foo
    puts "M"
  end
end

and include it in “C”.

class C
  include M
end

Let’s check how it affected the class hierarchy.

C.ancestors # => [C, M, Object, Kernel]

Module “M” got injected as a direct superclass of “C”.

C.new.foo # => "C" then "M"

As C#foo calls super it’s now obvious how we got that output.

About these ads

Actions

Information

8 responses

3 04 2008
rumoku

look at my example and try to explaine result:

module B1
def printB
puts "B1"
end
end

module B2
def printB
puts "B2"
end
end

class C
include B2,B1
end

a=C.new
a.printB

3 04 2008
mousebender

For the record: the result of the code snippet posted by rumoku is “B2″.

Interesting. It’s easy to explain how of the result, but for the why you would have to ask Ruby developers. ;-)

Anyway, if you look at the source code for Module#include (rb_mod_include in eval.c), you’ll see that the arguments for include are processes backwards. So, “include B2, B1″ is equivalent to “include B1; include B2″.

I agree it’s strange. Well, yet another Ruby quirk.

31 05 2010
shaK

Perfect and simple explanation. thanks a lot mousebender

30 05 2013
blogwritertiago

in your first example it’s possible to do this:

C.new.M1.foo

or how can i choose which one i can use?

Thanks

30 05 2013
Michał Kwiatkowski

You can’t choose. M2 is before M1 in the ancestors list, so its implementation of foo will used. A solution is to either use different names for those methods or utilize composition instead of inheritance.

30 05 2013
tiago

but with composition i wouldn’t have to change the modules to class, is that right?

30 05 2013
Michał Kwiatkowski

Really depends on your case. You have to include the module somewhere: if not on class, you can directly extend an object with it.

30 05 2013
tiago

Ok, thanks for the help i have tho figure what’s the best option.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




Follow

Get every new post delivered to your Inbox.

%d bloggers like this: