Module#prepend in Ruby 2.0
- 26 August 2016
- Ruby
- Module#prepend in Ruby 2.0
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!