Feature #18145
closedRescue by nested exception
Description
The introduction of Exception#cause
helps a lot when debugging nested errors.
Same goes for wrapped errors. I'm not really sure whether such wrapped errors are an advisable pattern to begin with, feel free to comment on this, but I've used it in a couple of vendored gems dealing with payment providers.
Here's some simplified code to illustrate. The Payment
class deals with all the gory things such as authorization, coercion or API quirks. A simplified version might look like this:
require 'rest_client'
module Provider
class FindError < StandardError; end
class Payment
attr_reader :id, :amount
private_class_method :new
def initialize(id, amount)
@id, @amount = id, amount
end
def self.find(id)
response = RestClient.get('https://api.provider.com/payments', params: { id: id })
body = JSON.parse(response.body)
new(id, body.fetch('amount'))
rescue
raise FindError
end
end
end
You can easily rescue
from anything going wrong when loading a payment:
begin
Provider::Payment.find(123)
rescue FindError
...
end
However, you might want to rescue differently for some specific causes (e.g. not found) but not for others (e.g. timeout):
begin
Provider::Payment.find(123)
rescue FindError => error
if error.cause.instance_of? RestClient::NotFound
...
else
...
end
end
How about allowing to rescue by nested exception with a syntax like?
begin
Provider::Payment.find(123)
rescue FindError & RestClient::NotFound
...
rescue FindError
...
end