This is part 3 of a 5-part series on Ruby tips and tricks gleaned from our team’s pull requests over the last two years. Part 1 covers blocks and ranges, and part 2 deals with destructuring and type conversions.
Dealing with exceptions can be tricky, and it’s easy to dig a hole for yourself that’s much harder to get out of than in. There’s a couple of rules you can follow that might be a little more work to begin with but pay off in the long run, making your code easier to debug.
First off, don’t use the statement-modifier (postfix)
Here’s an example method that’ll return
nil if there’s an error fetching an
item from an array.
However we have no idea what error it’s hiding. The array could be
nil, the index could be a string, you could have made a typo on
or any other error you could imagine.
This means we can’t work out the intention of the code from reading it, and the
result of unexpected errors doesn’t show up here, but later on in the code when
something gets passed that
nil. At that point it can be very hard to work out
that some unexpected error here is the source of that nil.
It’s going to save a lot of hassle if you use the standard form of rescue, either with the type of error we were expecting, or some logging.
rescue => e is a shortcut for
rescue StandardError => e, just like a bare
rescue is a shortcut for
rescue StandardError. This means the
only rescue from StandardError or subclasses of StandardError. Generally you
should follow Ruby’s example and only rescue from StandardError and its
subclasses, never from Exception. Rescuing from Exception can mean you catch
things you really shouldn’t, like SystemExit, the exception raised when your
program has been asked to exit. If you rescue this you can end up with a
program you can’t quit.
Likewise when raising errors you should only raise errors that are subclasses of StandardError, or you could end up with errors that blow though error handling they shouldn’t.
A good pattern to use in your own projects, particularly gems, is to define a general error for your whole project, and then have all others inherit from this.
This way you can easily rescue from any error in your project with
rescue MyProject::Error, or you can be more specific.
A slightly nicer way to write this, giving you an easier to read list is to take
advantage of the
Class.new generates a new class
inheriting from the one supplied as an argument. Ruby will also name a class
created like this after the first constant it is assigned to.
One problem you may face with this approach is that not all the errors generated by your code are directly raised by your code. If for example you’re making HTTP requests you’re likely to get connection errors at some point. Now you could rescue these errors in your code, and raise new errors with types you control, but then you’re discarding valuable debugging information; the original class, message and backtrace.
A workaround for this takes advantage of Ruby allowing you to supply a class
or module to
rescue uses the case equality operator
match its arguments against the exception. On Class this returns true if the
argument is an instance of the class or instance of a subclass. On Module it
returns true if the argument has the module included or extended in to it.
This means if you instead define the base error as a module, and include it
into the more specific errors you get the same behaviour as before, but you can
also ‘tag’ arbitrary errors raised by other bits of code you are using by
extending them with the base error. These can then also be rescued by a
rescue of the base error, but keep their original class, message and
backtrace (you’ll no longer be able to raise the base error, but that’s ok,
you should be raising the more specific errors).
Another use for this would be adapter classes for two disparate services (say
database clients for example), mapping the various errors to the same
categories, so your
rescues still work after you switch out the client under
This technique does however come with a drawback. The
#extend call will
invalidate Ruby’s global method cache, slowing your app for a moment each time
it’s called. We will be getting more fine-grained method cache invalidation in
Ruby 2.1, at which point there’s nothing to worry about. But even now this can
be a very useful and powerful technique.
Modules serve a number of purposes in Ruby. Perhaps the most visible is as the name-spacing mechanism, and then there is their main intended use as ‘mixins’. Modules like Enumerable and Comparable are fantastic, easily allowing you to add complex behaviour to your classes by defining just a few methods and including the modules. Modules have some other uses too.
Sometimes you have a bunch of related methods that are much closer to functions, they take and input, return an output, and don’t do anything with self. Modules act as great way to group these methods together.
Here’s one from one of our apps (there are a couple more methods, but they’re a bit too specific to share).
module_function declaration at the start of the module is a method
visibility modifier, like
protected. It makes the
method available directly on the module like a class method, and as a private
instance method so the method is available un-prefixed when the module is
A similar effect can be achieved switching
Here the instance methods of the module are copied as class methods. The
difference is that you can use the other method visibility modifiers, making
methods that are private on the module, or public on the instance (where they
would have all been public on the module and private on the instance with
Modules also make for handy singleton objects with no need to mess about preventing more than one copy being instantiated or working out how to get ahold of the reference, it’s all built in to Ruby.
Head on to Part 4.