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 |
|
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
|
|
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 |
|
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 |
|
1 2 3 4 |
|
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.
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.
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 |
|
1 2 |
|
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 |
|
1 2 |
|
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 |
|
1 2 3 |
|
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 |
|
1
|
|
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 |
|
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 |
|
In the first case we’re sending the
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 |
|
1 2 3 4 |
|
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 |
|
1 2 3 |
|
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).