Include, Extend, Prepend у Ruby: як вони працюють і в чому різниця

Обкладинка допису: Include, Extend, Prepend у Ruby: як вони працюють і в чому різниця
У Ruby модулі використовуються для повторного використання коду. Метод додавання модуля впливає на де і як методи з модуля стають доступними.

include - додає методи як методи екземпляра

  • Методи модуля стають доступними для всіх об’єктів класу.
  • Використовують, коли хочуть, щоб методи були у екземплярів класу.
module Greetings
  def hello
    "Hi!"
  end
end

class User
  include Greetings
end

u = User.new
u.hello # => "Hi!"
Методи модуля з’являються у ланцюжку предків класу (ancestors) після самого класу, тобто можна перевизначати їх у класі. Але давайте розберемо це детальніше. У Ruby кожен клас має ланцюжок предків (ancestors) - порядок, у якому Ruby шукає методи при виклику на об’єкті.
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]
Коли ти робиш include Greetings, Ruby вставляє модуль після класу у ланцюжку предків. Або якщо простіше запам'ятати - вище класу.
Це означає, що методи класу перекривають методи модуля, якщо вони мають однакові імена.
u = User.new
u.hello # => "Hi from class"
Ruby спочатку шукає метод у самому класі (User). Потім, якщо не знаходить, перевіряє модулі у ланцюжку (Greetings), далі предків (Object, Kernel).

extend - додає методи як методи класу

  • Методи модуля стають доступними безпосередньо на класі, а не на екземплярах.
  • Зручно для додавання "класових методів" без явного self.method.
module Tools
  def info
    "Class method!"
  end
end

class User
  extend Tools
end

User.info # => "Class method!"
User.new.info # => NoMethodError
Фактично Ruby додає методи в singleton class об’єкта (клас сам по собі - об’єкт).

prepend - додає методи перед класом у ланцюжку предків

  • Методи модуля з’являються перед класом у ланцюжку предків.
  • Це означає, що методи модуля можуть перевизначати методи класу.
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]
Ключовий момент: Greetings стоїть перед класом User.
Ruby шукає методи у порядку, який показує ancestors.
Тому коли викликаєш u.hello, Ruby спершу бачить метод у модулі Greetings, а вже потім у класі User.
u = User.new
u.hello # => "Hello from module"
Використання super у модулі
Якщо у модуля викликати super, Ruby піде далі по ланцюжку предків і виконає метод класу:
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"
Чому так?
  1. Ruby викликає hello у модулі Greetings (бо він перший у ancestors).
  2. Там є super. Ruby шукає наступний метод у ланцюжку предків, тобто в класі User.
  3. Метод класу повертає "Hello from class", потім додається " and module".

Якщо коротко

include -> методи модуля стають методами екземплярів класу, модуль після класу у ланцюжку предків.
extend -> методи модуля стають методами класу або конкретного об’єкта (singleton class).
prepend -> методи модуля стають методами екземплярів, але перед класом у ланцюжку предків, тобто мають пріоритет над методами класу.

Цей допис поки що не має жодних доповнень від автора/ки.

Цілочисельне ділення у Ruby: чому 6 / 4 дорівнює 1
28 жовт., 14:10

Цілочисельне ділення у Ruby: чому 6 / 4 дорівнює 1

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
28 жовт., 14:42

Як працює &:to_s у Ruby і що таке Symbol#to_proc

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Що таке Proc і Lambda в Ruby?
28 жовт., 15:57

Що таке Proc і Lambda в Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Що відбувається, якщо викликати [1, 2, 3].map(&Person)
29 жовт., 17:54

Що відбувається, якщо викликати [1, 2, 3].map(&Person)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Singleton class (eigenclass) у Ruby: що це і навіщо потрібно
29 жовт., 18:29

Singleton class (eigenclass) у Ruby: що це і навіщо потрібно

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
==, equal?, eql?, === у Ruby: що перевіряють і коли використовувати
29 жовт., 20:47

==, equal?, eql?, === у Ruby: що перевіряють і коли використовувати

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
module_function у Ruby: коли методи модуля доступні як модульні та як функції
29 жовт., 21:53

module_function у Ruby: коли методи модуля доступні як модульні та як функції

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Що таке memoization в Ruby?
30 жовт., 10:17

Що таке memoization в Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
is_a?, kind_of?, instance_of? — як Ruby перевіряє тип об’єкта?
30 жовт., 19:55

is_a?, kind_of?, instance_of? — як Ruby перевіряє тип об’єкта?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
&& vs and — різниця в Ruby, яка може зламати ваш код
30 жовт., 20:23

&& vs and — різниця в Ruby, яка може зламати ваш код

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Змінні у Ruby: @, @@ та class instance variable
30 жовт., 20:54

Змінні у Ruby: @, @@ та class instance variable

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Різниця між blank?, present?, empty? та nil? у Ruby
30 жовт., 21:06

Різниця між blank?, present?, empty? та nil? у Ruby

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