Модулі у Crystal мають два призначення:
Перше призначення - створювати простір імен, щоб уникати збігів назв. Однак він також групує код, завдяки чому легше зрозуміти, для чого цей код потрібен.
Друге призначення - визначати міксини, щоб ділитися кодом із типами.
Модулі схожі на класи, але головна відмінність у тому, що їх не можна інстанціювати, а отже, вони не мають змінних екземпляра.
Щоб оголосити модуль, ми використовуємо ключове слово module, а потім назву модуля.
module Foo
end
Простір імен - це спосіб групувати код.
Це дає змогу уникати збігів назв і полегшує розуміння, для чого призначений код.
Коли треба звернутися до константи або класу, розміщених усередині простору імен, ми використовуємо оператор ::.
module Foo
class Bar
end
end
Foo::Bar.new
Це може стати в пригоді, наприклад, коли кільком класам потрібна однакова «базова» функціональність або коли код треба поділити між неповʼязаними класами. Або коли ми хочемо поділитися кодом між класами, які не повʼязані між собою.
Є два різні способи використати модуль як міксин: перший - використати ключове слово include, а другий - ключове слово extend.
Обидва способи зроблять константи доступними для типу, який включає модуль або розширює його.
Ключове слово include зробить усі методи модуля доступними як методи екземпляра в типі, який включає цей модуль.
Ключове слово include має стояти на початку типу, а далі йде назва модуля.
module Foo
def foo
"foo"
end
end
class Bar
include Foo
end
Bar.new.foo # => "foo"
extend працює подібно до include, але замість того, щоб робити методи доступними як методи екземпляра, він робить їх доступними як методи класу.
Ключове слово extend треба писати на початку типу, а далі йде назва модуля.
module Foo
def foo
"foo"
end
end
class Bar
extend Foo
end
Bar.foo # => "foo"
Типовий прийом у Crystal - використовувати в модулі прийом extend self.
Це зробить усі методи модуля доступними як методи класу на самому модулі.
Це означає, що нам не доведеться визначати кожен метод через синтаксис def self.method_name.
module Foo
extend self
def foo
"foo"
end
end
Foo.foo # => "foo"