simonewebdesign

How to set a default message in your exception

Today I was thinking about a way to define custom exceptions with a predefined error message. For example, instead of doing this:

raise MyError, "Something went wrong."

We want to simply do:

raise MyError

This could be useful because if we need to raise that same exception again and again, we don’t have to specify the error message every time.

Well, how can we do that?

I spent all day figuring out the best way, actually doing very bad things – I’ve even attempted to monkey-patch the Kernel module!
So – believe me – it’s not as simple as it appears to be. Or, at least, I thought this until I stumbled across this article (dead link).

In short, you just need to override Exception#message.

For example:

class MyError < Exception
  def message
    "a predefined message"
  end
end

raise MyError
# => MyError: a predefined message

Quick note: I’m inheriting from StandardError, not Exception, because extending the Exception class in Ruby is considered really bad. Please don’t inherit from it: see here and here for the reason (in few words it’s because you may catch errors that are not meant to be catched, such as SyntaxError).

Of course you could also create a module with your own exceptions in it:

module CustomError
  class AnError < StandardError
    def message
      "A more specific error"
    end
  end

  class AnotherError < StandardError
    def message
      "just another error"
    end
  end
end

Or even a subclass of your custom error class:

module CustomError
  class Error < StandardError
    def message
      "default error"
    end
  end

  class SpecificError < Error
    def message
      "a more specific error"
    end
  end
end

However, this is not very useful. What I find useful, though, is that you can bring shared pieces of information from the base class to the subclasses, which is IMO very desirable in error handling.

Since Exception#message is nothing but an alias of exception.to_s, we can call super to get the superclass' message. For example, this is what I ended up doing:

module CustomError

  class Error < StandardError
    def initialize(msg=nil)
      @message = msg
    end

    def message
      "Message from main class: #{@message}."
    end
  end

  class SpecificError < Error
    def message
      super + " We also got a specific error."
    end
  end
end

And here's the result:

raise CustomError::SpecificError, "fubar"
# => CustomError::SpecificError: Message from main class: fubar. We also got a specific error.

This demonstrates that we can potentially carry whatever information (i.e. instances of objects involved in the error) in order to better handle errors in our applications.

That's it.
As always, please feel free to share your thoughts by commenting below.

Last update:

View this page on GitHub