All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

Include, Extend, Prepend in Ruby: how they work and what the difference is

Post cover: Include, Extend, Prepend in Ruby: how they work and what the difference is
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?
  1. Ruby calls hello in the Greetings module (because it is first in ancestors).
  2. There is super. Ruby looks for the next method in the ancestor chain, which is in the User class.
  3. 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.

Integer division in Ruby: why 6 / 4 equals 1
28 Oct 14:10

Integer division in Ruby: why 6 / 4 equals 1

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
28 Oct 14:42

How &:to_s works in Ruby and what Symbol#to_proc is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What are Proc and Lambda in Ruby?
28 Oct 15:57

What are Proc and Lambda in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What happens if you call [1, 2, 3].map(&Person)
29 Oct 17:54

What happens if you call [1, 2, 3].map(&Person)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Singleton class (eigenclass) in Ruby: what it is and why it is needed
29 Oct 18:29

Singleton class (eigenclass) in Ruby: what it is and why it is needed

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
==, equal?, eql?, === in Ruby: what they check and when to use them
29 Oct 20:47

==, equal?, eql?, === in Ruby: what they check and when to use them

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
module_function in Ruby: when module methods are available as module methods and as functions
29 Oct 21:53

module_function in Ruby: when module methods are available as module methods and as functions

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What is memoization in Ruby?
30 Oct 10:17

What is memoization in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?
30 Oct 19:55

is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
&& vs and — the difference in Ruby that can break your code
30 Oct 20:23

&& vs and — the difference in Ruby that can break your code

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Variables in Ruby: @, @@ and class instance variable
30 Oct 20:54

Variables in Ruby: @, @@ and class instance variable

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
The difference between blank?, present?, empty? and nil? in Ruby
30 Oct 21:06

The difference between blank?, present?, empty? and nil? in Ruby

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska