Ruby 2.5: Not Blocking My Rescue

Rescuing specific exceptions excessively can cause problems, but if you’ve ever had need to rescue within a do/end block, you might have found yourself using wordy syntax. Ruby 2.5 has a solution for you.

In Ruby 2.5, we’ll get a little syntactic sugar for handling exceptions inside do/end blocks. You can see the feature discussion on Ruby’s Redmine instance. If you’ve ever used the shorthand for rescuing inside a method without using begin/end keywords, this is basically that but inside blocks.

Below we’re going to work through a bit of code, but we’re not going to define all the methods.

Imagine a Santa class, and an algorithm for Santa Claus arriving in town. Santa needs some paper to jot down delivery details. If there is no paper, we raise an error.

class Santa
  def initialize(good, bad)
    @nice_children = good
    @naughty_children = bad
  end

  def make_list
    raise NoPaperError if @paper.nil?
    @list = nice_children.gifts.map(&:details)
  end

  def check_list
    @list.verify_children(nice_children, naughty_children)
  end

   # other Santastic behaviour below
end

We’ll define a global prepare method that takes a block. In Ruby 2.4 when we send the make_list message to Santa within the block, we need to use a full begin/rescue/end clause to describe the behaviour:

def song(children)
  santa = Santa.new(children.good, children.judged_capriciously_by_society)
  prepare do
    # must use `begin` here in Ruby 2.4 and earlier
    begin
      santa.make_list
      2.times { santa.check_list }
    rescue NoPaperError
       santa.request_paper
    ensure
       music_stops
    end
  end
end

def prepare(&block)
  yield
end

If we try to use the shorthand common in Classes, we hit an exception:

def song(children)
  santa = Santa.new(children.good, children.judged_capriciously_by_society)
  prepare do
    santa.make_list
    2.times { santa.check_list }

    rescue NoPaperError
      santa.request_paper
    ensure
      music_stops
    end
  end
end

song(children) #=> SyntaxError:  syntax error, unexpected keyword_rescue, expecting keyword_end

In Ruby 2.5 though, we can do this handily:

def song(children)
  santa = Santa.new(children.good, children.judged_capriciously_by_society)
  prepare do
    # no `begin` keyword!
    santa.make_list
    2.times { santa.check_list }

    rescue NoPaperError
      santa.request_paper
    ensure
      music_stops
    end
  end
end

song(children) # => “music stops”

So that’s nice. See you tomorrow for more Ruby 2.5 news!

This entry was filed under Ruby by James. Bookmark the permalink.

About James

James spends his time at FreeAgent reading, writing and occasionally deleting code. He finds joy in helping people, so making the lives of small businesses easier is right up his alley. You can find him frantically amplifying other voices over at @sarcainian.

comments powered by Disqus