Вызов методов
Вызов метода отправляет объекту сообщение, чтобы он выполнил некоторую работу.
В Ruby сообщение объекту отправляется так:
my_method()
Обратите внимание, что круглые скобки необязательны:
my_method
За исключением случаев, когда использование и отсутствие круглых скобок приводит к различиям, в этом документе при наличии аргументов используются круглые скобки, чтобы избежать путаницы.
В этом разделе рассматривается только вызов методов. См. также документацию по синтаксису определения методов.
Получатель
self является получателем по умолчанию. Если вы не укажете получателя, будет использоваться self. Чтобы указать получателя, используйте .:
my_object.my_method
Это отправляет сообщение my_method объекту my_object. Получателем может быть любой объект, но в зависимости от видимости метода отправка сообщения может вызвать NoMethodError.
Для обозначения получателя также можно использовать ::, но этот способ используется редко из-за возможной путаницы с ::, применяемым для пространств имён.
Цепочка вызовов методов
Можно «связывать» вызовы методов в цепочку, сразу вызывая следующий метод после предыдущего.
В этом примере в цепочку объединены методы Array#append и Array#compact:
a = [:foo, 'bar', 2] a1 = [:baz, nil, :bam, nil] a2 = a.append(*a1).compact a2 # => [:foo, "bar", 2, :baz, :bam]
Подробности:
-
Первый метод
mergeсоздаёт копиюa, по отдельности добавляет каждый элементa1в эту копию и возвращает[:foo, "bar", 2, :baz, nil, :bam, nil]
-
Следующий в цепочке метод
compactсоздаёт копию этого возвращённого значения, удаляет элементы со значениемnilи возвращает[:foo, "bar", 2, :baz, :bam]
В цепочку можно объединять методы из разных классов. В этом примере в цепочку объединены методы Hash#to_a и Array#reverse:
h = {foo: 0, bar: 1, baz: 2}
h.to_a.reverse # => [[:baz, 2], [:bar, 1], [:foo, 0]]
Подробности:
-
Первый метод
Hash#to_aпреобразуетaв массив и возвращает[[:foo, 0], [:bar, 1], [:baz, 2]]
-
Следующий в цепочке метод
Array#reverseсоздаёт копию этого возвращённого значения, разворачивает её и возвращает[[:baz, 2], [:bar, 1], [:foo, 0]]
Оператор безопасной навигации
&., называемый «оператором безопасной навигации», позволяет пропустить вызов метода, если получатель — nil. Он возвращает nil и не вычисляет аргументы метода, если вызов пропущен.
REGEX = /(ruby) is (\w+)/i "Ruby is awesome!".match(REGEX).values_at(1, 2) # => ["Ruby", "awesome"] "Python is fascinating!".match(REGEX).values_at(1, 2) # NoMethodError: undefined method `values_at' for nil:NilClass "Python is fascinating!".match(REGEX)&.values_at(1, 2) # => nil
Это позволяет легко объединять в цепочку методы, которые могут возвращать пустое значение. Обратите внимание, что &. пропускает только один следующий вызов, поэтому в более длинной цепочке необходимо добавлять оператор на каждом уровне:
"Python is fascinating!".match(REGEX)&.values_at(1, 2).join(' - ')
# NoMethodError: undefined method `join' for nil:NilClass
"Python is fascinating!".match(REGEX)&.values_at(1, 2)&.join(' - ')
# => nil
Аргументы
При отправке сообщения используются три типа аргументов: позиционные аргументы, именованные аргументы (или аргументы с ключевыми словами) и аргумент-блок. Для каждого отправляемого сообщения можно использовать один, два или все типы аргументов, но передавать их нужно именно в этом порядке.
В Ruby все аргументы передаются по ссылке и вычисляются сразу.
Аргументы разделяются ,:
my_method(1, '2', :three)
Аргументом может быть выражение или аргумент-хеш:
'key' => value
либо аргумент с ключевым словом:
key: value
Hash и аргументы с ключевыми словами должны идти подряд и располагаться после всех позиционных аргументов, но их можно смешивать:
my_method('a' => 1, b: 2, 'c' => 3)
Позиционные аргументы
Позиционные аргументы сообщения указываются после имени метода:
my_method(argument1, argument2)
Во многих случаях при отправке сообщения круглые скобки не нужны:
my_method argument1, argument2
Однако круглые скобки необходимы, чтобы избежать неоднозначности. Это вызовет SyntaxError, поскольку Ruby не понимает, какому аргументу метода следует передать argument3:
method_one argument1, method_two argument2, argument3
Если определение метода содержит *argument, дополнительные позиционные аргументы будут назначены argument в методе в виде Array.
Если определение метода не включает аргументы с ключевыми словами, аргументы с ключевыми словами или типа «хеш» назначаются последнему аргументу как единый хеш:
def my_method(options)
p options
end
my_method('a' => 1, b: 2) # prints: {'a'=>1, :b=>2}
Если передано слишком много позиционных аргументов, возникает ArgumentError.
Позиционные аргументы по умолчанию
Если в методе определены аргументы по умолчанию, передавать методу все аргументы не требуется. Ruby подставит недостающие аргументы по порядку.
Сначала рассмотрим простой случай, когда аргументы по умолчанию указаны справа. Рассмотрим следующий метод:
def my_method(a, b, c = 3, d = 4) p [a, b, c, d] end
Здесь c и d имеют значения по умолчанию, которые Ruby подставит за вас. Если отправить этому методу только два аргумента:
my_method(1, 2)
Ruby выведет [1, 2, 3, 4].
Если отправить три аргумента:
my_method(1, 2, 5)
Ruby выведет [1, 2, 5, 4]
Ruby подставляет недостающие аргументы слева направо.
Ruby позволяет указывать значения по умолчанию и в середине списка позиционных аргументов. Рассмотрим более сложный метод:
def my_method(a, b = 2, c = 3, d) p [a, b, c, d] end
Здесь b и c имеют значения по умолчанию. Если отправить этому методу только два аргумента:
my_method(1, 4)
Ruby выведет [1, 2, 3, 4].
Если отправить три аргумента:
my_method(1, 5, 6)
Ruby выведет [1, 5, 3, 6].
Объяснить это словами сложно и запутанно. Вместо этого я опишу происходящее через переменные и значения.
Сначала 1 присваивается a, затем 6 присваивается d. Остаются только аргументы со значениями по умолчанию. Поскольку 5 ещё не присвоено значение, оно передаётся в b, а для c используется значение по умолчанию 3.
Аргументы с ключевыми словами
Аргументы с ключевыми словами указываются после любых позиционных аргументов и разделяются запятыми, как позиционные аргументы:
my_method(positional1, keyword1: value1, keyword2: value2)
Для всех неуказанных аргументов с ключевыми словами используются значения по умолчанию из определения метода. Если указан аргумент с ключевым словом, которого нет в списке метода, а определение метода не допускает произвольные аргументы с ключевыми словами, возникает ArgumentError.
Значение аргумента с ключевым словом можно не указывать; в этом случае значение будет получено из контекста по имени ключа
keyword1 = 'some value' my_method(positional1, keyword1:) # ...is the same as my_method(positional1, keyword1: keyword1)
Обратите внимание: если круглые скобки метода тоже опущены, порядок разбора может оказаться неожиданным:
my_method positional1, keyword1: some_other_expression # ...is actually parsed as my_method(positional1, keyword1: some_other_expression)
Аргумент-блок
Аргумент-блок передаёт методу замыкание из области видимости вызывающего кода.
При отправке сообщения методу аргумент-блок всегда указывается последним. Блок передаётся методу с помощью do ... end или { ... }:
my_method do # ... end
или:
my_method {
# ...
}
do end имеет более низкий приоритет, чем { }, поэтому:
method_1 method_2 {
# ...
}
Блок передаётся в method_2, тогда как:
method_1 method_2 do # ... end
Блок передаётся в method_1. Обратите внимание: в первом случае, если используются круглые скобки, блок передаётся в method_1.
Блок принимает аргументы от метода, которому он был передан. Аргументы определяются так же, как аргументы метода. Аргументы блока указываются в | ... | после открывающего do или {:
my_method do |argument1, argument2| # ... end
Локальные аргументы блока
Также можно объявить локальные для блока аргументы с помощью ; в списке аргументов блока. Присваивание значения локальному аргументу блока не переопределяет локальные аргументы вне блока в области видимости вызывающего кода:
def my_method
yield self
end
place = "world"
my_method do |obj; place|
place = "block"
puts "hello #{obj} this is #{place}"
end
puts "place is: #{place}"
Будет выведено:
hello main this is block place is world
Таким образом, переменная place в блоке — не та же переменная place, что и вне блока. Если удалить ; place из аргументов блока, получится следующий результат:
hello main this is block place is block
Распаковка позиционных аргументов
Рассмотрим следующий метод:
def my_method(argument1, argument2, argument3) end
С помощью оператора * (или splat-оператора) можно преобразовать Array в список аргументов:
arguments = [1, 2, 3] my_method(*arguments)
или:
arguments = [2, 3] my_method(1, *arguments)
Оба варианта эквивалентны следующему:
my_method(1, 2, 3)
Оператор распаковки * можно применять к любому объекту, а не только к массивам. Если объект отвечает на вызов метода to_a, этот метод вызывается и должен вернуть Array; элементы этого массива передаются как отдельные позиционные аргументы:
class Name
def initialize(name)
@name = name
end
def to_a = @name.split(' ')
end
name = Name.new('Jane Doe')
p(*name)
# prints separate values:
# Jane
# Doe
Если у объекта нет метода to_a, сам объект передаётся как один аргумент:
class Name
def initialize(name)
@name = name
end
end
name = Name.new('Jane Doe')
p(*name)
# Prints the object itself:
# #<Name:0x00007f9d07bca650 @name="Jane Doe">
Это позволяет полиморфно обрабатывать один или несколько аргументов. Также обратите внимание, что для nil определён NilClass#to_a, возвращающий пустой массив, поэтому возможна условная распаковка:
my_method(*(some_arguments if some_condition?))
Если метод to_a существует, но не возвращает Array, при распаковке возникнет ошибка:
class Name
def initialize(name)
@name = name
end
def to_a = @name
end
name = Name.new('Jane Doe')
p(*name)
# can't convert Name to Array (Name#to_a gives String) (TypeError)
Также можно использовать ** (описанный далее), чтобы преобразовать Hash в аргументы с ключевыми словами.
Если количество объектов в Array не совпадает с количеством аргументов метода, возникает ArgumentError.
Если splat-оператор стоит первым в вызове, необходимо использовать круглые скобки, чтобы избежать неоднозначности между оператором распаковки и оператором умножения. В этом случае Ruby выдаёт предупреждение в подробном режиме:
my_method *arguments # warning: '*' interpreted as argument prefix my_method(*arguments) # no warning
Распаковка аргументов с ключевыми словами
Рассмотрим следующий метод:
def my_method(first: 1, second: 2, third: 3) end
С помощью оператора ** (оператора распаковки ключевых слов) можно преобразовать Hash в аргументы с ключевыми словами:
arguments = { first: 3, second: 4, third: 5 }
my_method(**arguments)
или:
arguments = { first: 3, second: 4 }
my_method(third: 5, **arguments)
Оба варианта эквивалентны следующему:
my_method(first: 3, second: 4, third: 5)
Оператор распаковки ** можно применять к любому объекту, а не только к хешам. Если объект отвечает на вызов метода to_hash, этот метод вызывается и должен вернуть Hash; элементы этого хеша передаются как аргументы с ключевыми словами:
class Name
def initialize(name)
@name = name
end
def to_hash = {first: @name.split(' ').first, last: @name.split(' ').last}
end
name = Name.new('Jane Doe')
p(**name)
# Prints: {name: "Jane", last: "Doe"}
В отличие от оператора *, ** вызывает ошибку, если применяется к объекту, который не отвечает на вызов to_hash. Единственное исключение — nil: этот объект явно не определяет данный метод, но его всё равно можно использовать при распаковке **, не добавляя никаких аргументов с ключевыми словами.
Это также позволяет выполнять условную распаковку:
my_method(some: params, **(some_extra_params if pass_extra_params?))
Как и оператор *, ** вызывает ошибку, если объект отвечает на вызов to_hash, но не возвращает Hash.
Если в определении метода используется оператор распаковки ключевых слов для сбора произвольных аргументов с ключевыми словами, они не будут собраны с помощью *:
def my_method(*a, **kw) p arguments: a, keywords: kw end my_method(1, 2, '3' => 4, five: 6)
Вывод:
{:arguments=>[1, 2], :keywords=>{'3'=>4, :five=>6}}
Proc: преобразование в блок
Рассмотрим метод, использующий блок:
def my_method yield self end
С помощью оператора & (оператора преобразования в блок) можно преобразовать proc или lambda в аргумент-блок:
argument = proc { |a| puts "#{a.inspect} was yielded" }
my_method(&argument)
Если оператор преобразования в блок стоит первым в вызове, необходимо использовать круглые скобки, чтобы избежать предупреждения:
my_method &argument # warning my_method(&argument) # no warning
Поиск метода
При отправке сообщения Ruby ищет для получателя метод, имя которого совпадает с именем сообщения. Методы хранятся в классах и модулях, поэтому при поиске обходятся именно они, а не сами объекты.
Порядок поиска метода для класса или модуля получателя R:
-
Модули, добавленные перед
R, в обратном порядке -
Поиск подходящего метода в
R -
Подключённые модули
Rв обратном порядке
Если R — это класс с суперклассом, поиск повторяется для суперкласса R, пока метод не будет найден.
После нахождения совпадения поиск метода прекращается.
Если совпадение не найдено, поиск повторяется с начала, но для method_missing. По умолчанию method_missing — это BasicObject#method_missing, который при вызове вызывает NameError.
Если активны рефинменты (экспериментальная функция), поиск метода изменяется. Подробнее см. в документации по рефинментам.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.