Master Ruby Blocks

Master Ruby Blocks

Blocks are one of the most powerful and often overlooked feature of ruby. I must confess that it took me a while to figure out how ruby blocks work and how they can be useful in practice.

There is something about yield that makes blocks very hard to understand at first. I’m going to talk about some of the concepts and provide a few examples so by the end of this post you’ll have a solid understanding of ruby blocks.

The basics: What are ruby blocks?
1. What are ruby blocks?
2. How yield works?
3. What does &block mean?
4. Return value
5. How does .map(&:something) work?
6. Iterators and how to build one yourself
7. Initialize objects with default values using blocks
8. Ruby block examples

What are ruby blocks?
A block is basically just code that you put inside do and end. That’s it. “But where’s the magic?” you might ask. We’ll get there in just a minute but first things first.

You can write the block in two ways:
1. Multi-line, between do and end
2. Inline, between { and }

Both versions will do the exact same thing so it’s up to you which one you choose. As a general style-guide, it’s better to use the multi-line version if your code has more than one line, just to make it easier to read.

Here’s a basic example of a multi-line block:

1
2
3
[1, 2, 3].each do |n|
  puts "Number #{n}"
end

It’s called a multi-line block because it’s not inline, not because it’s got more than one line of code (which is not the case here). The same example can be written with an inline block:

1
[1, 2, 3].each {|n| puts "Number #{n}"}

Both versions will print numbers 1, 2 and 3 in order. The little n letter you see between the pipes (|n|) is called a block parameter and it’s value in this case is going to be each of the numbers in turn, in the order they are listed inside the array. So for the first iteration, the value of n will be 1, then for the second iteration, the value will be 2, and then 3.

1
2
3
4
Number 1
Number 2
Number 3
 => nil

How yield works?
Here’s the bad wolf. This guy is responsible for all the confusion and magic around ruby blocks. I think most of the confusion comes from the way it calls the block and how it’s passing parameters to it. We’ll be looking at both scenarios in this section.

1
2
3
4
5
6
7
8
9
def my_method
  puts "reached the top"
  yield
  puts "reached the bottom"
end

my_method do
  puts "reached yield"
end
1
2
3
4
reached the top
reached yield
reached the bottom
 => nil

So basically when the execution of my_method reaches the line with the call to yield, the code inside the block gets executed. Then, when the code inside the block finishes, the execution of my_method continues.

Ruby Block Flow

Passing blocks to methods
A method doesn’t need to specify the block in it’s signature in order to receive a block parameter. You can just pass a block to any function but unless that function calls yield, the block won’t get executed.

On the other hand, if you do call yield in your method, then the block parameter becomes mandatory and the method will raise an exception if it doesn’t receive a block.

If you want to make the block an optional parameter, you can use the block_given? method which will return either true or false depending on if a block was passed in to the method or not.

Yield takes parameters too
Any parameter passed to yield will serve as a parameter to the block. So when the block runs, it can use the parameters passed in from the original method. Those parameters can be variables local to the method in which yield lives in.

The order of the arguments is important because the order you use to pass in the parameters is the order in which the block receives them.

Ruby Block Arguments

One thing to note here is that the parameters inside the block are local to the block (unlike those passed in from the method to the block).

What does &block (ampersand parameter) mean?
You’ve probably seen this &block all over the place in ruby code. It’s how you can pass a reference to the block (instead of a local variable) to a method. In fact, ruby allows you to pass any object to a method as if it were a block. The method will try to use the passed in object if it’s already a block but if it’s not a block it will call to_proc on it in an attempt to convert it to a block.

Also note that the block part (without the ampersand) is just a name for the reference, you can use whatever name you like if it makes more sense to you.

1
2
3
4
5
6
def my_method(&block)
  puts block
  block.call
end

my_method { puts "Hello!" }
1
2
#<Proc:0x0000010124e5a8@tmp/example.rb:6>
Hello!

Return value
yield returns the last evaluated expression (from inside the block). So in other words, the value that yield returns is the value the block returns.

1
2
3
4
5
6
7
8
def my_method
  value = yield
  puts "value is: #{value}"
end

my_method do
  2
end
1
2
value is 2
  => nil

