Ruby blocks explained

My initial plan for this post was to write about the Ruby module Enumerable, but I realized that it would be a good idea to introduce blocks first. We’re going to need to have a good understanding of what blocks are before we talk about Enumerable.

Blocks are a concept that often are difficult for newcomers to Ruby to grasp. They are similar to anonymous functions, lambdas etc. In essence, they are a block of code that you can pass around and execute.

One way to create a code block like that is to create a Proc object.

code_block = proc do |name|
  puts "Hello #{name}!"
# outputs: "Hello Alex!"

Binding code to a variable like this is useful, since it enables us to pass behavior as a parameter to a method. One such example is file manipulation; we can pass a proc object to specify the code that we want to run while the file is open. However, we rarely want to use the code more than once, so we can omit the assignment and inline the code directly in the method invocation. This will look similar to anonymous functions in JavaScript.

open("file.txt", proc do
  # do the work

This doesn’t look very much like Ruby and, fortunately, there is native support in Ruby for a much prettier construct. We ignore the “proc” keyword and we add the block after the parameters instead.

open("file.txt") do 
  # do the work.

That looks a lot better and we got rid of that trailing parenthesis.

Do and end are not the only way to define blocks, you can use curly braces as well. The common convention is to use do and end on multi-line blocks and curly braces on single line code. There is however the “Weirich Convention” where you use curly braces for code where the main purpose is to return a value and do/end to signal that the code’s primary purpose is side-effects.

We can also pass parameters to blocks and we define them within pipes like this:

fruits = ["apple", "banana", "orange"]
fruits.each do |name|
  puts name
# outputs:
# apple
# banana
# orange

To use blocks in your own method you use the keyword “yield”.

def foo
  puts "yielding to the block"
  puts "done with the block"

foo { puts "This is the code in the block" }
# outputs:
# yielding to the block
# This is the code in the block
# done with the block

If you want to send parameters to the block you just use them as parameters in the yield call. In the following example, we yield names to the block and the block prints a greeting.

def people
  yield "Alex"
  yield "Beth"
  yield "Carl"

people do |name|
  puts "Nice to meet you #{name}!"
# outputs:
# Nice to meet you Alex!
# Nice to meet you Beth!
# Nice to meet you Carl!

I hope that this explanation will be useful and serve as a foundation to the things I will talk about in my next post.