Understanding Ruby Code Blocks and Procs (Part 2)
Code block and Proc recap
A method invocation can include up to five parts: the receiver, the dot operator, the method name, the arguments, and the code block.
In the method invocation above, the code block was the code delimited by the curly braces.
Code blocks are not objects, but they can be stored in objects. These special objects are instances of the Proc class. The following is one way to store a code block in a Proc instance.
The relationship between code blocks and Procs
One thing to keep clear is a code block is not a Proc and a Proc is not a code block. They are different. Here’s how you convert a code block into a Proc
In the code above, you can imagine the following steps taking place:
turn_into_proc
is invoked with a code block:
The code block becomes the code block of Proc.new
The new Proc instance is stored in code_block
.
How does turn_into_proc
know that code_block
should be a Proc object
and not a regular argument? The & operator. The ampersand tells the parser to
look at the method invocation’s code block and convert it into a Proc object.
How to convert a Proc object into a code block.
Just as the & operator is used to convert a code block into a Proc object, it can also be used to convert a Proc object into a code block.
What is to_proc?
When using a Proc object to stand in for a code block, the & operator does two things:
- Calls the Proc object’s
to_proc
method. - Informs the parser that the resulting Proc object (after
to_proc
has been called) is acting as a code block for the method invocation.
Let’s imagine what this looks like in code form:
Understanding our initial piece of code
Finally, we’ve arrived at the point where we can understand how the code above works.
In the previous section, we learned that the & operator calls to_proc
on the
object that is standing in for a code block. We’ve been working with Proc objects
standing in for code blocks, but what if the object were a Symbol? If it’s a Symbol,
Symbol#to_proc is called. Symbol#to_proc can be implemented like this:
So, with that piece of code, we can now see how to understand our initial piece of code.
The code above makes perfect sense! It took a long explanation to arrive at this point, but it was worth the wait.