Module#prepend in Ruby 2.0

Subscribe to receive new articles. No spam. Quality content.

The best way to understand how Module#prepend works is to figure out method lookup process in Ruby.

If it's a simple inheritance and method is present in parent class but not in a main class:

class Human
  def hi
    "Hi from Human!"
  end
end

class Man < Human
end

Man.new.hi  # => Hi from Human!

It's the easiest and most obvious case. Method in parent class Human is being called.

Let's see more interesting case, when methods with the same name present in both: parent class and module:

class Human
  def hi
    "Hi from Human!"
  end
end

module Greetable
  def hi
    "Hi from Module!"
  end
end

class Man < Human
  include Greetable
end

Man.new.hi  # => "Hi from Module!"

As we can see, in this case method from module Greetable is being called. So in hierarchy of ancestors - modules have higher priority than parent classes. We can check that using ancestors method:

Man.ancestors # => [Man, Greetable, Human, ...]

It shows us that to find method, Ruby will look into Man class itself, then it will go to module Greetable and only after that it will look into parent class Human.

Let's get back to Module#prepend.

Let's check what will happen if we change include Greetable to prepend Greetable.

Method ancestors shows impact from changing include to prepend:

Man.ancestors # => [Greetable, Man, Human, ...]

As we can see, module Greetable is the first element of ancestors' chain now. It means that even if we add hi method to main class Man - it will not be called. The first place to look for methods is a Greetable module now.

Simple example to prove the impact:

module Greetable
  def hi
    "Hi from Module!"
  end
end

class Man
  prepend Greetable

  def hi
    "Hi from Man"
  end
end

Man.new.hi  # => "Hi from Module!"

Man.ancestors.inspect # => [Greetable, Man, Object]

It's unusual to change method lookup in Ruby this way, because it makes code more implicit and harder to understand. So you should use this option carefully. Usually it's better to change design of your classes to avoid using prepend.

But in rare cases prepend works just perfectly and can save your from even more "hacky" approaches.

Thanks for reading!

Subscribe to receive new articles. No spam. Quality content.

Comments