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's the difference

Post cover: Include, Extend, Prepend in Ruby: how they work and what's the difference
This content has been automatically translated from Ukrainian.
In Ruby, modules are used to reuse code. Addition method module affects where and how methods from the module become available.

include - adds methods as instance methods

  • Module methods are becoming available for all class objects.
  • Used when they want the 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 ancestral chain of the class (ancestors) after the class itself, that is, you can override them in the class. But let's take a closer look at it. In Ruby, every class has chain of ancestors (ancestors) - the order in which Ruby searches for methods when called 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 make include Greetings, Ruby inserts the module after class in the chain of ancestors. Or if it's easier to remember - above class.
This means that class methods overlap module methods, if they have the same names.
u = User.new
u.hello # => "Hi from class"
Ruby first searches for the method in the class itself (User). Then, if it does not find it, it checks the modules in the chain (Greetings), then the ancestors (Object, Kernel).

extend - adds methods as class methods

  • Module methods are becoming available directly on the classroom, 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
Ruby actually adds methods in the singleton class of the object (the class itself is an object).

prepend - Adds pre-class methods in the ancestral chain

  • Module methods are emerging before the class in the chain of ancestors.
  • 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]
Key point: Greetings is in front of the User class.
Ruby looks for methods in the order that ancestors shows.
So when you call u.hello, Ruby first sees the method in the Greetings module, and only then in the User class.
u = User.new
u.hello # => "Hello from module"
Using super in the module
If the module is called super, Ruby will go further along the chain of ancestors and will 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 so?
  1. Ruby calls hello in the Greetings module (because it is the first in ancestors).
  2. There is a super. Ruby searches for the following method in the ancestral chain, that is, in the User class.
  3. The class method returns "Hello from class", then "and module" is added.

In short

include -> module methods become methods copies class, module after class in the chain of ancestors.
extend -> module methods become methods class or specific object (singleton class).
prepend -> module methods become methods copies, but before class in the chain of ancestors, that is, they have priority over class methods.

This post doesn't have any additions from the author yet.

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

Integer division in Ruby: why 6/4 is 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 is checked and when to use
29 Oct 20:47

==, equal?, eql?, === in Ruby: what is checked and when to use

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

module_function in Ruby: When module methods are available as modular 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 object type?
30 Oct 19:55

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

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

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

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

The variables in Ruby are @, @@ and class instance variable

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

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

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