Ruby

Rescue from errors with a grace

Rescue from errors with a grace February 11, 2019

How many times you had to deal with the external API’s errors in order to make your application behaves properly? You may end up then with the following code, especially when errors classes are generic:

class SomeAPIService
  def self.call
    perform_action
  rescue SomeAPI::Error => e
    if e.message.match(/account expired/)
      send_email_with_notification_about_expired_account
    elsif e.message.match(/api key invalid/)
      send_email_with_notification_about_invalid_api_key
    elsif e.message.match(/unauthorized action performed/)
      Rollbar.error('log some useful info')
    else
      raise(e)
    end
  end
end

This code is far from being readable, easy-testable and perfect, isn’t it? If the external API does not provide custom error messages, we can create them on our side:

class SomeAPIAccountExpired < StandardError
  def self.===(exception)
    exception.class == SomeAPI::Error && exception.message.match(/account expired/)
  end
end
 
class SomeAPIInvalidAPIKey < StandardError
  def self.===(exception)
    exception.class == SomeAPI::Error && exception.message.match(/api key invalid/)
  end
end
 
class SomeAPIUnauthorizedAction < StandardError
  def self.===(exception)
    exception.class == SomeAPI::Error && exception.message.match(/unauthorized action performed/)
  end
end

How does it work? When an error is raised, Ruby is checking the error class against the classes you have defined in the rescue. If there is a match then the exception gets rescued. Every operator in Ruby is a method so we can define our version of === as well.

Having the above error classes defined, allows us to refactor our code in the following way:

class SomeAPIService
  def self.call
    perform_action
  rescue SomeAPIAccountExpired
    send_email_with_notification_about_expired_account
  rescue SomeAPIInvalidAPIKey
    send_email_with_notification_about_invalid_api_key
  rescue SomeAPIUnauthorizedAction
    Rollbar.error('log some useful info')
  end
end

Now, we can test each error class separately and easily test the SomeAPIService. The whole service’s code is also readable. We also don’t have to re-raise the error when we don’t support error message because it happens automatically as such error is not matched. That’s it!

Photo by Joe Calomeni from Pexels

Download free RSpec & TDD ebook

Do you want to earn more or jump to the next level in your company? Do you know that testing skills are one of the most desired skills? There is only first step: start testing and do it right. My ebook can help you. Subscribe to the newsletter to get a free copy of the book.