How to take advantage of Ruby blocks
Tags: ruby, rails
Published: 2019-05-29 23:46:19

---
title: How to take advantage of Ruby blocks
published: true
description: An overview of blocks in ruby and some examples
tags: ruby, rails
---


Blocks in ruby are a very powerful aspect of the language. They are often less used in when compared to other features of the language and it is something not everyone is comfortable using them, and much less writing code that consumes blocks. So let's start with a small introduction:

**What are blocks?**
Essentially, a method with no name. It is a piece of code, delimited either by `{ }` or by `do... end`. That we are able to call in a certain context For example:

```ruby
# The following syntaxes are equivalent. They will both execute the block twice.

2.times do
  puts "foo"
end

2.times { puts "foo" }
```

A more complicated example: Let's assume that for a user masquerading gem, you want a method in which you can pass in a user, grant them all permissions, run some code with those permissions in place, and then reset the old permissions once the code has finished running. This is where blocks shine. A very simple test suite for this would be something like:

```ruby
RSpec.describe UserImpersonator do
  it "grants permissions while inside the block" do

    UserImpersonator.grant_all_permissions valid_user do
      expect(valid_user.roles[:admin]).to be_true
    end

    expect(valid_user.roles[:admin]).to be_false
  end
end
```

We first need a `UserImpersonator` class and a `grant_all_permissions` method that accepts the user, and the **block** of code you want to run while you have all permissions in place.

```ruby
class UserImpersonator
  def self.grant_all_permissions(user, &block)
    new(user).run(&block)
  end

  def initialize(user)
    @user = user
  end

  def run(&block)
    begin
      cache_old_permissions
      assign_all_permissions
      block.call
    ensure
      reset_permissions
    end
  end
end
```

Let's break up the code above: 

The `&block` on the `self.grant_all_permissions` method definition lets the method know that it can expect to be passed in a block. This will instantiate a new `UserImpersonator` object and call the run method on it, forwarding the `&block` argument.

The `run` method's first two lines after the `begin` keyword are the set up, we want to cache the old permissions so that we can reset them later and assign the new ones. After this, we want to actually run the block that was passed in, and after that, we want to reset the permissions back, regardless of whether or not an error was raised somewhere in between, hence the `ensure` keyword. 

There you have it, we've just created a method that accepts a block and runs it wherever we want, this is certainly useful, but we can go deeper


Let's try now passing a specific context into our block, or block variables, if you have used rails, even for a bit, then you have probably seen this when you iterate through your database records, for example:

```ruby
User.all.each do |user|
  user.update admin: false
end
```

The block variable is what is between the pipes, `user` in this case.


**New example:** Let's imagine we're running a restaurant, and we would like to calculate the cost of an order. Since an order can be made out of different items, we want to be able to add as much or as little items within the context of that order. Again, starting with the tests, we could have an expectation like:

```ruby
RSpec.describe Order do
  it "allows to make operations" do
    result = Order.cost do |c|
      c.add_taco
      c.add_taco
      c.add_guacamole
      c.add_beer
    end

    expect(result).to eq 44
  end
end

```

To make the test above pass, one possible implementation that would make the test above to pass would be:

```ruby
class Order
  def self.cost
    yield Actions.new
  end

  class Actions
    def initialize
      @cost = 0
    end

    def add_taco
      @cost += 10
    end

    def add_guacamole
      @cost += 6
    end

    def add_beer
      @cost += 18
    end
  end
end
```

Things to note:


1) The block variable, or `c` in the case of the test is an instance of the `Actions` class, which means it has access to the `add_taco`, `add_guacamole` and `add_beer` methods

2) You don't need to specify `&block` as an parameter in the `cost` method since you are using `yield` inside of the method

3) `yield` makes a block an **OPTIONAL** parameter, which means that you COULD call cost and pass no block at all. This however, will fail and complain with a

```bash
LocalJumpError (no block given (yield))
```

To solve this, a handy little method called `block_given?` allows you to verify if a block was passed in. So you could refactor the `cost` method to handle that:

```ruby
def self.cost
  yield Actions.new if block_given?
end
```

I really hope that this deep dive into the more fun and out there features of ruby was useful. Personally, I find it enlightening to find out how the inner workings of the tools I use very often within **RSpec** or **factory_bot** fit together step by step