I recently had an interview for a Ruby on Rails developer position and have been asked a lot of interesting (and somewhat difficult) questions. They required some solid knowledge of how Ruby works. One of those questions was following:

Say we have two modules with a method with the same name in both of them.

module Wolf
  def speak
    puts "Auuu!"
  end
end

module Dog
  def speak
    puts "Woof!"
  end
end

And we include them both to a class, create an instance of that class and call that method. What will happen?

class Mutant
  include Dog
  include Wolf
end

m = Mutant.new
m.speak

In this case, the method from Wolf module will be called and output will be “Auuu!”. Let’s see why and how this happens.

When a method is called Ruby interpreter will first look if that method is defined on that object’s class. If it didn’t find the right method there, it will check that class’s superclass. It will keep moving this way up until it finds the right method. This chain of superclasses is called ancestors chain. In case method was not found on any class in the ancestors chain, method_missing method will be called (and it will raise NoMethodError by default).

But what about modules?

Let’s first take a look at ancestors chain of our Mutant class by calling ancestors method.

Mutant.ancestors
 => [Mutant, Wolf, Dog, Object, Kernel, BasicObject]

So we have Mutant class here as expected, we also have Object and BasicObject which are classes defined by Ruby that all other classes inherit from. But there are also modules here. There’s Wolf and Dog that we defined, and Kernel which is included by default in all Ruby classes.

So how did the modules get here? When you call include in ruby it will wrap that module in an anonymous class and add it in the ancestral chain right above the current class.

When we included Dog, it got wrapped in a class and Mutant class inherited from it. It was set above Mutant and below Object in ancestors chain. When we included Wolf, it pushed Dog up and got added right above Mutant in the ancestor chain.

We got a response from Wolf module because it’s first in the ancestors chain. Ruby interpreter will first check if that module has speak class, it will find it there and won’t look any further. If we included the modules in different order, the other method would be called.