Skip to content

Instantly share code, notes, and snippets.

@timuruski
Last active January 1, 2016 18:59
Show Gist options
  • Save timuruski/8187571 to your computer and use it in GitHub Desktop.
Save timuruski/8187571 to your computer and use it in GitHub Desktop.
Nested exception mixin for Ruby
# Adapted from from: http://nestegg.rubyforge.org/
module NestedException
# Public: Initializes a new nested error, the original error defaults
# to the global error variable so that it doesn't need to be explicity
# provided when re-raising an exception.
def initialize(message = nil, cause = $!)
@cause = cause
super(message || cause && cause.message)
end
attr_reader :cause
def set_backtrace(new_backtrace)
if cause
last_line = new_backtrace.last
cause_lines = cause.backtrace.take_while { |line| line != last_line }
cause_lines << last_line
new_backtrace << "cause: #{cause.class.name}: #{cause}"
new_backtrace.concat(cause_lines)
end
super(new_backtrace)
end
end
require 'nested_exception'
describe NestedException do
# Helper to test re-raising a nested exception, it returns the result
# of the block so it can be examined with assertions.
def raise_nested(cause)
begin
fail cause
rescue => original_error
raise yield original_error
end
rescue => yielded_error
return yielded_error
end
let(:original) { RuntimeError.new('Born to fail ') }
let(:nested_exception) {
Class.new(StandardError) do
include NestedException
end
}
it 'defaults to details about the original exception' do
reraised = raise_nested(original) do |_|
nested_exception.new
end
expect(reraised.message).to eq original.message
expect(reraised.cause).to eq original
end
it 'includes part of the original backtrace' do
reraised = raise_nested(original) do |_|
nested_exception.new
end
expect(reraised.backtrace).to include(*original.backtrace)
end
it 'accepts a specific error message' do
reraised = raise_nested(original) do |_|
nested_exception.new('Alternate message')
end
expect(reraised.message).to eq 'Alternate message'
end
it 'does not prevent overriding params for initialize' do
nested_exception = Class.new(StandardError) do
attr_reader :user
def initialize(user, original)
super(original.message)
@user = user
end
end
reraised = raise_nested(original) do |error|
nested_exception.new('Alice', error)
end
expect(reraised.user).to eq 'Alice'
expect(reraised.message).to eq original.message
expect(reraised.cause).to eq original
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment