Methods in Ruby may not take more than one block. However, you can bind any arbitrary number of blocks as procs (or lambdas) and pass them as regular arguments. You don't get the nice block sugar syntax (do...end), but it works just fine.
In Ruby, blocks (the anonymous functions passed to methods via {|...| } or do ... end syntax) are one of the very few exceptions to the "everything is an object" rule. In order to address them as objects, to_proc is called on them, which produces an instance of Proc, which is essentially a bound block.
Ruby makes special allowances for the fact that it is not uncommon to want to pass a callback or anonymous function to a method. You pass that with the block syntax, then can declare that you want access to the block as a Proc by adding the &block parameter as the last argument in your arg list, which takes your passed anonymous function and binds it to a Proc instance stored in the variable block. This lets you call it (by calling block.call) or pass it on as a normal object instance (as block) or as a block (as &block). You can also implicitly call it with yield without binding it to a Proc.
def debug(obj = nil, &block)
p [:object, obj]
p [:block, block]
end
def pass_block_as_proc(&foo)
puts "Passing as foo"
debug(foo.class)
puts "Passing as &foo"
debug(&foo)
end
block
pass_block_as_proc {}
Output:
Passing "blaz"as }foo
[:object, ##<Proc:0x000055ee3a724a38>]
[:block, =>nil]
Passing Procas &foo
[:object, nil]
[:block, #<Proc:0x000055ee3a724a38>]
So, if we want to pass multiple callbacks to a method, we can't rely on the anonymous block sugar. Instead, we have to pass them as bound Proc instances:
def multiple_procs(value, proc1, proc2)
proc2.call(proc1.call(value))
end
puts multiple_procs "foo",
->(s) { s.upcase },
->(s) { s + s }
# => FOOFOO
This link is some additional useful reading to understand the difference between blocks and procs.