Spec-Zone.ru › Ruby 3.4

Модули

В 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

Обратите внимание: если Outer и Outer::Inner ещё не определены, будет вызвано исключение NameError.

Этот стиль позволяет автору уменьшить количество отступов: вместо трёх уровней отступов достаточно одного. Однако область поиска констант при создании пространства имён с помощью такого синтаксиса отличается от области поиска при использовании более многословного синтаксиса.

Область видимости

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

Методы

Описание определения методов см. в документации по синтаксису методов.

Методы класса можно вызывать напрямую. (Это может немного сбивать с толку, но метод модуля часто называют «методом класса», а не «методом модуля». См. также 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 в качестве получателя. Если приватный метод вызывается с получателем, отличным от литерала self, будет вызвано исключение NoMethodError.

class A
  def without
    m
  end

  def with_self
    self.m
  end

  def with_other
    A.new.m
  end

  def with_renamed
    copy = self
    copy.m
  end

  def m
    1
  end

  private :m
end

a = A.new
a.without      #=> 1
a.with_self    #=> 1
a.with_other   # NoMethodError (private method `m' called for #<A:0x0000559c287f27d0>)
a.with_renamed # NoMethodError (private method `m' called for #<A:0x0000559c285f8330>)

alias и undef

Методы также можно создавать псевдонимами или отменять их определение, однако эти операции не ограничены модулями и классами. Описание см. в разделе о прочих элементах синтаксиса.

Классы

Каждый класс также является модулем, но, в отличие от модулей, класс нельзя добавить как примесь в другой модуль (или класс). Как и модуль, класс можно использовать в качестве пространства имён. Кроме того, класс наследует методы и константы своего суперкласса.

Определение класса

Для создания класса используйте ключевое слово class:

class MyClass
  # ...
end

Если суперкласс не указан, новый класс будет наследовать от Object. Чтобы унаследовать класс от другого класса, укажите <, а затем имя класса:

class MySubclass < MyClass
  # ...
end

Существует специальный класс BasicObject, предназначенный для создания пустого класса с минимальным набором встроенных методов. Вы можете использовать BasicObject, чтобы создать независимую структуру наследования. Дополнительную информацию см. в документации по BasicObject.

Как и модули, классы можно открывать повторно. При повторном открытии класса суперкласс можно не указывать. Если указать суперкласс, отличный от указанного в предыдущем определении, будет вызвана ошибка.

class C
end

class D < C
end

# OK
class D < C
end

# OK
class D
end

# TypeError: superclass mismatch for class D
class D < String
end

Наследование

Любой метод, определённый в классе, можно вызвать из его подкласса:

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 можно вызывать сколько угодно раз.

Классы Singleton

Класс-синглтон объекта (также называемый метаклассом или собственным классом) — это класс, содержащий методы только для данного экземпляра. Получить доступ к классу-синглтону объекта можно с помощью 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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API