Модули
Модули в Ruby служат двум целям: именованное пространство и функциональность миксинов.
Именованное пространство можно использовать для организации кода по пакетам или функциональности, которая отделяет общие имена от конфликтов с другими пакетами. Например, пространство имён IRB предоставляет функциональность для irb, предотвращая коллизию общего имени «Context».
Функциональность миксинов позволяет обмениваться общими методами между несколькими классами или модулями. В Ruby есть модуль миксин Enumerable, который предоставляет множество методов перечисления, основанных на методе each и Comparable, позволяющий сравнивать объекты на основе метода сравнения <=>.
Обратите внимание, что между модулями и классами много сходств. Помимо возможности смешивания модуля, описание модулей ниже также относится к классам.
Определение модуля
Модуль создается с использованием ключевого слова module.
module MyModule # ... end
Модуль можно переоткрывать любое количество раз, чтобы добавить, изменить или удалить функциональность:
module MyModule def my_method end end module MyModule alias my_alias my_method end module MyModule remove_method :my_method end
Переоткрытие классов — очень мощная функция Ruby, но лучше переоткрывать только те классы, которые вы сами создали. Переоткрытие классов, которые вам не принадлежат, может привести к конфликтам имён или трудно диагностируемым ошибкам.
Вложение
Модули могут быть вложены:
module Outer module Inner end end
Многие пакеты создают один внешний модуль (или класс) для предоставления пространства имён для своей функциональности.
Вы также можете определять внутренние модули с помощью ::, при условии, что внешние модули (или классы) уже определены:
module Outer::Inner::GrandChild end
Обратите внимание, что это вызовет NameError, если Outer и Outer::Inner ещё не определены.
Этот стиль позволяет автору сократить отступы. Вместо 3 уровней отступов нужен только один. Однако область поиска констант отличается при создании пространства имён с помощью этого синтаксиса вместо более подробного синтаксиса.
Область видимости
self
self относится к объекту, который определяет текущую область видимости. self изменится при входе в другой метод или при определении нового модуля.
Константы
Доступные константы отличаются в зависимости от вложенности модулей (какой синтаксис использовался для определения модуля). В следующем примере константа A::Z доступна из B, так как A является частью вложенности:
module A
Z = 1
module B
p Module.nesting #=> [A::B, A]
p Z #=> 1
end
end
Однако, если вы используете :: для определения A::B без вложенности внутри A, будет возбуждено исключение NameError, потому что вложенность не включает A.
module A Z = 1 end module A::B p Module.nesting #=> [A::B] p Z #=> raises NameError end
Если константа определена на верхнем уровне, вы можете добавить :: для её обращения:
Z = 0
module A
Z = 1
module B
p ::Z #=> 0
end
end
Методы
Для документации определения методов см. документацию синтаксиса для методов.
Методы класса Class могут вызываться непосредственно. (Это немного запутанно, но метод в модуле часто называют «методом класса», а не «методом модуля». См. также Module#module_function, который может преобразовать метод экземпляра в метод класса.)
Когда метод класса ссылается на константу, он использует те же правила, что и при ссылке на неё вне метода, так как область видимости одинаковая.
Методы экземпляров, определённые в модуле, вызываются только при включении. Эти методы имеют доступ к константам, определённым при включении через список предков:
module A
Z = 1
def z
Z
end
end
include A
p self.class.ancestors #=> [Object, A, Kernel, BasicObject]
p z #=> 1
Видимость
В Ruby есть три типа видимости. По умолчанию — public. Публичный метод может вызываться из любого другого объекта.
Вторая видимость — protected. При вызове защищённого метода отправитель должен быть подклассом получателя или получатель должен быть подклассом отправителя. В противном случае будет возбуждено исключение NoMethodError.
Защищённая видимость чаще всего используется для определения == и других методов сравнения, где автор не хочет раскрывать состояние объекта любому вызывающему объекту и хочет ограничить его только унаследованными классами.
Вот пример:
class A
def n(other)
other.m
end
end
class B < A
def m
1
end
protected :m
end
class C < B
end
a = A.new
b = B.new
c = C.new
c.n b #=> 1 -- C is a subclass of B
b.n b #=> 1 -- m called on defining class
a.n b # raises NoMethodError A is not a subclass of B
Третья видимость — private. Приватный метод не может вызываться с получателем, даже с self. Если приватный метод вызывается с получателем, будет возбуждено исключение NoMethodError.
alias и undef
Вы также можете использовать алиасы или удалять методы, но эти операции не ограничены модулями или классами. Документацию см. в разделе разного синтаксиса.
Классы
Каждый класс также является модулем, но в отличие от модулей класс нельзя смешивать с другим модулем (или классом). Как и модуль, класс можно использовать как пространство имён. Класс также наследует методы и константы от своего родительского класса.
Определение класса
Используйте ключевое слово class для создания класса:
class MyClass # ... end
Если вы не укажете родительский класс, ваш новый класс унаследует от Object. Вы можете унаследовать от другого класса, используя < за которым следует имя класса:
class MySubclass < MyClass # ... end
Существует специальный класс BasicObject, разработанный как пустой класс и включающий минимум встроенных методов. Вы можете использовать BasicObject для создания независимой структуры наследования. См. документацию BasicObject для получения дополнительной информации.
Наследование
Любой метод, определённый в классе, доступен для вызова в его подклассе:
class A
Z = 1
def z
Z
end
end
class B < A
end
p B.new.z #=> 1
То же самое относится к константам:
class A
Z = 1
end
class B < A
def z
Z
end
end
p B.new.z #=> 1
Вы можете переопределить функциональность метода родительского класса, переопределив метод:
class A
def m
1
end
end
class B < A
def m
2
end
end
p B.new.m #=> 2
Если вы хотите вызвать функциональность родительского класса из метода, используйте super.
class A
def m
1
end
end
class B < A
def m
2 + super
end
end
p B.new.m #=> 3
Когда используется без аргументов, super использует аргументы, переданные методу подкласса. Чтобы не передавать аргументы методу родительского класса, используйте super(). Чтобы передать определённые аргументы методу родительского класса, укажите их вручную, например super(2).
super может быть вызван любое количество раз в методе подкласса.
Сиглетон-классы
Сиглетон-класс (также известный как метакласс или собственный класс) объекта — это класс, который содержит методы только для этого экземпляра. Доступ к сиглетон-классу объекта можно получить, используя class <<
object так:
class C end class << C # self is the singleton class here end
Чаще всего сиглетон-класс вызывается так:
class C
class << self
# ...
end
end
Это позволяет определять методы и атрибуты класса (или модуля) без необходимости писать def self.my_method.
Поскольку вы можете открыть сиглетон-класс любого объекта, это означает, что этот блок кода:
o = Object.new def o.my_method 1 + 1 end
эквивалентен этому блоку кода:
o = Object.new
class << o
def my_method
1 + 1
end
end
Оба объекта будут иметь my_method который возвращает 2.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.