Skip to content

Commit 99e141f

Browse files
committed
Add tests for exotic rescued error capturing
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.
1 parent 440fea7 commit 99e141f

File tree

2 files changed

+120
-5
lines changed

2 files changed

+120
-5
lines changed

language/fixtures/rescue.rb

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,108 @@
11
module RescueSpecs
2+
class ClassVariableCaptor
3+
def capture(msg)
4+
raise msg
5+
rescue => @@captured_error
6+
:caught
7+
end
8+
9+
def captured_error
10+
self.class.remove_class_variable(:@@captured_error)
11+
end
12+
end
13+
14+
class ConstantCaptor
15+
# Using lambda gets around the dynamic constant assignment warning
16+
CAPTURE = -> msg {
17+
begin
18+
raise msg
19+
rescue => CapturedError
20+
:caught
21+
end
22+
}
23+
24+
def capture(msg)
25+
CAPTURE.call(msg)
26+
end
27+
28+
def captured_error
29+
self.class.send(:remove_const, :CapturedError)
30+
end
31+
end
32+
33+
class GlobalVariableCaptor
34+
def capture(msg)
35+
raise msg
36+
rescue => $captured_error
37+
:caught
38+
end
39+
40+
def captured_error
41+
$captured_error.tap do
42+
$captured_error = nil # Can't remove globals, only nil them out
43+
end
44+
end
45+
end
46+
47+
class InstanceVariableCaptor
48+
attr_reader :captured_error
49+
50+
def capture(msg)
51+
raise msg
52+
rescue => @captured_error
53+
:caught
54+
end
55+
end
56+
57+
class LocalVariableCaptor
58+
attr_reader :captured_error
59+
60+
def capture(msg)
61+
raise msg
62+
rescue => captured_error
63+
@captured_error = captured_error
64+
:caught
65+
end
66+
end
67+
68+
class SafeNavigationSetterCaptor
69+
attr_accessor :captured_error
70+
71+
def capture(msg)
72+
raise msg
73+
rescue => self&.captured_error
74+
:caught
75+
end
76+
end
77+
78+
class SetterCaptor
79+
attr_accessor :captured_error
80+
81+
def capture(msg)
82+
raise msg
83+
rescue => self.captured_error
84+
:caught
85+
end
86+
end
87+
88+
class SquareBracketsCaptor
89+
def capture(msg)
90+
@hash = {}
91+
92+
raise msg
93+
rescue => self[:error]
94+
:caught
95+
end
96+
97+
def []=(key, value)
98+
@hash[key] = value
99+
end
100+
101+
def captured_error
102+
@hash[:error]
103+
end
104+
end
105+
2106
def self.begin_else(raise_exception)
3107
begin
4108
ScratchPad << :one

language/rescue_spec.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,22 @@ class ArbitraryException < StandardError
2323
end.should == :caught
2424
end
2525

26-
it "can capture the raised exception in a local variable" do
27-
begin
28-
raise SpecificExampleException, "some text"
29-
rescue SpecificExampleException => e
30-
e.message.should == "some text"
26+
{
27+
# Standard use case
28+
'can capture the raised exception in a local variable' => RescueSpecs::LocalVariableCaptor,
29+
# Exotic use cases
30+
'can capture the raised exception in a class variable' => RescueSpecs::ClassVariableCaptor,
31+
'can capture the raised exception in a constant' => RescueSpecs::ConstantCaptor,
32+
'can capture the raised exception in a global variable' => RescueSpecs::GlobalVariableCaptor,
33+
'can capture the raised exception in an instance variable' => RescueSpecs::InstanceVariableCaptor,
34+
'can capture the raised exception using a safely navigated setter method' => RescueSpecs::SafeNavigationSetterCaptor,
35+
'can capture the raised exception using a setter method' => RescueSpecs::SetterCaptor,
36+
'can capture the raised exception using a square brackets setter' => RescueSpecs::SquareBracketsCaptor,
37+
}.each do |description, klass|
38+
it description do
39+
captor = klass.new
40+
captor.capture('some text').should == :caught # Ensure rescue body still runs
41+
captor.captured_error.message.should == 'some text'
3142
end
3243
end
3344

0 commit comments

Comments
 (0)