How does .map(&:something) work?
You’ve probably used shortcuts like .map(&:capitalize) a lot, especially if you’ve done any Rails coding. It’s a very clean shortcut to .map { |title| title.capitalize }.

But how does it really work?

It turns out that the Symbol class implements the to_proc method which will unwrap the short version into it’s longer variant. Nice right?

Iterators and how to build one yourself
You can call yield as many times as you want inside a method. That’s basically how iterators work. Calling yield for each of the elements in the array mimics the behavior of the built in ruby iterators.

Let’s see how we can write a method similar to the map method in ruby.

1
2
3
4
5
6
7
8
9
10
11
12
13
def my_map(array)
  new_array = []

  for element in array
    new_array.push yield element
  end

  new_array
end

my_map([1, 2, 3]) do |number|
  number * 2
end
1
2
3
2
4
6

Initialize objects with default values
A cool pattern we can use with ruby blocks is to initialize an object with default values. You’ve probably seen this pattern if you’ve ever ventured into a .gemspec file from any ruby gem.

The way it works is, you have an initializer that calls yield(self). In the context of the initialize method, self is the object being initialized.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Car
  attr_accessor :color, :doors

  def initialize
    yield(self)
  end
end

car = Car.new do |c|
  c.color = "Red"
  c.doors = 4
end

puts "My car's color is #{car.color} and it's got #{car.doors} doors."
1
My car's color is Red and it's got 4 doors.

Ruby blocks examples
Examples are all the rage these days so let’s try to find a few interesting ways of using blocks in real world (or as close to real world as possible) scenarios.

Wrap text in html tags
Blocks are the perfect candidate whenever you need to wrap a chunk of dynamic code within some static code. So for example if you want to generate an html tag for some text. The text is the dynamic part (cause you never know what you’ll want to wrap) and the tags are the static part, they never change.

1
2
3
4
5
6
7
8
9
10
11
def wrap_in_h1
  "<h1>#{yield}</h1>"
end

wrap_in_h1 { "Here's my heading" }

# => "<h1>Here's my heading</h1>"

wrap_in_h1 { "Ha" * 3 }

# => "<h1>HaHaHa</h1>"

Note that the power of using blocks over methods is when you need to reuse some of the behavior but do something slightly different with it. So let’s say we have a string we want to wrap inside html tags and then do something different with it.

1
2
3
4
5
6
7
def wrap_in_tags(tag, text)
  html = "<#{tag}>#{text}</#{tag}>"
  yield html
end

wrap_in_tags("title", "Hello") { |html| Mailer.send(html) }
wrap_in_tags("title", "Hello") { |html| Page.create(:body => html) }

In the first case we’re sending the Hello string via email and in the second case we’re creating a Page record. Both cases use the same method but they do different things.

Take a note
Let’s say we want to build a way to quickly store ideas into a database table. For that to work we want to pass in the note and have the method deal with the database connections. Ideally we’d like to call Note.create { "Nice day today" } and not worry about opening and closing database connections. So let’s do this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Note
  attr_accessor :note

  def initialize(note=nil)
    @note = note
    puts "@note is #{@note}"
  end

  def self.create
    self.connect
    note = new(yield)
    note.write
    self.disconnect
  end

  def write
    puts "Writing \"#{@note}\" to the database."
  end

  private
  def self.connect
    puts "Connecting to the database..."
  end

  def self.disconnect
    puts "Disconnecting from the database..."
  end
end

Note.create { "Foo" }
1
2
3
4
Connecting to the database...
@note is Foo
Writing "Foo" to the database.
Disconnecting from the database...

The implementation details of connecting, writing and disconnecting to and from the database were left out since they’re out of the scope of this article.

Find divisible elements of an array
It seems like I’m getting further and further away from “the real world scenario” but anyways, I’m gonna shoot one last example. So let’s say you want to get every element of an array that is divisible by 3 (or any number you choose), how would you do that with ruby blocks?

1
2
3
4
5
6
7
8
9
10
class Fixnum
  def to_proc
    Proc.new do |obj, *args|
      obj % self == 0
    end
  end
end

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].select(&3)
puts numbers
1
2
3
3
6
9

You can think of blocks as simply a chunk of code, and yield allows you to inject that code at some place into a method. That means you can have one method work in different ways, you don’t have to write multiple methods (you can reuse one method to do different things).