Table of contentsClick link to navigate to the desired location
This content has been automatically translated from Ukrainian.
In Ruby, modules are used for code reuse. The method of adding a module affects where and how methods from the module become available.
include - adds methods as instance methods
- Module methods become available to all objects of the class.
- Used when you want methods to be in class instances.
module Greetings
def hello
"Hi!"
end
end
class User
include Greetings
end
u = User.new
u.hello # => "Hi!"
Module methods appear in the class's ancestor chain after the class itself, meaning they can be overridden in the class. But let's break this down in more detail. In Ruby, every class has an ancestor chain - the order in which Ruby looks for methods when calling on an object.
module Greetings
def hello
"Hi from module"
end
end
class User
include Greetings
def hello
"Hi from class"
end
end
p User.ancestors
# => [User, Greetings, Object, Kernel, BasicObject]
When you do include Greetings, Ruby inserts the module after the class in the ancestor chain. Or to remember it simply - above the class.
This means that class methods override module methods if they have the same names.
u = User.new u.hello # => "Hi from class"
Ruby first looks for the method in the class itself (User). Then, if it doesn't find it, it checks the modules in the chain (Greetings), then the ancestors (Object, Kernel).
extend - adds methods as class methods
- Module methods become available directly on the class, not on instances.
- Convenient for adding "class methods" without explicit self.method.
module Tools
def info
"Class method!"
end
end
class User
extend Tools
end
User.info # => "Class method!"
User.new.info # => NoMethodError
In fact, Ruby adds methods to the singleton class of the object (the class itself is an object).
prepend - adds methods before the class in the ancestor chain
- Module methods appear before the class in the ancestor chain.
- This means that module methods can override class methods.
module Greetings
def hello
"Hello from module"
end
end
class User
prepend Greetings
def hello
"Hello from class"
end
end
p User.ancestors
# => [Greetings, User, Object, Kernel, BasicObject]
The key point: Greetings is before the User class.
Ruby looks for methods in the order shown by ancestors.
So when you call u.hello, Ruby first sees the method in the Greetings module, and then in the User class.
u = User.new u.hello # => "Hello from module"
Using super in a module
If a module calls super, Ruby will go further up the ancestor chain and execute the class method:
module Greetings
def hello
super + " and module"
end
end
class User
prepend Greetings
def hello
"Hello from class"
end
end
u = User.new
u.hello # => "Hello from class and module"
Why is that?
- Ruby calls hello in the Greetings module (because it is first in ancestors).
- There is super. Ruby looks for the next method in the ancestor chain, which is in the User class.
- The class method returns "Hello from class", then " and module" is added.
In short
include -> module methods become instance methods of the class, the module after the class in the ancestor chain.
extend -> module methods become class or specific object methods (singleton class).
prepend -> module methods become instance methods, but before the class in the ancestor chain, meaning they take precedence over class methods.
This post doesn't have any additions from the author yet.