Mastering Ruby Blocks in Less Than 5 Minutes

Mastering 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.

What are ruby blocks?

A block is basically just code that you put inside do and end (or { and } for one-liners). That’s it. Done, let’s go home :)

Oh well, I guess there are a few more things to say about blocks.

Both the multi-line version and the inline will do the exact same thing so it’s up to you which one you choose. As a general rule of thumb, it’s better to use the do/end 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].each do |n|
  # Prints out a number
  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. The same exact thing can be written inline.

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

Both versions will print numbers 1, 2 and 3 in that order. The little n 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 (you can learn more about the each method here).

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.

Number 1
Number 2
Number 3

One thing to note is that any method can receive a block. Doesn’t matter if it uses it or not. Here’s an example.

def my_method
  puts "something"
end

my_method { "hello" } # => "something"

In the example above, the block is ignored. But the thing to note is that no one stops you from passing the block to the method. More about this in a second.

How yield works

This thing is responsible for most of my confusion around ruby blocks. It took me forever to get it.

def my_method
  puts "reached the top"
  yield
  puts "reached the bottom"
end

my_method do
  puts "reached yield"
end

# output
reached the top
reached yield
reached the bottom

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 execution and yield

Passing blocks to methods

Remember how you can pass a block to a method whether it wants it or not?

Well, if you call yield inside the 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, 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 (i.e. name and age) are local to the block. That means you can’t use them outside of the block. Let’s test this.

def my_method
  yield("John", 2)
  puts "Hi #{name}"
end

my_method { |name, age| puts "#{name} is #{age} years old" }

# output
John is 2 years old
NameError: undefined local variable or method `name' for #<IRB::...>

As you can see, name is not available to my_method because it’s local to the block.

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.

def my_method
  value = yield
  puts "value is: #{value}"
end

my_method do
  2
end

value is 2

What does &block (ampersand parameter) mean?

Here’s what &object does:

  • if the object is a block, it converts it to a Proc.
  • if the object is a Proc, it converts it to a block.
  • if the object is something else, it calls to_proc on it, and then converts it to a block.

So let’s test this. First by examining the object as a block.

def a_method(&block)
  block
end

a_method { "x" } # => #<Proc:...>

Now let’s see what happens when the object is a Proc.

a_proc = Proc.new { "x" }
a_method(&a_proc) # => #<Proc:...>

Since the argument is already a Proc, it’s converted to a block.

As a sidenote here, if the object is a Proc, it’s lambda? status is perserved. Meaning, you get the benefits of a lambda. Namely argument checking, and having them return values.

a_lambda = -> () { "x" } => #<Proc:... (lambda)>
a_method(&a_lambda) # => #<Proc:... (lambda)>

And finnaly, when you pass it a something that’s not a block or a Proc.

a_method(&:even?) # => #<Proc:...>

This is because calling Symbol#to_proc returns a Proc that can take an object and calls the method you specified on it. That’s confusing, I know. So let’s see an example.

a_proc = :foobar.to_proc
a_proc.call("some string")
# => NoMethodError: undefined method `foobar' for "some string":String

So let’s look again at what that does.

  1. Calling to_proc on the symbol :foobar returns a new Proc (i.e. a_proc).
  2. a_proc will call the foobar method on any object you send it.

Here’s what to_proc would look like if you were to redefine it in Ruby.

class Symbol
  def to_proc
    Proc.new { |obj, *args| obj.send(self, *args) }
  end
end

How does .map(&:something) work?

Map makes for a good example of using the ampersand notation, so head over to How to Use the Ruby Map Method to read more.

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 you can write a method similar to the map method in ruby.

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

# output
2
4
6

Initialize objects with default values

A cool pattern you 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.

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."

# output

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.

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 you have a string you want to wrap inside html tags and then do something different with it.

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 <title>Hello</title> 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 you want to build a way to quickly store ideas into a database table. For that to work you 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.

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" }

# output

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?

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

# output

3
6
9

Conclusion

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).

You’ve made it! Having read the whole post means you’re on your way to find more creative uses of ruby blocks. If for some reason you’re still confused or feel like there’s something missing from the text please let me know in the comments.

Share this article if you’ve learned something new about ruby blocks.

If you liked this article, please take a moment and say thanks by sharing it on your favorite social media channel.