Reputation: 3000
Suppose I have a method that takes args and a block:
def yield_if_widget(*args, &block)
if args[0].is_a?(Widget)
block.call
end
end
I can call this method with arguments and a block:
yield_if_widget(Widget.new) do
puts "I like widgets"
end
But what if I have another method that prepares the arguments and the block:
def widget_and_block
args = [Widget.new]
block = proc{ puts "I like widgets" }
[args, block]
end
And I want to be able to pass it directly to the first method:
yield_if_widget(*widget_and_block)
Is this possible? How? Assume that yield_if_widget
is defined in a library and monkey-patching it is not an option.
Upvotes: 4
Views: 103
Reputation: 121000
To make ruby understand that the parameter in a call to a method is a block, a commercial and must be put in front of it.
def widget_and_block
args = [Widget.new]
block = proc{ puts "I like widgets" }
[args, block]
end
wab = widget_and_block
# ⇓
yield_if_widget(*wab.first, &wab.last)
Oneliner (it won’t return what yiw
returns):
widget_and_block.tap { |args, cb| yield_if_widget *args, &cb }
UPD Basically, ampersand in method call is used to say “hey, convert this to proc and use as codeblock” to ruby. It’s a syntax part of the language, like you have to put array content inside square brackets, not curly. That’s why an ampersand should exist in the source code.
On the other hand, whether you were free to modify the yield_if_widget
and remove ampersand from parameter list:
-def yield_if_widget(*args, &block)
+def yield_if_widget(*args, block)
the code would work as expected, since the proc instance is passed as the last parameter and calling call
method on it is very naturally permitted.
Please also note, that prepending an ampersand to the last parameter to method call forces #to_proc
method to be called on it, like in:
[1,2,3].reduce &:+
#⇒ 6
The magic above works because Symbol
class has it’s own implementation of #to_proc
method.
Upvotes: 2
Reputation: 5949
you can't do it in one line (as far as I can tell). You need temporary variables.
given these methods:
def prepare
return [4, proc { puts "called" }]
end
def run(a, &block)
puts a
block.call
end
You can pass the return values from prepare
to your method like so:
i,blk = prepare()
run(i, &blk)
Upvotes: 0