Skip to content

Commit

Permalink
Add tests for exotic rescued error capturing
Browse files Browse the repository at this point in the history
The idiomatic way to capture exceptions is to assign to a local variable

    rescue => local_variable

However, other kinds of LVALUEs also work

    rescue => $global_variable
    rescue => @@class_variable
    rescue => @instance_variable
    rescue => Constant
    rescue => receiver&.setter_method
    rescue => receiver.setter_method
    rescue => receiver[:key]

Some of the tests involve side effects to the global state. We can
remove the constant and class variable, but the best we can go for the
global variable is to nil it out. This is effectively identical to
removing it though, as nil is the default when accessing an undefined
global.
  • Loading branch information
sambostock committed Jun 24, 2020
1 parent 440fea7 commit 13db389
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 5 deletions.
104 changes: 104 additions & 0 deletions language/fixtures/rescue.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,108 @@
module RescueSpecs
class ClassVariableCaptor
def capture(msg)
raise msg
rescue => @@captured_error
:caught
end

def captured_error
self.class.remove_class_variable(:@@captured_error)
end
end

class ConstantCaptor
# Using lambda gets around the dynamic constant assignment warning
CAPTURE = -> msg {
begin
raise msg
rescue => CapturedError
:caught
end
}

def capture(msg)
CAPTURE.call(msg)
end

def captured_error
self.class.send(:remove_const, :CapturedError)
end
end

class GlobalVariableCaptor
def capture(msg)
raise msg
rescue => $captured_error
:caught
end

def captured_error
$captured_error.tap do
$captured_error = nil # Can't remove globals, only nil them out
end
end
end

class InstanceVariableCaptor
attr_reader :captured_error

def capture(msg)
raise msg
rescue => @captured_error
:caught
end
end

class LocalVariableCaptor
attr_reader :captured_error

def capture(msg)
raise msg
rescue => captured_error
@captured_error = captured_error
:caught
end
end

class SafeNavigationSetterCaptor
attr_accessor :captured_error

def capture(msg)
raise msg
rescue => self&.captured_error
:caught
end
end

class SetterCaptor
attr_accessor :captured_error

def capture(msg)
raise msg
rescue => self.captured_error
:caught
end
end

class SquareBracketsCaptor
def capture(msg)
@hash = {}

raise msg
rescue => self[:error]
:caught
end

def []=(key, value)
@hash[key] = value
end

def captured_error
@hash[:error]
end
end

def self.begin_else(raise_exception)
begin
ScratchPad << :one
Expand Down
21 changes: 16 additions & 5 deletions language/rescue_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,22 @@ class ArbitraryException < StandardError
end.should == :caught
end

it "can capture the raised exception in a local variable" do
begin
raise SpecificExampleException, "some text"
rescue SpecificExampleException => e
e.message.should == "some text"
{
# Standard use case
'can capture the raised exception in a local variable' => RescueSpecs::LocalVariableCaptor,
# Exotic use cases
'can capture the raised exception in a class variable' => RescueSpecs::ClassVariableCaptor,
'can capture the raised exception in a constant' => RescueSpecs::ConstantCaptor,
'can capture the raised exception in a global variable' => RescueSpecs::GlobalVariableCaptor,
'can capture the raised exception in an instance variable' => RescueSpecs::InstanceVariableCaptor,
'can capture the raised exception using a safely navigated setter method' => RescueSpecs::SafeNavigationSetterCaptor,
'can capture the raised exception using a setter method' => RescueSpecs::SetterCaptor,
'can capture the raised exception using a square brackets setter' => RescueSpecs::SquareBracketsCaptor,
}.each do |description, klass|
it description do
captor = klass.new
captor.capture('some text').should == :caught # Ensure rescue body still runs
captor.captured_error.message.should == 'some text'
end
end

Expand Down

0 comments on commit 13db389

Please sign in to comment.