The goal of this series of articles is to understand how the following code works.

["ruby", "rails"].map(&:upcase)
=> ["RUBY", "RAILS"]

To understand this code, we have to look at code blocks and Procs.

What is a code block

A code block is an optional part of a method invocation.

1.upto(10) { |x| puts x }

In the code above, the code block is the code following 1.upto(10) that is in between the curly braces. It is important to note that a code block is not standalone. It must follow a method invocation. However, a method invocation does not necessarily need to have a code block.

As seen above, a code block is delimited by curly braces. It can also be delimited by the keywords do and end if it’s a multiline code block.

1.upto(10) do |x|
  puts x 

How a method calls a code block

Even if you tack on a code block to a method invocation, how does the method actually call the code within the code block? Look at the following example.

def call_code_block

call_code_block { puts "Calling code in the code block." }
=> Calling code in the code block.

As you can see, the keyword yield calls the code block. What if a code block isn’t given? In that case, the Kernel module provides a block_given? method that returns true if a code block is given. So we can modify our previous code:

def call_code_block
  yield if block_given?

As you noticed earlier on, code blocks can take arguments. Simply pass the arguments to the yield keyword, and they’ll be passed to the code block. Armed with this knowledge, it’s not too hard to write our own version of the each method that classes that include the Enumerable module can call.

class Array
  def my_each
    i = 0
    while i < self.size
      i += 1

[1,2,3].my_each { |x| puts x }
 => [1, 2, 3] # It works!

What is a Proc?

A lot of things in Ruby are objects, but code blocks are not. What if you want to store a code block somewhere and pass it around from method to method? Well, fortunately with Ruby, you can with a Proc object. An instance of Proc is simply an object that represents a code block. You’re familiar with code blocks. They look like the following:

{ "I am a code block." }

Remember, though, a code block cannot stand alone. It must follow a method invocation. The code above is just to remind you what a code block looks like. To store a code block in an object, it must be the code block of a Proc instance.

code_block_object = Proc.new { "I am a code block. I am also the code block of a Proc instance, and I'm stored within that instance." }

In methods, we have the ability to call a code block with the yield keyword. How do we call the code block stored within a Proc object then? Simple: use the call method on that object.

=> "I am a code block. I am also the code block of a Proc instance, and I'm stored within that instance."

The code block within code_block_object was called, and it returned a string because the code block was simply a string. If that is confusing, you can think of code blocks supplied to Proc.new in a different way. The code block supplied to Proc.new becomes the body of a method, which just so happens to be placed in a Proc instance. Let’s look at an example to clear this up.

def print_important_message
  puts "Hello world!" # <--- body of method

Hello world!
=> nil

print_important_message_proc = Proc.new { puts "Hello world!" }
# the code block following Proc.new is the same as the body of the previous method

Hello world!
=> nil

All right, that might have been a lot to take in, so let’s stop for a bit and continue this with part 2 of this series.