Blocks are a fairly basic part of ruby. They're delimited by either do |arg0,arg1| ... end
or { |arg0,arg1,arg2| ... }
.
They allow you to specify a callback to pass to a method.
This callback can be invoked two ways - either by capturing
it by specifying a final argument prefixed with &
, or by
using the yield
keyword:
irb> def meth_captures(arg, &block)
block.call( arg, 0 ) + block.call( arg.reverse , 1 )
end
#=> nil
irb> meth_captures('pony') do |word, num|
puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
word + num.to_s
end
in callback! word = "pony" num = 0
in callback! word = "ynop" num = 1
#=> "pony0ynop1"
irb> def meth_yields(arg)
yield(arg, 0) + yield(arg.upcase, 1)
end
#=> nil
irb> meth_yields('frog') do |word, num|
puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
word + num.to_s
end
in callback! word = "frog", num = 0
in callback! word = "FROG", num = 1
#=> "frog0FROG1"
Note that our callback was the same in each case - we can remove
repetition by saving our callback in an object, and then passing it to each
method. This can be done using lambda
to capture the callback in an object,
and then passed to a method by prefixing it with &
.
irb> callback = lambda do |word, num|
puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
word + num.to_s
end
#=> #<Proc:0x0052e3d8@(irb):22>
irb> meth_captures('unicorn', &callback)
in callback! word = "unicorn", num = 0
in callback! word = "nrocinu", num = 1
#=> "unicorn0nrocinu1"
irb> meth_yields('plate', &callback)
in callback! word = "plate", num = 0
in callback! word = "PLATE", num = 1
#=> "plate0PLATE1"
It's important to understand the different uses of &
here as a prefix to the last argument of a function
- in a function definition, it captures any passed block into that object
- in a function call, it expands the given callback object into a block
If you look around blocks are used all over the place, especially in iterators, like Array#each
.