Haroon Ahmed

Handling and raising errors in Rails

15 May 2017

I wanted to find the best way to handle errors in rails, so my code is easy to read and my tests too.

Firstly, I created an errors.rb file in the /lib folder.

class Errors
  # When they don't have permission to do something
  class Forbidden < StandardError; end
# When something they want is not found
  class NotFound < StandardError; end
# When the input is somehow bad
  class InvalidParameters < StandardError; end
end

At the top of my application_controller.rb I loaded that file (require ‘errors’). Then in my application controller I added the code below to rescue from my exceptions.

rescue_from Errors::Forbidden do
  not_found
end
rescue_from Errors::NotFound do
  bad_request
end
rescue_from Errors::InvalidParameters do
  bad_request
end
def not_found
  respond_to do |format|
    format.html do
      render file: "#{Rails.root}/public/404.html",
      layout: false, status: :not_found
    end
    format.xml do
      head :not_found
    end
    format.any do
      head :not_found
    end
  end
end
def bad_request
  respond_to do |format|
    format.html do
      render file: "#{Rails.root}/public/422.html",
      layout: false, status: :unprocessable_entity
    end
    format.xml do
      head :unprocessable_entity
    end
    format.any do
      head :unprocessable_entity
    end
  end
end

Now I can raise an exception in my controllers like so:

def update
  load_application
  raise Errors::NotFound unless @application.valid?(:applicant_check)
  @application.date_terms_sent = Time.now
  if @application.save!
    flash.notice = 'Terms emailed to applicant'
    ApplicantTermsMailer.terms_email(@application).deliver_later
    redirect_to action: 'show' and return
  end
  render 'show'
end

This is really easy to test, using mini test.

test 'terms fail to send out as applicant name and/or email not found' do
  sign_in users(:batman)
  patch application_terms_url(application_id: @application.id)
  assert_response :unprocessable_entity
end

Finally I just need to make my error pages look good.