I have the following code:
def call_block
Proc.new.call
my_local_proc = Proc.new { Proc.new.call }
my_local_proc.call
end
call_block { p 'block' }
The output is:
block
block
Can someone explain to me how Proc.new found the block I passed to call_block?
I guess that Proc.new just searches for the closest block and that it`s implemented entirely in C++.
And I have another question: Can something like this be achieve using only ruby? I mean,
can I write a method such that, if not block has been given, takes the block that was passed to the method calling it. Something like:
def bar
if not block_given?
#use the block that has been given to the caller
end
# some code
end
def foo
bar
end
foo { :block }
Proc.newwill use the method’s block if called without one inside a method with one attached. This is documented behavior.To find out how YARV does it, let’s read the source code. Specifically, the
proc_newfunction:This line retrieves a pointer to the block associated with the current control frame.
I believe these control frames implement Ruby’s stack. We are currently inside the
Proc.newcontrol frame, so this would retrieve the pointer to the block given to the method.If the pointer isn’t
NULL, thenProc.newwas passed a block explicitly. What if the pointer isNULL, though?We move up on the stack and try to get its block. In other words, we move up to the caller’s control frame and try to get its block.
Now, if it’s not
NULL, then we pretty much succeeded. If it’s stillNULL, then we can’t create aProc, so we raise anArgumentError.The algorithm boils down to this:
Proc.newwas given a blockSource code altered for readability. Visit linked source file on GitHub for the original.