ЗмістНатисність на посилання, щоб перейти до потрібного місця
У Ruby модулі дозволяють організовувати код, але іноді потрібно, щоб метод був доступний як для екземплярів модуля, так і безпосередньо з модуля (як функція). Для цього використовується module_function.
Як працює module_function
- Робить метод приватним для екземплярів модуля (про private читайте тут).
- Додає також "модульну копію" методу, яку можна викликати без створення об’єкта.
module MathHelper
def square(x)
x * x
end
module_function :square
end
Тепер можна викликати метод безпосередньо через модуль:
MathHelper.square(5) # => 25
Або використовувати його всередині інших методів модуля (як приватний):
module MathHelper
def cube(x)
square(x) * x
end
module_function :cube
end
MathHelper.cube(3) # => 27
Особливості
Приватний для інстансів модуля:
include MathHelper square(5) # => NoMethodError (бо square став приватним)
Підходить для утиліт
module_function часто використовують у "статичних утилітах", щоб не створювати об’єкт модуля для виклику функцій.
Якщо коротко:
- module_function робить метод одночасно:
- Приватним для інстансів (через include)
- Доступним як функцію через модуль
MathHelper.square(5) # працює include MathHelper square(5) # помилка
Це зручно для модулів, які мають набір утиліт, як у стандартних бібліотеках Ruby (Math, Enumerable для прикладу).
Але якщо звернете увагу на пункт - приватним для інстансів (через include), то можете спитати, а що з extend та prepend (про ці методи писав тут)?
- Метод модуля стає приватним для екземплярів і доступним через сам модуль.
- Виклик через include не працює напряму (метод приватний).
- Виклик через extend працює як класовий метод.
- Виклик через prepend не працює на об’єкті (метод приватний).
Все ще важко зрозуміти? Давайте розглянемо більш детально:
module_function робить дві речі одночасно:
-
Приватним для екземплярів
- Якщо ти включаєш модуль (include) або робиш prepend, метод стає приватним для всіх об’єктів, тобто ти не можеш викликати його просто так: obj.method - буде помилка.
- Ruby так робить, щоб ви не змогли випадково викликати цей метод на екземплярі.
-
Доступним через сам модуль
- Ruby створює копію методу на рівні модуля.
- Тому можна викликати: ModuleName.method - це працює як "статична функція".
Простий приклад:
module Utils
def greet
"Hello"
end
module_function :greet
end
# Через модуль можна
Utils.greet # => "Hello"
# Через include — не можна, бо приватний
include Utils
greet # => помилка NoMethodError
- Чому extend працює? extend додає методи модуля у singleton class класу або об’єкта. Там метод викликається через клас/об’єкт, а не через екземпляр, тобто працює.
- Чому prepend не працює? prepend вставляє метод у ланцюжок предків, але оригінальний метод залишається приватним -> об’єкт не може викликати його напряму.
Цей допис поки що не має жодних доповнень від автора/ки.