179

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 

5 Answers 5

239
+50

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 
Sign up to request clarification or add additional context in comments.

7 Comments

Why namespace them into the Exceptions module?
I would think that /lib might not be the place for errors. They are very application specific and I am under the impression that code that I am putting in /lib is meant to be code that could be reused in other applications.
Ruby on Rails instructions did not work for me -- is some additional step needed to actually load this new file in the typical case?
@oaamados it's not that is not recommended. Rails 5 just does not autoload it anymore.
@ABMagil seems I have to otherwise Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define it the other option would be one class per exception I think
|
35

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

3 Comments

How should I raise these exceptions?
@NikhilWagh either raise FooError, "Example message..." or raise FooError.new("Example message...")
You may need config.autoload_paths += %W[#{Rails.root}/app/errors] in your config/application.rb for the app to load that folder.
28

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.

3 Comments

While in theory you are right, what happen when the same error can be raised by various classes in totally different situations?
@Alain Why not define those errors used by more than one class in an Exceptions/Errors module, but leave all others defined in the single class which uses them?
@ScottW, In that case, we're relying on the developer to remember to check.
22

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!

Comments

12

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") 

3 Comments

And if you don't want a separate file for each new error, just put them all in lib/app_name/error.rb
Getting an uninitialized constant MyController::AppName. I'm calling raise in my controller
@NikhilWagh You'll want to prepend the class name with a double colon (::) to ensure it looks in the root namespace for your class.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.