mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
f48bb1b4ad
The issue presented in #26246 showed a deeper underlying problem. When we fell back to the exception handler for an exceptions cause, we were calling that handler with the outer raised exception. This breaks the calling code's expectations, especially if the exception has methods on it behond those from `StandardError`.
142 lines
2.9 KiB
Ruby
142 lines
2.9 KiB
Ruby
require "abstract_unit"
|
|
|
|
class WraithAttack < StandardError
|
|
end
|
|
|
|
class MadRonon < StandardError
|
|
end
|
|
|
|
class CoolError < StandardError
|
|
end
|
|
|
|
module WeirdError
|
|
def self.===(other)
|
|
Exception === other && other.respond_to?(:weird?)
|
|
end
|
|
end
|
|
|
|
class Stargate
|
|
# Nest this so the 'NuclearExplosion' handler needs a lexical const_get
|
|
# to find it.
|
|
class NuclearExplosion < StandardError; end
|
|
|
|
attr_accessor :result
|
|
|
|
include ActiveSupport::Rescuable
|
|
|
|
rescue_from WraithAttack, with: :sos_first
|
|
|
|
rescue_from WraithAttack, with: :sos
|
|
|
|
rescue_from "NuclearExplosion" do
|
|
@result = "alldead"
|
|
end
|
|
|
|
rescue_from MadRonon do |e|
|
|
@result = e.message
|
|
end
|
|
|
|
rescue_from WeirdError do
|
|
@result = "weird"
|
|
end
|
|
|
|
def dispatch(method)
|
|
send(method)
|
|
rescue Exception => e
|
|
rescue_with_handler(e)
|
|
end
|
|
|
|
def attack
|
|
raise WraithAttack
|
|
end
|
|
|
|
def nuke
|
|
raise NuclearExplosion
|
|
end
|
|
|
|
def ronanize
|
|
raise MadRonon.new("dex")
|
|
end
|
|
|
|
def fall_back_to_cause
|
|
# This exception is the cause and has a handler.
|
|
ronanize
|
|
rescue
|
|
# This is the exception we'll handle that doesn't have a cause.
|
|
raise "unhandled RuntimeError with a handleable cause"
|
|
end
|
|
|
|
def weird
|
|
StandardError.new.tap do |exc|
|
|
def exc.weird?
|
|
true
|
|
end
|
|
|
|
raise exc
|
|
end
|
|
end
|
|
|
|
def sos
|
|
@result = "killed"
|
|
end
|
|
|
|
def sos_first
|
|
@result = "sos_first"
|
|
end
|
|
end
|
|
|
|
class CoolStargate < Stargate
|
|
attr_accessor :result
|
|
|
|
include ActiveSupport::Rescuable
|
|
|
|
rescue_from CoolError, with: :sos_cool_error
|
|
|
|
def sos_cool_error
|
|
@result = "sos_cool_error"
|
|
end
|
|
end
|
|
|
|
class RescuableTest < ActiveSupport::TestCase
|
|
def setup
|
|
@stargate = Stargate.new
|
|
@cool_stargate = CoolStargate.new
|
|
end
|
|
|
|
def test_rescue_from_with_method
|
|
@stargate.dispatch :attack
|
|
assert_equal "killed", @stargate.result
|
|
end
|
|
|
|
def test_rescue_from_with_block
|
|
@stargate.dispatch :nuke
|
|
assert_equal "alldead", @stargate.result
|
|
end
|
|
|
|
def test_rescue_from_with_block_with_args
|
|
@stargate.dispatch :ronanize
|
|
assert_equal "dex", @stargate.result
|
|
end
|
|
|
|
def test_rescue_from_error_dispatchers_with_case_operator
|
|
@stargate.dispatch :weird
|
|
assert_equal "weird", @stargate.result
|
|
end
|
|
|
|
def test_rescues_defined_later_are_added_at_end_of_the_rescue_handlers_array
|
|
expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError"]
|
|
result = @stargate.send(:rescue_handlers).collect(&:first)
|
|
assert_equal expected, result
|
|
end
|
|
|
|
def test_children_should_inherit_rescue_definitions_from_parents_and_child_rescue_should_be_appended
|
|
expected = ["WraithAttack", "WraithAttack", "NuclearExplosion", "MadRonon", "WeirdError", "CoolError"]
|
|
result = @cool_stargate.send(:rescue_handlers).collect(&:first)
|
|
assert_equal expected, result
|
|
end
|
|
|
|
def test_rescue_falls_back_to_exception_cause
|
|
@stargate.dispatch :fall_back_to_cause
|
|
assert_equal "dex", @stargate.result
|
|
end
|
|
end
|