Все между do и end является блоком. |number| – список параметров, передающихся в блок вызывающей функцией. each – метод на массиве, принимающий блок в качестве параметра.
Вызов этого блока внутри метода each происходит методом yield, проверить наличие блока можно функцией block_given?:
def _if(condition)
condition and yield if block_given?
end
_if(2 > 3) do
puts “Strange..”
end
Можно объявить явно передачу блока внутрь метода:
def set_handler(event, &block)
@handlers ||= {}
@handlers[event] ||= []
@handlers[event] << block
end
def raise_event(event, params)
@handlers[event].each do |block|
block.call(params)
end
end
…
class Subscriber
def subscribe(eventor)
@data = “Hi!”
eventor.set_handler :start do |params|
puts params
puts @data
end
end
end
eventor = Eventor.new
Subscriber.new.subscribe eventor
eventor.raise_event :start, “Some data”
=> Some data
=> Hi!
Надо обратить внимание, что метод таким образом не получится “сохранить”, потому что его упоминание вызовет его выполнение:
Eventor.set_handler :start, my_method
my_method и попытке воспользоваться результатом, как блоком.
yield просто позволяет не вникать в такие вещи, как сохранение куда-либо этих блоков.
Обратите внимание на то, что после вызова eventor.run уже нет ссылок на Subscriber. Кроме той, что осталась в блоке, лежащем в eventor#handlers. Т.е. при формировании блока в методе Subscriber#subscribe внутрь него попала ссылка на копию экземпляра Subscriber.
Дело тут вот в чем. С блоками связано понятие замыкания ( closure ). Дело в том, что внутри этого блока доступны все те переменные, которые доступные в методе вне его. Даже когда этот блок будет выполняться совершенно в другой функции, все равно они будут доступны. Конечно их перечень меняться уже не сможет, но ссылки на них блок будет держать. Т.е. оформленный блоком кусок кода тоже является объектом типа Proc, который держит ссылки на все те переменные, к которым обращался его код в той функции, где он создавался.
Это “явление” называется замыканием. С ним надо быть очень аккуратным, что бы не создавать ситуации утечек памяти. Поэтому иногда вместо блока предпочтительнее воспользоваться eval-ом, который таких проблем не создает.