The implicit block argument passed to a Ruby method can be executed using yield, or its existence can be checked using block_given?. I'm trying to procify this implicit block to pass it to another method.
Is this possible?
(It's access to the implicit block argument I'm asking about. Replacing this with an explicit argument won't cut it.)
You can procify it, and more importantly give it a name so you can reference it, using the & ampersand unary prefix sigil in the parameter list of the method, like so:
#implicit, anonymous, cannot be referenced:
def foo
yield 23 if block_given?
end
foo {|i| puts i }
# 23
#explicit, named, can be referenced:
def bar(&blk)
yield 23 if block_given? # still works
blk.(42) if blk # but now it also has a name and is a `Proc`
# since we have the block available as an object, we can inspect it
p blk.arity, blk.parameters, blk.source_location, blk.binding
b = blk.binding
p b.local_variables.map {|var| [var, b.local_variable_get(var)] }.to_h
end
quux = "Hello"
bar { |a, b, c = nil, d: nil, &e| puts a }
# 23
# 42
# 2
# [[:opt, :a], [:opt, :b], [:opt, :c], [:key, :d], [:block, :e]]
# ["(irb)", 24]
# #<Binding:0x00007fb091051308>
# { :quux => "Hello" }
Those are your two choices:
ProcThere used to be an undocumented trick that was actually an unintended side-effect of how Proc::new was implemented in MRI: Proc::new did not check whether you passed a block or not, it simply assumed that you passed a block and would take the first block off the top of the internal VM stack. So, if you didn't pass a block to Proc::new, it would actually end up creating a Proc for the implicit block that was passed to the method (since that was the one which just happened to be on the top of the stack).
But, that was never portable, never guaranteed, never worked in all Ruby implementations, and AFAIK no longer works in YARV.
You can refer to the block argument via Proc.new. From the docs:
::newmay be called without a block only within a method with an attached block, in which case that block is converted to theProcobject.
Example:
def bar
yield * 2
end
def foo
bar(&Proc.new)
end
foo(123)
#=> 456
Note that Proc.new raises an ArgumentError when called without passing a block.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With