myproc = Proc.new { |a| puts 'Параметр равен #{а}' }
myproc.call(99) # Параметр равен 99
Кроме того, Ruby автоматически создает объект Proc, когда метод, последний параметр которого помечен амперсандом, вызывается с блоком в качестве параметра:
def take_block(x, &block)
puts block.class
x.times {|i| block[i, i*i] }
end
take_block(3) { |n,s| puts '#{n} в квадрате равно #{s}' }
В этом примере демонстрируется также применение квадратных скобок как синонима метода call
. Вот что выводится в результате исполнения:
Proc
0 в квадрате 0
1 в квадрате 1
2 в квадрате 4
Объект Proc
можно передавать методу, который ожидает блок, предварив имя знаком &
:
myproc = proc { |n| print n, '... ' }
(1..3).each(&myproc) # 1... 2... 3...
Ruby позволяет также превратить метод в объект. Исторически для этого применяется метод Object#method
, который создает объект класса Method
как замыкание в конкретном объекте.
str = 'cat'
meth = str.method(:length)
a = meth.call # 3 (длина 'cat')
str << 'erpillar'
b = meth.call # 11 (длина 'caterpillar')
str = 'dog'
# Обратите внимание на следующий вызов! Переменная str теперь ссылается
# на новый объект ('dog'), но meth по-прежнему связан со старым объектом.
с = meth.call # 11 (длина 'caterpillar')
Начиная с версии Ruby 1.6.2, можно также применять метод Module#instance_method
для создания объектов UnboundMethod
. С их помощью представляется метод, ассоциированный с классом, а не с конкретным объектом. Прежде чем вызывать объект UnboundMethod
, нужно связать его с каким-то объектом. Результатом операции связывания является объект Method
, который можно вызывать как обычно:
umeth = String.instance_method(:length)
m1 = umeth.bind('cat')
m1.call # 3
m2 = umeth.bind('caterpillar')
m2.call # 11
Явное связывание делает объект UnboundMethod
интуитивно более понятным, чем Method
.
11.2.7. Как работает включение модулей?
Когда модуль включается в класс, Ruby на самом деле создает прокси-класс, являющийся непосредственным родителем данного класса. Возможно, вам это покажется интуитивно очевидным, возможно, нет. Все методы включаемого модуля «маскируются» методами, определенными в классе.
module MyMod
def meth
'из модуля'
end
end
class ParentClass
def meth
'из родителя'
end
end
class ChildClass < ParentClass
include MyMod
def meth
'из потомка'
end
end
x = ChildClass.new p
p x.meth # Из потомка.
Выглядит это как настоящее наследование: все, что потомок переопределил, становится действующим определением вне зависимости от того, вызывается ли include
до или после переопределения.
Вот похожий пример, в котором метод потомка вызывает super
, а не просто возвращает строку. Как вы думаете, что будет возвращено?
# Модуль MyMod и класс ParentClass не изменились.
class ChildClass < ParentClass
include MyMod
def meth
'Из потомка: super = ' + super
end
end
x = ChildClass.new
p x.meth # Из потомка: super = из модуля
Отсюда видно, что модуль действительно является новым родителем класса. А что если мы точно