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:
Proc
There 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:
::new
may be called without a block only within a method with an attached block, in which case that block is converted to theProc
object.
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.
Take a look at this answer . In your case it would be something like:
def outer
wrapper = lambda { |something|
p 'Do something crazy in this wrapper'
yield(something)
}
other_method(&wrapper)
end
def other_method
yield(5)
end
outer { |x| puts x + 3 }
With that you get:
"Do something crazy in this wrapper"
8
=> nil
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.