Where to define custom error types in Ruby and/or Rails?

Ruby on-RailsRubyConventions

Ruby on-Rails Problem Overview


Is there a best practice for defining custom error types in a Ruby library (gem) or Ruby on Rails application? Specifically:

  1. Where do they belong structurally in the project? A separate file, inlined with the relevant module/class definition, somewhere else?
  2. Are there any conventions that establish when to and when not to create a new error type?

Different libraries have different ways of doing things, and I haven't noticed any real patterns. Some libraries always use custom error types while others don't use them at all; some have all errors extending StandardError while others have nested hierarchies; some are just empty class definitions, others have all sorts of clever tricks.

Oh, and just because I feel like calling these "error types" is sort of ambiguous, what I mean is this:

class AuthenticationError < StandardError; end
class InvalidUsername < AuthenticationError; end

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

For Gems

I have seen many times that you define exceptions in this way:

> gem_dir/lib/gem_name/exceptions.rb

and defined as:

module GemName

  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end

end

an example of this would be something like this in httparty

For Ruby on Rails

Put them in your lib/ folder under a file called exceptions.rb, which would look something like this:

module Exceptions
  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end
end

and you would use it like this:

raise Exceptions::InvalidUsername

Solution 2 - Ruby on-Rails

in rails you can make app/errors directory

# app/errors/foo_error.rb
class FooError < StandardError; end

restart spring/server and it should pick it up

Solution 3 - Ruby on-Rails

I think in order to have cohesive source files in your project, you should define errors in the class in which may throw them and nowhere else.

Some heirarchy can be helpful - namespaces are good at keeping redundant strings out of type names - but that's more a matter of taste - there's no need to go overboard provided you have at least one custom exception type in your app which you use throughout to differentiate between 'intentional' and 'accidental' exception cases.

Solution 4 - Ruby on-Rails

This is an old question, but I wanted to share how I'm handling custom errors in Rails, including attaching error messages, testing, and how to handle this with ActiveRecord models.

Creating Custom Error
class MyClass
  # create a custome error
  class MissingRequirement < StandardError; end

  def my_instance_method
    raise MyClass::MissingRequirement, "My error msg" unless true   
  end
end
Testing (minitest)
test "should raise MissingRequirement if ____ is missing"
  # should raise an error
  error = assert_raises(MyClass::MissingRequirement) {
    MyClass.new.my_instance_method
  }
  
  assert error.message = "My error msg"
end
With ActiveRecord

I think it's worth noting that if working with an ActiveRecord model, a popular pattern is to add an error to the model as described below, so that your validations will fail:

def MyModel < ActiveRecord::Base
  validate :code_does_not_contain_hyphens

  def code_does_not_contain_hyphens
    errors.add(:code, "cannot contain hyphens") if code.include?("-")
  end
end

When validations are run, this method will piggy-back onto ActiveRecord's ActiveRecord::RecordInvalid error class and will cause validations to fail.

Hope this helps!

Solution 5 - Ruby on-Rails

To ensure that autoloading works as expected in Rails 4.1.10 for multiple custom error classes, you'll want to specify separate files for each. This should work in development with its dynamically reloading.

This is how I setup errors in a recent project:

In lib/app_name/error/base.rb

module AppName
	module Error
		class Base < StandardError; end
	end
end

and in subsequent custom errors, like in lib/app_name/error/bad_stuff.rb

module AppName
	module Error
		class BadStuff < ::AppName::Error::Base; end
	end
end

You should then be able to call your errors via:

 raise AppName::Error::BadStuff.new("Bad stuff just happened")

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncoreywardView Question on Stackoverflow
Solution 1 - Ruby on-RailsMike LewisView Answer on Stackoverflow
Solution 2 - Ruby on-RailsschpetView Answer on Stackoverflow
Solution 3 - Ruby on-RailsDean RadcliffeView Answer on Stackoverflow
Solution 4 - Ruby on-RailsMattView Answer on Stackoverflow
Solution 5 - Ruby on-RailsspyleView Answer on Stackoverflow