Simple Error Classes
The obvious way to define a custom error class in Ruby would be to create a new class that inherits from StandardError:
class MyCustomError < StandardError
end
However, for simple error classes which don't add any additional data or functionality, we can achieve the same with a single line of code:
MyCustomError = Class.new(StandardError)
This is a common Ruby idiom for defining custom error classes. It also surfaces one of the wonders of Ruby: that classes are objects too, and can be created and assigned to constants at runtime. The Class constructor takes a superclass as an argument and returns a new class that inherits from it.
Here's an example of how this pattern can be used to define different errors for different types of HTTP responses:
require "net/http"
class HttpClient
# Base error class for all HTTP errors
HttpError = Class.new(StandardError)
# Specific error classes for different HTTP status codes
NotFoundError = Class.new(HttpError)
UnauthorizedError = Class.new(HttpError)
ServerError = Class.new(HttpError)
def self.get(url)
response = Net::HTTP.get_response(URI(url))
case response
when Net::HTTPSuccess
response.body
when Net::HTTPNotFound
raise NotFoundError, "Resource not found at #{url}"
when Net::HTTPUnauthorized
raise UnauthorizedError, "Unauthorized access to #{url}"
when Net::HTTPServerError
raise ServerError, "Server error #{response.code} for #{url}"
else
raise HttpError, "HTTP error #{response.code} for #{url}"
end
end
end
HttpClient.get("https://httpbin.org/status/401")
# => HttpClient::UnauthorizedError Unauthorized access to https://httpbin.org/status/401
HttpClient.get("https://httpbin.org/status/404")
# => HttpClient::NotFoundError Resource not found at https://httpbin.org/status/404
HttpClient.get("https://httpbin.org/status/500")
# => HttpClient::ServerError Server error 500 for https://httpbin.org/status/500
HttpClient.get("https://httpbin.org/json")
# => "{ ... }"
One of these days we'll take a closer look at Ruby's object model and the "everything is an object" philosophy, but for now, just appreciate how this pattern allows us to define error classes without any boilerplate.