Naming Things

Share this post

Custom exception inheritance

www.namingthings.org

Discover more from Naming Things

Adventures in software design with Joel Drapper.
Over 1,000 subscribers
Continue reading
Sign in

Custom exception inheritance

Joel Drapper
Oct 25, 2022
Share this post

Custom exception inheritance

www.namingthings.org
Share

It’s a good idea to define custom exception classes in Ruby libraries to allow users to match against specific library exceptions. For example, if you want to rescue a Phlex name error, you can match against Phlex::NameError.

We also want to allow people to match with varying levels of precision. For example, we should be able to rescue Phlex::Error to catch any of our libraries custom errors. We can do this by creating a base Error class and having our other exceptions inherit from it.

module Phlex
  Error = Class.new(StandardError)
  NameError = Class.new(Error)
  ArgumentError = Class.new(Error)
end

Phlex::NameError and Phlex::ArgumentError are both Phlex::Errors so we can match either of these by rescuing Phlex::Error.

But what if we want to give our errors semantic meaning? Phlex::NameError should be a NameError and Phlex::ArgumentError should be an ArgumentError. We could adjust the errors to inherit from the semantic counterparts, but in doing so, we’d lose the ability to rescue all Phlex errors as Phlex::Error.

module Phlex
  Error = Class.new(StandardError)
  NameError = Class.new(NameError)
  ArgumentError = Class.new(ArgumentError)
end

Phlex::ArgumentError is now a semantic ArgumentError and will match when rescuing ArgumentError, but it won’t match when rescuing Phlex::Error.

What I like to do is define an Error module and mix it into my custom exceptions. Here’s what that looks like:

module Phlex
  Error = Module.new

  NameError = Class.new(NameError) { include Error }
  ArgumentError = Class.new(ArgumentError) { include Error }
  StandardError = Class.new(StandardError) { include Error }
end

This pattern allows us to inherit from semantic Ruby exceptions while maintaining our ability to match against all library errors. Phlex::ArgumentError, for example, is still an ArgumentError, but it’s also a Phlex::Error.

We can’t raise a Phlex::Error itself because it’s just a module, but we can define Phlex::StandardError instead.

Share this post

Custom exception inheritance

www.namingthings.org
Share
Comments
Top
New

No posts

Ready for more?

© 2023 Naming Things
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing