Summoning Error Classes As Needed
In the past, I've written a lot of code like this:
class SomeThing class SomeError < RuntimeError; end class AnotherError < RuntimeError; end class YetAnotherError < RuntimeError; end # some methods that can raise the above errors... end
I have a new strategy I've been using for code like this and it has really been working out well. Here is how I do the same thing today:
class SmarterThing def self.const_missing(error_name) # :nodoc: if error_name.to_s =~ /Error\z/ const_set(error_name, Class.new(RuntimeError)) else super end end # error raising methods here... end
Let's discuss how this works. The
const_missing() method is a hook in Ruby, much like the beloved
method_missing(). Note that
const_missing() is a class method though, instead of an instance method. When a constant is used and Ruby can't find it, a call to the hook will be triggered.
In this version, I just check to see if the constant name ends in
Error. If it does, I summon an
Exception subclass on the fly. Other calls to this hook are forwarded on to Ruby's default error raising implementation via
The building of the
Exception subclass has a few interesting points of note. First, we see that we can build a
Class object as we do any other with a simple call to
new(). Beyond that, we can pass
new() a parent Class we would like to inherit from. So if you would prefer to inherit from
StandardError, you can just change the reference here. Finally,
const_set() assigns the new
Class to the constant name referenced and returns it. This means that future references for the same constant will not go through this hook and will receive the same
That's the how, but let's talk a little about the why.
When I showed this trick to a friend, he complained that these summoned errors are not easily RDoced. That's true, but I've actually found this is improving my documentation instead of hurting it.
Raise your hand if you tend to click on all the errors listed in the API documentation and read about those. Yeah, I don't either. With those gone (and note that I explicitly disabled RDoc for my hack), I just add details about the
Exceptions a method can raise to the RDoc of that method. The end result of all this is that the documentation has moved to a place where it is helpful to me and thus I actually read it.
A final bonus of this technique is that it even works if you are dynamically generating error names in some code, say with