return_bang 1.0 → 1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +3 -2
- data/History.txt +8 -1
- data/README.rdoc +14 -4
- data/Rakefile +2 -2
- data/lib/return_bang.rb +226 -41
- data/test/test_return_bang.rb +318 -0
- metadata +52 -30
- metadata.gz.sig +4 -2
data.tar.gz.sig
CHANGED
@@ -1,2 +1,3 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
7�d+��*"��������N֝��W4�����Y�H�<!���־�b��5
|
2
|
+
k���Sκ������O�4���@M>�~r��e�1S��F[y_�Ɓ���e8ӊ�z����ޒ��:���{Ϥ�/R�����I��O`�������S�\
|
3
|
+
�d���6E<Tr&�d����"\�m�P�C4����|���V��^����e����N��Ty�w���$�2<$�w�|��dJ�)]A8�)�����{?��
|
data/History.txt
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
+
=== 1.1 / 2012-04-01
|
2
|
+
|
3
|
+
* Minor enhancements
|
4
|
+
* Added raise! to raise exceptions
|
5
|
+
* Added rescue! to rescue exceptions raised
|
6
|
+
* Added ensure! to always execute a block of code to perform cleanup
|
7
|
+
|
1
8
|
=== 1.0 / 2011-12-20
|
2
9
|
|
3
|
-
* Major
|
10
|
+
* Major enhancement
|
4
11
|
* Birthday!
|
5
12
|
|
data/README.rdoc
CHANGED
@@ -6,16 +6,26 @@ bugs :: https://github.com/drbrain/return_bang/issues
|
|
6
6
|
|
7
7
|
== Description
|
8
8
|
|
9
|
-
return_bang implements non-local exits
|
10
|
-
|
11
|
-
|
9
|
+
return_bang implements non-local exits for methods. As a bonus, you also get
|
10
|
+
exception handling that ignores standard Ruby's inflexible begin; rescue;
|
11
|
+
ensure; end syntax.
|
12
|
+
|
13
|
+
Use return_bang to exit back to a processing loop from deeply nested code, or
|
14
|
+
just to confound your enemies *and* your friends! What could possibly go
|
15
|
+
wrong?
|
12
16
|
|
13
17
|
== Features
|
14
18
|
|
15
19
|
* Implements non-local exits for methods
|
16
20
|
* Nestable
|
17
21
|
* Named and stack-based exit points, go exactly where you need to be
|
18
|
-
*
|
22
|
+
* Full exception handling support through raise!, rescue! and ensure!
|
23
|
+
* Ignores pesky ensure, rescue and require blocks for when you really, really
|
24
|
+
need to return
|
25
|
+
|
26
|
+
== Problems
|
27
|
+
|
28
|
+
* Not enough use of continuations
|
19
29
|
|
20
30
|
== Synopsis
|
21
31
|
|
data/Rakefile
CHANGED
@@ -5,14 +5,14 @@ require 'hoe'
|
|
5
5
|
|
6
6
|
Hoe.plugin :minitest
|
7
7
|
Hoe.plugin :git
|
8
|
+
Hoe.plugin :travis
|
8
9
|
|
9
10
|
Hoe.spec 'return_bang' do
|
10
11
|
developer 'Eric Hodel', '[email protected]'
|
11
12
|
|
12
13
|
rdoc_locations << 'docs.seattlerb.org:/data/www/docs.seattlerb.org/return_bang/'
|
13
14
|
|
14
|
-
|
15
|
-
self.extra_rdoc_files << 'README.rdoc'
|
15
|
+
spec_extras['required_ruby_version'] = '>= 1.9.2'
|
16
16
|
end
|
17
17
|
|
18
18
|
# vim: syntax=ruby
|
data/lib/return_bang.rb
CHANGED
@@ -1,59 +1,78 @@
|
|
1
|
-
|
2
|
-
require 'continuation'
|
3
|
-
rescue LoadError
|
4
|
-
# in 1.8 it's built-in
|
5
|
-
end
|
1
|
+
require 'continuation'
|
6
2
|
|
7
3
|
##
|
8
4
|
# ReturnBang is allows you to perform non-local exits from your methods. One
|
9
5
|
# potential use of this is in a web framework so that a framework-provided
|
10
6
|
# utility methods can jump directly back to the request loop.
|
11
7
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
8
|
+
# Since providing just non-local exits is insufficient for modern Ruby
|
9
|
+
# development, full exception handling support is also provided via #raise!,
|
10
|
+
# #rescue! and #ensure!. This exception handling support completely bypasses
|
11
|
+
# Ruby's strict <tt>begin; rescue; ensure; return</tt> handling.
|
15
12
|
#
|
16
13
|
# require 'return_bang' gives you a module you may include only in your
|
17
14
|
# application or library code. require 'return_bang/everywhere' includes
|
18
15
|
# ReturnBang in Object, so it is only recommended for application code use.
|
19
16
|
#
|
20
|
-
#
|
17
|
+
# == Methods
|
18
|
+
#
|
19
|
+
# return_here is used to designate where execution should be resumed. Return
|
20
|
+
# points may be arbitrarily nested. #return! resumes at the previous resume
|
21
|
+
# point, #return_to returns to a named return point.
|
22
|
+
#
|
23
|
+
# #raise! is used to indicate an exceptional situation has occurred and you
|
24
|
+
# would like to skip the rest of the execution.
|
25
|
+
#
|
26
|
+
# #rescue! is used to rescue exceptions if you have a way to handle them.
|
27
|
+
#
|
28
|
+
# #ensure! is used when you need to perform cleanup where an exceptional
|
29
|
+
# situation may occur.
|
30
|
+
#
|
31
|
+
# == Example
|
21
32
|
#
|
22
33
|
# include ReturnBang
|
23
34
|
#
|
24
35
|
# def framework_loop
|
25
36
|
# loop do
|
26
|
-
# # setup code
|
27
|
-
#
|
28
37
|
# return_here do
|
29
|
-
#
|
30
|
-
# end
|
38
|
+
# # setup this request
|
31
39
|
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
40
|
+
# ensure! do
|
41
|
+
# # clean up this request
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# rescue! FrameworkError do
|
45
|
+
# # display framework error
|
46
|
+
# end
|
35
47
|
#
|
36
|
-
#
|
37
|
-
#
|
48
|
+
# rescue! do
|
49
|
+
# # display application error
|
50
|
+
# end
|
38
51
|
#
|
39
|
-
#
|
52
|
+
# user_code
|
53
|
+
# end
|
54
|
+
# end
|
40
55
|
# end
|
41
56
|
#
|
42
57
|
# def user_code
|
43
58
|
# user_utility_method
|
44
|
-
#
|
45
|
-
#
|
59
|
+
#
|
60
|
+
# other_condition = some_more code
|
61
|
+
#
|
62
|
+
# return! if other_condition
|
63
|
+
#
|
64
|
+
# # rest of user method
|
46
65
|
# end
|
47
66
|
#
|
48
67
|
# def user_utility_method
|
49
|
-
#
|
50
|
-
#
|
51
|
-
# #
|
68
|
+
# raise! "there was an error" if some_condition
|
69
|
+
#
|
70
|
+
# # rest of utility method
|
52
71
|
# end
|
53
72
|
|
54
73
|
module ReturnBang
|
55
74
|
|
56
|
-
VERSION = '1.
|
75
|
+
VERSION = '1.1'
|
57
76
|
|
58
77
|
##
|
59
78
|
# Raised when attempting to return! when you haven't registered a location
|
@@ -63,31 +82,185 @@ module ReturnBang
|
|
63
82
|
class NonLocalJumpError < StandardError
|
64
83
|
end
|
65
84
|
|
85
|
+
def _make_exception args # :nodoc:
|
86
|
+
case args.length
|
87
|
+
when 0 then
|
88
|
+
if exception = Thread.current[:current_exception] then
|
89
|
+
exception
|
90
|
+
else
|
91
|
+
RuntimeError.new
|
92
|
+
end
|
93
|
+
when 1 then # exception or string
|
94
|
+
arg = args.first
|
95
|
+
|
96
|
+
case arg = args.first
|
97
|
+
when Class then
|
98
|
+
unless Exception >= arg then
|
99
|
+
raise TypeError,
|
100
|
+
"exception class/object expected (not #{arg.inspect})"
|
101
|
+
end
|
102
|
+
arg.new
|
103
|
+
else
|
104
|
+
RuntimeError.new arg
|
105
|
+
end
|
106
|
+
when 2 then # exception, string
|
107
|
+
klass, message = args
|
108
|
+
klass.new message
|
109
|
+
else
|
110
|
+
raise ArgumentError, 'too many arguments to raise!'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Executes the ensure blocks in +frames+ in the correct order.
|
116
|
+
|
117
|
+
def _return_bang_cleanup frames # :nodoc:
|
118
|
+
chunked = frames.chunk do |type,|
|
119
|
+
type
|
120
|
+
end
|
121
|
+
|
122
|
+
chunked.reverse_each do |type, chunk_frames|
|
123
|
+
case type
|
124
|
+
when :ensure then
|
125
|
+
chunk_frames.each do |_, block|
|
126
|
+
block.call
|
127
|
+
end
|
128
|
+
when :rescue then
|
129
|
+
if exception = Thread.current[:current_exception] then
|
130
|
+
frame = chunk_frames.find do |_, block, objects|
|
131
|
+
objects.any? do |object|
|
132
|
+
object === exception
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
next unless frame
|
137
|
+
|
138
|
+
# rebuild stack since we've got a handler for the exception.
|
139
|
+
unexecuted = frames[0, frames.index(frame) - 1]
|
140
|
+
_return_bang_stack.concat unexecuted if unexecuted
|
141
|
+
|
142
|
+
_, handler, = frame
|
143
|
+
handler.call exception
|
144
|
+
|
145
|
+
return # the exception was handled, don't continue up the stack
|
146
|
+
end
|
147
|
+
when :return then
|
148
|
+
# ignore
|
149
|
+
else
|
150
|
+
raise "[bug] unknown return_bang frame type #{type}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
66
155
|
def _return_bang_names # :nodoc:
|
67
156
|
Thread.current[:return_bang_names] ||= {}
|
68
157
|
end
|
69
158
|
|
70
|
-
|
71
|
-
|
72
|
-
|
159
|
+
def _return_bang_pop # :nodoc:
|
160
|
+
frame = _return_bang_stack.pop
|
161
|
+
|
162
|
+
_return_bang_names.delete _return_bang_names.key _return_bang_stack.length
|
163
|
+
|
164
|
+
frame
|
165
|
+
end
|
166
|
+
|
167
|
+
def _return_bang_stack # :nodoc:
|
168
|
+
Thread.current[:return_bang_stack] ||= []
|
169
|
+
end
|
170
|
+
|
171
|
+
##
|
172
|
+
# Unwinds the stack to +continuation+ including trimming the stack above the
|
173
|
+
# continuation, removing named return_heres that can't be reached and
|
174
|
+
# executing any ensures in the trimmed stack.
|
73
175
|
|
74
|
-
|
176
|
+
def _return_bang_unwind_to continuation # :nodoc:
|
177
|
+
found = false
|
75
178
|
|
76
|
-
|
179
|
+
frames = _return_bang_stack.select do |_, block|
|
180
|
+
found || found = block == continuation
|
77
181
|
end
|
78
|
-
else # 1.8
|
79
|
-
def _return_bang_pop # :nodoc:
|
80
|
-
return_point = _return_bang_stack.pop
|
81
|
-
value = _return_bang_stack.length
|
82
182
|
|
83
|
-
|
183
|
+
start = _return_bang_stack.length - frames.length
|
184
|
+
|
185
|
+
_return_bang_stack.slice! start, frames.length
|
84
186
|
|
85
|
-
|
187
|
+
frames.each_index do |index|
|
188
|
+
offset = start + index
|
189
|
+
|
190
|
+
_return_bang_names.delete _return_bang_names.key offset
|
86
191
|
end
|
192
|
+
|
193
|
+
_return_bang_cleanup frames
|
87
194
|
end
|
88
195
|
|
89
|
-
|
90
|
-
|
196
|
+
##
|
197
|
+
# Adds an ensure block that will be run when exiting this return_here block.
|
198
|
+
#
|
199
|
+
# ensure! blocks run in the order defined and can be added at any time. If
|
200
|
+
# an exception is raised before an ensure! block is encountered, that block
|
201
|
+
# will not be executed.
|
202
|
+
#
|
203
|
+
# Example:
|
204
|
+
#
|
205
|
+
# return_here do
|
206
|
+
# ensure! do
|
207
|
+
# # this ensure! will be executed
|
208
|
+
# end
|
209
|
+
#
|
210
|
+
# raise! "uh-oh!"
|
211
|
+
#
|
212
|
+
# ensure! do
|
213
|
+
# # this ensure! will not be executed
|
214
|
+
# end
|
215
|
+
# end
|
216
|
+
|
217
|
+
def ensure! &block
|
218
|
+
_return_bang_stack.push [:ensure, block]
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Raises an exception like Kernel#raise.
|
223
|
+
#
|
224
|
+
# ensure! blocks and rescue! exception handlers will be run as the exception
|
225
|
+
# is propagated up the stack.
|
226
|
+
|
227
|
+
def raise! *args
|
228
|
+
Thread.current[:current_exception] = _make_exception args
|
229
|
+
|
230
|
+
type, = _return_bang_stack.first
|
231
|
+
|
232
|
+
_, final = _return_bang_stack.shift if type == :return
|
233
|
+
|
234
|
+
frames = _return_bang_stack.dup
|
235
|
+
|
236
|
+
_return_bang_stack.clear
|
237
|
+
|
238
|
+
_return_bang_cleanup frames
|
239
|
+
|
240
|
+
final.call if final
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# Rescues +exceptions+ raised by raise! and yields the exception caught to
|
245
|
+
# the block given.
|
246
|
+
#
|
247
|
+
# If no exceptions are given, StandardError is rescued (like the rescue
|
248
|
+
# keyword).
|
249
|
+
#
|
250
|
+
# Example:
|
251
|
+
#
|
252
|
+
# return_here do
|
253
|
+
# rescue! do |e|
|
254
|
+
# puts "handled exception #{e.class}: #{e}"
|
255
|
+
# end
|
256
|
+
#
|
257
|
+
# raise! "raising an exception"
|
258
|
+
# end
|
259
|
+
|
260
|
+
def rescue! *exceptions, &block
|
261
|
+
exceptions = [StandardError] if exceptions.empty?
|
262
|
+
|
263
|
+
_return_bang_stack.push [:rescue, block, exceptions]
|
91
264
|
end
|
92
265
|
|
93
266
|
##
|
@@ -99,7 +272,13 @@ module ReturnBang
|
|
99
272
|
raise NonLocalJumpError, 'nowhere to return to' if
|
100
273
|
_return_bang_stack.empty?
|
101
274
|
|
102
|
-
|
275
|
+
_, continuation, = _return_bang_stack.reverse.find do |type,|
|
276
|
+
type == :return
|
277
|
+
end
|
278
|
+
|
279
|
+
_return_bang_unwind_to continuation
|
280
|
+
|
281
|
+
continuation.call value
|
103
282
|
end
|
104
283
|
|
105
284
|
##
|
@@ -112,15 +291,21 @@ module ReturnBang
|
|
112
291
|
|
113
292
|
value = callcc do |cc|
|
114
293
|
_return_bang_names[name] = _return_bang_stack.length if name
|
115
|
-
_return_bang_stack.push cc
|
294
|
+
_return_bang_stack.push [:return, cc]
|
116
295
|
|
117
296
|
begin
|
118
297
|
yield
|
119
298
|
ensure
|
120
|
-
|
299
|
+
_return_bang_unwind_to cc
|
121
300
|
end
|
122
301
|
end
|
123
302
|
|
303
|
+
if exception = Thread.current[:current_exception] then
|
304
|
+
Thread.current[:current_exception] = nil
|
305
|
+
|
306
|
+
raise exception
|
307
|
+
end
|
308
|
+
|
124
309
|
# here is where the magic happens
|
125
310
|
unwind_to = Thread.current[:unwind_to]
|
126
311
|
|
data/test/test_return_bang.rb
CHANGED
@@ -13,6 +13,324 @@ class TestReturnBang < MiniTest::Unit::TestCase
|
|
13
13
|
def teardown
|
14
14
|
assert_empty _return_bang_stack
|
15
15
|
assert_empty _return_bang_names
|
16
|
+
assert_nil Thread.current[:current_exception]
|
17
|
+
end
|
18
|
+
|
19
|
+
def test__make_exception
|
20
|
+
e = _make_exception []
|
21
|
+
|
22
|
+
assert_instance_of RuntimeError, e
|
23
|
+
end
|
24
|
+
|
25
|
+
def test__make_exception_class
|
26
|
+
e = _make_exception [StandardError]
|
27
|
+
|
28
|
+
assert_instance_of StandardError, e
|
29
|
+
end
|
30
|
+
|
31
|
+
def test__make_exception_class_message
|
32
|
+
e = _make_exception [StandardError, 'hello']
|
33
|
+
|
34
|
+
assert_instance_of StandardError, e
|
35
|
+
|
36
|
+
assert_equal 'hello', e.message
|
37
|
+
end
|
38
|
+
|
39
|
+
def test__make_exception_current_exception
|
40
|
+
expected = ArgumentError.new
|
41
|
+
Thread.current[:current_exception] = expected
|
42
|
+
|
43
|
+
e = _make_exception []
|
44
|
+
|
45
|
+
assert_same expected, e
|
46
|
+
ensure
|
47
|
+
Thread.current[:current_exception] = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def test__make_exception_message
|
51
|
+
e = _make_exception %w[hello]
|
52
|
+
|
53
|
+
assert_instance_of RuntimeError, e
|
54
|
+
assert_equal 'hello', e.message
|
55
|
+
end
|
56
|
+
|
57
|
+
def test__make_exception_non_Exception
|
58
|
+
e = assert_raises TypeError do
|
59
|
+
_make_exception [String]
|
60
|
+
end
|
61
|
+
|
62
|
+
assert_equal 'exception class/object expected (not String)', e.message
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_ensure_bang
|
66
|
+
ensured = false
|
67
|
+
|
68
|
+
return_here do
|
69
|
+
ensure! do
|
70
|
+
ensured = true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
assert ensured, 'ensured was not executed'
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_ensure_bang_multiple
|
78
|
+
ensured = []
|
79
|
+
|
80
|
+
return_here do
|
81
|
+
ensure! do
|
82
|
+
ensured << 1
|
83
|
+
end
|
84
|
+
ensure! do
|
85
|
+
ensured << 2
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
assert_equal [1, 2], ensured
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_ensure_bang_multiple_return
|
93
|
+
ensured = []
|
94
|
+
|
95
|
+
return_here do
|
96
|
+
ensure! do
|
97
|
+
ensured << 1
|
98
|
+
end
|
99
|
+
ensure! do
|
100
|
+
ensured << 2
|
101
|
+
end
|
102
|
+
|
103
|
+
return!
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_equal [1, 2], ensured
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_ensure_bang_nest
|
110
|
+
ensured = []
|
111
|
+
|
112
|
+
return_here do
|
113
|
+
ensure! do
|
114
|
+
ensured << 2
|
115
|
+
end
|
116
|
+
return_here do
|
117
|
+
ensure! do
|
118
|
+
ensured << 1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
assert_equal [1, 2], ensured
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_ensure_bang_nest_raise
|
127
|
+
ensured = []
|
128
|
+
|
129
|
+
assert_raises RuntimeError do
|
130
|
+
return_here do
|
131
|
+
ensure! do
|
132
|
+
ensured << 2
|
133
|
+
end
|
134
|
+
return_here do
|
135
|
+
ensure! do
|
136
|
+
ensured << 1
|
137
|
+
end
|
138
|
+
|
139
|
+
raise!
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
assert_equal [1, 2], ensured
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_ensure_bang_raise_after
|
148
|
+
ensured = false
|
149
|
+
|
150
|
+
assert_raises RuntimeError do
|
151
|
+
return_here do
|
152
|
+
ensure! do
|
153
|
+
ensured = true
|
154
|
+
end
|
155
|
+
|
156
|
+
refute ensured, 'ensure! executed too soon'
|
157
|
+
|
158
|
+
raise!
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
assert ensured, 'ensure! not executed'
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_ensure_bang_raise_before
|
166
|
+
ensured = false
|
167
|
+
|
168
|
+
assert_raises RuntimeError do
|
169
|
+
return_here do
|
170
|
+
raise!
|
171
|
+
|
172
|
+
ensure! do
|
173
|
+
ensured = true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
refute ensured, 'ensure! must not be executed'
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_ensure_bang_raise_in_ensure
|
182
|
+
ensured = []
|
183
|
+
|
184
|
+
assert_raises RuntimeError do
|
185
|
+
return_here do
|
186
|
+
ensure! do
|
187
|
+
ensured << 2
|
188
|
+
end
|
189
|
+
|
190
|
+
return_here do
|
191
|
+
ensure! do
|
192
|
+
ensured << 1
|
193
|
+
raise!
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
assert_equal [1, 2], ensured
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_raise_bang
|
203
|
+
e = assert_raises RuntimeError do
|
204
|
+
return_here do
|
205
|
+
raise! 'hello'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
assert_equal 'hello', e.message
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_raise_bang_ignore_rescue
|
213
|
+
assert_raises RuntimeError do
|
214
|
+
return_here do
|
215
|
+
begin
|
216
|
+
raise! 'hello'
|
217
|
+
rescue
|
218
|
+
flunk 'must not execute rescue body'
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_raise_bang_re_raise
|
225
|
+
rescues = []
|
226
|
+
|
227
|
+
assert_raises ArgumentError do
|
228
|
+
return_here do
|
229
|
+
rescue! do
|
230
|
+
rescues << 2
|
231
|
+
end
|
232
|
+
|
233
|
+
return_here do
|
234
|
+
rescue! do
|
235
|
+
rescues << 1
|
236
|
+
|
237
|
+
raise!
|
238
|
+
end
|
239
|
+
|
240
|
+
raise! ArgumentError, 'hello'
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
assert_equal [1, 2], rescues
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_rescue_bang
|
249
|
+
rescued = false
|
250
|
+
|
251
|
+
assert_raises RuntimeError do
|
252
|
+
return_here do
|
253
|
+
rescue! do
|
254
|
+
rescued = true
|
255
|
+
end
|
256
|
+
|
257
|
+
raise! 'hello'
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
assert rescued, 'rescue not executed'
|
262
|
+
end
|
263
|
+
|
264
|
+
def test_rescue_bang_default
|
265
|
+
rescued = false
|
266
|
+
|
267
|
+
assert_raises Exception do
|
268
|
+
return_here do
|
269
|
+
rescue! do
|
270
|
+
rescued = true
|
271
|
+
end
|
272
|
+
|
273
|
+
raise! Exception
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
refute rescued, 'rescue must default to StandardError'
|
278
|
+
end
|
279
|
+
|
280
|
+
def test_rescue_bang_exceptions
|
281
|
+
rescued = false
|
282
|
+
ensured = true
|
283
|
+
|
284
|
+
return_here do
|
285
|
+
rescue! do
|
286
|
+
rescued = true
|
287
|
+
end
|
288
|
+
|
289
|
+
ensure! do
|
290
|
+
ensured = true
|
291
|
+
end
|
292
|
+
|
293
|
+
return!
|
294
|
+
end
|
295
|
+
|
296
|
+
refute rescued, 'rescue! must not execute'
|
297
|
+
assert ensured, 'ensure! must execute'
|
298
|
+
end
|
299
|
+
|
300
|
+
def test_rescue_bang_multiple
|
301
|
+
rescued = false
|
302
|
+
|
303
|
+
assert_raises TypeError do
|
304
|
+
return_here do
|
305
|
+
rescue! ArgumentError, TypeError do
|
306
|
+
rescued = true
|
307
|
+
end
|
308
|
+
|
309
|
+
raise! TypeError
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
assert rescued, 'rescue not executed'
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_rescue_bang_type
|
317
|
+
rescued = false
|
318
|
+
|
319
|
+
assert_raises StandardError do
|
320
|
+
return_here do
|
321
|
+
rescue! StandardError do
|
322
|
+
rescued = true
|
323
|
+
end
|
324
|
+
|
325
|
+
rescue! RuntimeError do
|
326
|
+
flunk 'wrong rescue! executed'
|
327
|
+
end
|
328
|
+
|
329
|
+
raise! StandardError
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
assert rescued, 'StandardError exception not rescued'
|
16
334
|
end
|
17
335
|
|
18
336
|
def test_return_bang_no_return_here
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: return_bang
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 13
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: "1.
|
8
|
+
- 1
|
9
|
+
version: "1.1"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Eric Hodel
|
@@ -15,9 +15,9 @@ bindir: bin
|
|
15
15
|
cert_chain:
|
16
16
|
- |
|
17
17
|
-----BEGIN CERTIFICATE-----
|
18
|
-
|
18
|
+
MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
|
19
19
|
YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
|
20
|
-
|
20
|
+
ZXQwHhcNMTIwMjI4MTc1NDI1WhcNMTMwMjI3MTc1NDI1WjBBMRAwDgYDVQQDDAdk
|
21
21
|
cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
|
22
22
|
FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
|
23
23
|
LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
|
@@ -25,17 +25,18 @@ cert_chain:
|
|
25
25
|
Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
|
26
26
|
mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
|
27
27
|
g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
|
28
|
-
|
29
|
-
BBS5k4Z75VSpdM0AclG2UvzFA/
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
sCANiQ8BAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
29
|
+
BBS5k4Z75VSpdM0AclG2UvzFA/VW5DAfBgNVHREEGDAWgRRkcmJyYWluQHNlZ21l
|
30
|
+
bnQ3Lm5ldDAfBgNVHRIEGDAWgRRkcmJyYWluQHNlZ21lbnQ3Lm5ldDANBgkqhkiG
|
31
|
+
9w0BAQUFAAOCAQEAPeWzFnrcvC6eVzdlhmjUub2s6qieBkongKRDHQz5MEeQv4LS
|
32
|
+
SARnoHY+uCAVL/1xGAhmpzqQ3fJGWK9eBacW/e8E5GF9xQcV3mE1bA0WNaiDlX5j
|
33
|
+
U2aI+ZGSblqvHUCxKBHR1s7UMHsbz1saOmgdRTyPx0juJs68ocbUTeYBLWu9V4KP
|
34
|
+
zdGAG2JXO2gONg3b4tYDvpBLbry+KOX27iAJulUaH9TiTOULL4ITJVFsK0mYVqmR
|
35
|
+
Q8Tno9S3e4XGGP1ZWfLrTWEJbavFfhGHut2iMRwfC7s/YILAHNATopaJdH9DNpd1
|
36
|
+
U81zGHMUBOvz/VGT6wJwYJ3emS2nfA2NOHFfgA==
|
36
37
|
-----END CERTIFICATE-----
|
37
38
|
|
38
|
-
date:
|
39
|
+
date: 2012-04-01 00:00:00 Z
|
39
40
|
dependencies:
|
40
41
|
- !ruby/object:Gem::Dependency
|
41
42
|
name: minitest
|
@@ -45,32 +46,51 @@ dependencies:
|
|
45
46
|
requirements:
|
46
47
|
- - ~>
|
47
48
|
- !ruby/object:Gem::Version
|
48
|
-
hash:
|
49
|
+
hash: 21
|
49
50
|
segments:
|
50
51
|
- 2
|
51
|
-
-
|
52
|
-
version: "2.
|
52
|
+
- 11
|
53
|
+
version: "2.11"
|
53
54
|
type: :development
|
54
55
|
version_requirements: *id001
|
55
56
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
57
|
+
name: rdoc
|
57
58
|
prerelease: false
|
58
59
|
requirement: &id002 !ruby/object:Gem::Requirement
|
59
60
|
none: false
|
60
61
|
requirements:
|
61
62
|
- - ~>
|
62
63
|
- !ruby/object:Gem::Version
|
63
|
-
hash:
|
64
|
+
hash: 19
|
64
65
|
segments:
|
65
|
-
-
|
66
|
-
-
|
67
|
-
version: "
|
66
|
+
- 3
|
67
|
+
- 10
|
68
|
+
version: "3.10"
|
68
69
|
type: :development
|
69
70
|
version_requirements: *id002
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: hoe
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 35
|
80
|
+
segments:
|
81
|
+
- 2
|
82
|
+
- 16
|
83
|
+
version: "2.16"
|
84
|
+
type: :development
|
85
|
+
version_requirements: *id003
|
70
86
|
description: |-
|
71
|
-
return_bang implements non-local exits
|
72
|
-
|
73
|
-
|
87
|
+
return_bang implements non-local exits for methods. As a bonus, you also get
|
88
|
+
exception handling that ignores standard Ruby's inflexible begin; rescue;
|
89
|
+
ensure; end syntax.
|
90
|
+
|
91
|
+
Use return_bang to exit back to a processing loop from deeply nested code, or
|
92
|
+
just to confound your enemies *and* your friends! What could possibly go
|
93
|
+
wrong?
|
74
94
|
email:
|
75
95
|
|
76
96
|
executables: []
|
@@ -105,10 +125,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
105
125
|
requirements:
|
106
126
|
- - ">="
|
107
127
|
- !ruby/object:Gem::Version
|
108
|
-
hash:
|
128
|
+
hash: 55
|
109
129
|
segments:
|
110
|
-
-
|
111
|
-
|
130
|
+
- 1
|
131
|
+
- 9
|
132
|
+
- 2
|
133
|
+
version: 1.9.2
|
112
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
135
|
none: false
|
114
136
|
requirements:
|
@@ -121,9 +143,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
143
|
requirements: []
|
122
144
|
|
123
145
|
rubyforge_project: return_bang
|
124
|
-
rubygems_version: 1.8.
|
146
|
+
rubygems_version: 1.8.21
|
125
147
|
signing_key:
|
126
148
|
specification_version: 3
|
127
|
-
summary: return_bang implements non-local exits
|
149
|
+
summary: return_bang implements non-local exits for methods
|
128
150
|
test_files:
|
129
151
|
- test/test_return_bang.rb
|
metadata.gz.sig
CHANGED
@@ -1,2 +1,4 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
V���Ɗ`�`u�L�N�]n��ԙ�
|
2
|
+
1��j%>��
|
3
|
+
p�-ZyQ��
|
4
|
+
�*�o�辵�2�ϸg�_���Q��]�
|