A Shorthand Syntax for Defining Errors in Ruby

other, ruby

A common practice that I use in Ruby is defining custom error classes for my own code. There’s no great reason to do it (that I’ve heard) other than providing a more expressive error message than the standard error/exception classes.

Here’s a contrived example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
require 'open3'

class Curl
  # From: https://curl.haxx.se/libcurl/c/libcurl-errors.html
  CURLE_OK = 0
  CURLE_UNSUPPORTED_PROTOCOL = 1
  CURLE_FAILED_INIT = 2
  CURLE_URL_MALFORMAT = 3
  # ... etc ...

  # custom errors
  class CurlUnsupportedProtocolError < StandardError
  end
  class CurlFailedInitError < StandardError
  end
  class CurlURLMalformatError < StandardError
  end
  # ... etc ...

  def initialize(url)
    @url = url
  end

  def execute
    cmd = ['curl', @url]
    out, err, status = Open3.capture3(*cmd)

    case status.exitstatus
    when CURLE_OK
      # Do work
    when CURLE_UNSUPPORTED_PROTOCOL
      raise CurlUnsupportedProtocolError.new('<informative error message>')
    when CURLE_FAILED_INIT
      raise CurlFailedInitError.new('<another informative error message>')
    when CURLE_URL_MALFORMAT
      raise CurlURLMalformatError.new('<many errors, such excite>')
    # ... etc ...
    end
  end
end

puts Curl.new('google.com').execute

Notice that we’re declaring three custom error classes CurlUnsupportedProtocolError, CurlFailedInitError, and CurlURLMalformatError. As you can imagine, if we completed this for all 80 or so CURLE exception codes we’d have a long list of errors.

We could use semi-colons to simplify things:

1
2
3
4
5
6
7
8
9
# That's ugly!
class CurlUnsupportedProtocolError < StandardError; end
class CurlFailedInitError < StandardError; end
class CurlURLMalformatError < StandardError; end

# Better, but still ugly
class CurlUnsupportedProtocolError  < StandardError; end
class CurlFailedInitError           < StandardError; end
class CurlURLMalformatError         < StandardError; end

But I’m not particularly a fan of that syntax. Instead you can use some shorthand syntax from Ruby’s Class definitions. Because even Ruby’s classes are classes! Very Wow! :doge:

1
2
3
4
# Using Class.new from https://ruby-doc.org/core-1.9.3/Class.html#method-c-new
CurlUnsupportedProtocolError = Class.new(StandardError)
CurlFailedInitError          = Class.new(StandardError)
CurlURLMalformatError        = Class.new(StandardError)

The Class.new will create an anonymous class using the value passed in as the superclass. And then we assign it to our constants and viola! We get a new set of error classes.

Since seeing the above error syntax, it’s the one I like to stick with in all of my code because of the readability of it. I’ve never come across any other application of Class.new, although I’m sure there are some if I went looking.

Hope this is something you’ll find useful!

This page was delicately crafted on by Gavin Miller.