Understand Ruby Blocks by looking at examples from popular gems
Tags: rails, ruby
Published: 2022-10-28 09:43:35

Ruby Blocks is something all Ruby developers use everyday. For example, while using the `each, times, or  upto` methods or the countless other Enumerable module methods, we normally pass a block along with the method. For example:

```ruby
1.upto(5) {|i| puts(i)}
```

The code included in the brackets {} is passed to the `upto` method. This isn't passed as an argument, but as a block, and it is yielded by the `upto` method.

There are many different ways to use blocks, and here are some common patterns for using blocks:

## 1. Execute around
This pattern is mainly used when you have some common code you need to run before or after something else. For example, suppose you have a `benchmark` method like below:

```ruby
def with_benchmark
  start_time = Time.now
  puts "Starting benchmark at #{start_time}"
  
  yeild
  
  stop_time = Time.now
  puts "The method took #{stop_time - start_time} to complete"
end
```

Now you can find out how long any method takes to complete by doing something like this:

```ruby
with_benchmark do
  # Call the signup method  
end

with_benchmark do
  # Call any other method to find out how long it takes to run 
end
```

You could even improve the benchmark method to take an argument, and compare that with the result of the yield. For example:

```ruby
def with_benchmark(expected_time)
  start_time = Time.now
  puts "Starting benchmark at #{start_time}"
  
  result = yeild
  
  stop_time = Time.now
  puts "The method took #{stop_time - start_time} to complete"
  
  if result > expected_time
    puts "Failed! Took too long to complete"
  else
    puts "Passed! That was super fast!"
  end
end

with_benchmark(5) do
  # Do anything you want
  sleep 10
end

with_benchmark(5) do
  # Call some method
  sleep 2
end
```

### Examples of 'Execute around' from gems:
The Ruby standard library has a benchmark module that includes methods similar to what we have written above:

```ruby
def realtime
  r0 = Time.now
  yield
  Time.now - r0
end
```

2. It is not necessary that we have to run something before and after the yield. Sometimes, we can run only the before part, or only the after part. For example, you may need to change the state of something before yielding. The Shopify App gem maintained by Shopify, in the `activate_shopify_session` method, activates a Shopify session before yielding the block.

```ruby
module ShopifyApp
  module LoginProtection
    ...
    def activate_shopify_session
      ...
      begin
        ShopifyAPI::Context.activate_session(current_shopify_session)
        yield
      ensure
        ShopifyAPI::Context.deactivate_session
      end
    end
```

Like in the example above, it is also always recommended to use an `ensure` clause to change the state back incase the yielded block raises an error. As the code in the `ensure` clause is always executed, whether or not an exception is raised,  it's guaranteed to return the state to how things were before you called yield.

### 2. Initializing an object with default values
Another common pattern of using blocks is to initialize an object with default values.

1. In Rails, you can create a new object in many different ways:

```ruby
# By passing the attribues as a hash
Shop.new(name: "Starbucks")

# By setting the attributes after initializing or creating the object
shop = Shop.new
shop.name = "Starbucks"

# Or by using blocks!
shop = Shop.new do |s|
  s.name = "Starbucks"
end
```

2. While creating a new Ruby gem

```ruby
Gem::Specification.new do |s|
  s.name        = 'example'
  s.version     = '0.1.0'
  s.licenses    = ['MIT']
  s.summary     = "This is an example!"
  s.description = "Much longer explanation of the example!"
  s.authors     = ["Ruby Coder"]
  s.email       = 'rubycoder@example.com'
  s.files       = ["lib/example.rb"]
  s.homepage    = 'https://rubygems.org/gems/example'
  s.metadata    = { "source_code_uri" => "https://github.com/example/example" }
end
```

This pattern is useful if you want to make the code more readable, and explicitly state what are the attributes required while setting up a new object.

To initialize an object with a block, you can do it by yielding the object on initialization. For example, if we had a simple Shop class, we could do it this way:

```ruby
class Shop
  attr_accessor :name

  def initialize
    @name = "Starbucks"
    yield(self) if block_given?
  end

  def to_s
    "Name: #{@name}"
  end
end

shop = Shop.new
puts shop
# (would output "Name: Starbucks")

shop = Shop.new do |s|
 s.name = "Coffee Day"
end
puts shop
# (would output "Name: Coffee Day")
```

## 3. Saving blocks to execute later
This is something I came across while reading Eloquent Ruby. This pattern is useful when you want to use blocks to be called only when required - i.e. you can hang on to a block until you need to use it.

Until now we have called blocks implicitly using the yield to fire off the block. However, blocks can also be captured as a parameter, and can then be run by calling the call method on it. Calling blocks explicitly has it's own advantages and is clearer to someone reading the code.

This is an example from the Pay gem.

```ruby
module Pay
  module Webhooks
    class Delegator
      ...
      
      # Configure DSL
      def configure(&block)
        raise ArgumentError, "must provide a block" unless block
        block.arity.zero? ? instance_eval(&block) : yield(self)
      end
      ...
    end
  end
 end
```

Passing blocks explicitly can also be really useful if you have an expensive method, and you only want to run it when really needed. Eloquent Ruby, shows a useful application for this. Suppose you have a Document class, and most times you only need to fetch the title and the author. Occasionally, you may need to read the actual content of the document from different sources that are chosen by the user.

```ruby
class Document
  attr_reader :title, :author
  
  def initialize(title, author, &block)
    @title = title
    @author - author
    @initializer_block = block
  end
  
  def content
    if @initializer_block
      @content = @initializer_block.call
      @initializer_block = nil
    end
    @content
  end
end
```

Implementing the document class this way means we can can get the contents from a file, or via HTTP, or via FTP.

```ruby
file_doc = Document.new('file', 'Eloquent Ruby') do
  File.read('some_text.txt')
end

google_doc = Document.new('http', 'Eloquent Ruby') do
  Net::HTTP.get_response('www.google.com', '/index.html').body
end
```

The above examples does look similar to the initializing examples below - but the main difference is that the Document class waits until the content method to fire off the block. If the content method is never called, the block will also never get called.

We can also see more examples of this pattern where the block is saved for later in before_action in Rails.

```ruby
class ApplicationController < ActionController::Base
  before_action do |controller|
    # do something before each action
  end
end
```

And in Active Record's life cycle hooks.
```ruby
class User < ActiveRecord::Base
  before_create do |user|
    puts "about to create #{user.name}"
  end
end
```

Conclusion
Using blocks with yield may take some time to get used to, but it gives us a different style of programming, that can improve the design of our code.

If you are looking for a senior Rails developer to join your team, do send me an email to arjun.rajkumar@hey.com. I took 6 months off as I became a new dad, but am currently open to new Ruby/Rails related work.

This post originally appeared on [Weightless](https://weightlessapps.io/keeping-my-ruby-class-methods-small-and-focussed/).