Why Ruby blocks exist

Exploring Ruby's "each" method

It seems like more and more languages these days are getting support for closures in one form or another. (Even Java is getting in on the game, finally.) Ruby has had closure-like structures called blocks since its early days, though, and they’re central to the language. Used properly, they can reduce repetition and even make coding less error-prone. Understanding blocks can give you some great ideas to take home to your language of choice. (Or, who knows? Maybe you’ll decide you like coding in Ruby better!)

Today, we’re going to show you just one of the many Ruby methods that use blocks: each. We’ll show you some repetitive code that, in most languages, would be hard to refactor. But using each, we’ll quickly wring that repetition out.

The repeating loop

Let’s say a client has asked us to implement an invoicing system for them. The first requested feature is the ability to take all the prices on an order and total them.

Our client’s code generates an array of item prices for an invoice:

We need to define a method that takes such an array, and totals the prices. As in most other languages, we could just loop through the array items, and add them to a variable’s value as we go.

Output:

The Ruby syntax you see here shouldn’t seem too strange if you’re coming from another language. The method definition is marked with def ... end. The loop is marked by while ... end; we break out of it as soon as we pass the last index in the array. The += operator adds a value to the existing amount in a variable.

Now, we’ll need a second method that can process a refund for lost orders. It needs to loop through the invoice prices, and subtract each amount from the customer’s account balance.

Output:

The refund method looks highly similar to total; we’re just working with negative values instead of positive ones.

We also need a third method that will reduce each item’s price by 1/3 and print the savings.

Output:

Between these 3 methods, there’s a lot of duplicated code, and it all seems to be related to looping through the array of prices. It’s definitely a violation of the DRY (Don’t Repeat Yourself) principle. It would be nice if we could extract the repeated code out into another method, and have totalrefund, and show_discounts call it…

The problem is: how will you set up the variables you need prior to running the loop? And how will you execute the code you need within the loop?

Blocks

If only we could pass a chunk of code into a method, to be executed while that method runs, our problem would be solved. We could rely on do_something_with_every_item to handle the looping for us, and pass it chunks of code that add all the prices, or subtract them, or calculate a discount. Too bad most languages don’t have a simple means to do such a thing.

Ruby has a way, though: blocks! A block is basically a chunk of code that you associate with a method call. While the method runs, it can invoke the block one or more times.

To declare a method that takes a block, include the yield keyword. If you want to pass one or more parameters to the block, include them as arguments to yield.

When you call your method, you can provide any code you want within a block. The block sits after the method’s argument list, looking kind of like an additional argument.

block

Here’s the method call and block in action:

Output:

When Ruby encounters the yield keyword while executing your method, it passes control from the method to the block. The arguments to yield get placed into those block parameters that you specify within the vertical bars. These parameters will live only as long as the block does, and you can act on them in the Ruby statements that make up the block body.

DRYing up our code with blocks

Now that we’ve discovered Ruby blocks, we can actually implement that mythical do_something_with_every_item method we were wishing for, and take the repetition out of our code.

That’s it! The method will loop through each item in the array. Each time it encounters the yield keyword, the method will pass the current item to the block as a parameter.

Now, we can re-define our other methods to utilize do_something_with_every_item:

Output:

But here’s the neat part: your new method doesn’t limit you to working with numbers! You can process an array full of strings:

Output:

…Or an array full of Time instances:

Output:

…Or anything else you can dream up!

The “each” method

And now, of course, it’s time for the punchline. What we’ve implemented here is already available as a method on Ruby’s Array class: the each method.

Here’s what the totalfix_names, and get_years methods above would look like if they were re-written using each:

You can call them the same way as before:

The each method lets us remove a lot of duplicate code! And each is just one of many methods that use Ruby blocks in powerful ways. We’ll look at more of the exciting possibilities in a later post.

If you want to play around with blocks yourself, check out this screencast. It will help you get set up with a Ruby environment, and walk you through some experiments with each as well as other methods.

Editor’s note: This post is adapted from Jay’s upcoming book, Head First Ruby.

Related

Sign up for the O'Reilly Programming Newsletter to get weekly insight from industry insiders.
topic: Programming
tags: , ,
  • cpolis

    Great article! It’s worth noting that Ruby enumerables have some other really useful ‘block methods’ in addition to #each.

    For example, the total function in this post can be a one liner with #inject:
    prices.inject { |amount, price| amount + prices }

    or more concisely:
    prices.inject(&:+)

    http://ruby-doc.org/core-2.1.0/Enumerable.html#method-i-inject

    • Vincent Franco

      Note if there is no prices you terse usage of inject throws an error. Also kind of neat is the fact that you can omit the proc to “&”. So less error prone and without proc to: prices.inject(0, :+)

  • mikewolfson

    Cool article. Some good concepts that are language agnostic.