return_bang 1.0
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 +2 -0
- data/.autotest +8 -0
- data/.gemtest +0 -0
- data/History.txt +5 -0
- data/Manifest.txt +8 -0
- data/README.rdoc +84 -0
- data/Rakefile +18 -0
- data/lib/return_bang.rb +148 -0
- data/lib/return_bang/everywhere.rb +7 -0
- data/test/test_return_bang.rb +83 -0
- metadata +129 -0
- metadata.gz.sig +2 -0
data.tar.gz.sig
ADDED
data/.autotest
ADDED
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
= return_bang
|
2
|
+
|
3
|
+
home :: https://github.com/drbrain/return_bang
|
4
|
+
rdoc :: http://docs.seattlerb.org/return_bang
|
5
|
+
bugs :: https://github.com/drbrain/return_bang/issues
|
6
|
+
|
7
|
+
== Description
|
8
|
+
|
9
|
+
return_bang implements non-local exits from methods. Use return_bang to exit
|
10
|
+
back to a processing loop from deeply nested code, or just to confound your
|
11
|
+
enemies *and* your friends! What could possibly go wrong?
|
12
|
+
|
13
|
+
== Features
|
14
|
+
|
15
|
+
* Implements non-local exits for methods
|
16
|
+
* Nestable
|
17
|
+
* Named and stack-based exit points, go exactly where you need to be
|
18
|
+
* Ignores pesky ensure blocks for when you really, really need to return
|
19
|
+
|
20
|
+
== Synopsis
|
21
|
+
|
22
|
+
require 'return_bang/everywhere'
|
23
|
+
|
24
|
+
def some_method
|
25
|
+
deeply_nested
|
26
|
+
# never reached
|
27
|
+
end
|
28
|
+
|
29
|
+
def deeply_nested
|
30
|
+
return!
|
31
|
+
end
|
32
|
+
|
33
|
+
return_here do
|
34
|
+
some_method
|
35
|
+
end
|
36
|
+
# resumes here
|
37
|
+
|
38
|
+
== Testimonials
|
39
|
+
|
40
|
+
"you'll wind up with your cock in /dev/null somehow" -- slyphon
|
41
|
+
|
42
|
+
"Haha! Right! This skips ensure… SO EVIL‼‼" -- drbrain
|
43
|
+
|
44
|
+
"This is so evil that 6 def test_… have turned into: 16 tests, 65 assertions,
|
45
|
+
18 failures, 7 errors" -- drbrain
|
46
|
+
|
47
|
+
== Install
|
48
|
+
|
49
|
+
sudo gem install return_bang
|
50
|
+
|
51
|
+
== Developers
|
52
|
+
|
53
|
+
After checking out the source, run:
|
54
|
+
|
55
|
+
$ rake newb
|
56
|
+
|
57
|
+
This task will install any missing dependencies, run the tests/specs,
|
58
|
+
and generate the RDoc.
|
59
|
+
|
60
|
+
== License
|
61
|
+
|
62
|
+
(The MIT License)
|
63
|
+
|
64
|
+
Copyright (c) Eric Hodel
|
65
|
+
|
66
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
67
|
+
a copy of this software and associated documentation files (the
|
68
|
+
'Software'), to deal in the Software without restriction, including
|
69
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
70
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
71
|
+
permit persons to whom the Software is furnished to do so, subject to
|
72
|
+
the following conditions:
|
73
|
+
|
74
|
+
The above copyright notice and this permission notice shall be
|
75
|
+
included in all copies or substantial portions of the Software.
|
76
|
+
|
77
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
78
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
79
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
80
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
81
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
82
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
83
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
84
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.plugin :minitest
|
7
|
+
Hoe.plugin :git
|
8
|
+
|
9
|
+
Hoe.spec 'return_bang' do
|
10
|
+
developer 'Eric Hodel', 'drbrain@segment7.net'
|
11
|
+
|
12
|
+
rdoc_locations << 'docs.seattlerb.org:/data/www/docs.seattlerb.org/return_bang/'
|
13
|
+
|
14
|
+
self.readme_file = 'README.rdoc'
|
15
|
+
self.extra_rdoc_files << 'README.rdoc'
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=ruby
|
data/lib/return_bang.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
begin
|
2
|
+
require 'continuation'
|
3
|
+
rescue LoadError
|
4
|
+
# in 1.8 it's built-in
|
5
|
+
end
|
6
|
+
|
7
|
+
##
|
8
|
+
# ReturnBang is allows you to perform non-local exits from your methods. One
|
9
|
+
# potential use of this is in a web framework so that a framework-provided
|
10
|
+
# utility methods can jump directly back to the request loop.
|
11
|
+
#
|
12
|
+
# return_here is used to designate where execution should be resumed. Return
|
13
|
+
# points may be arbitrarily nested. #return! resumes at the previous resume
|
14
|
+
# point, #return_to returns to a named return point.
|
15
|
+
#
|
16
|
+
# require 'return_bang' gives you a module you may include only in your
|
17
|
+
# application or library code. require 'return_bang/everywhere' includes
|
18
|
+
# ReturnBang in Object, so it is only recommended for application code use.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
#
|
22
|
+
# include ReturnBang
|
23
|
+
#
|
24
|
+
# def framework_loop
|
25
|
+
# loop do
|
26
|
+
# # setup code
|
27
|
+
#
|
28
|
+
# return_here do
|
29
|
+
# user_code
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # resume execution here
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def render_error_and_return message
|
37
|
+
# # generate error
|
38
|
+
#
|
39
|
+
# return!
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# def user_code
|
43
|
+
# user_utility_method
|
44
|
+
# # these lines never reached
|
45
|
+
# # ...
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def user_utility_method
|
49
|
+
# render_error_and_return "blah" if some_condition
|
50
|
+
# # these lines never reached
|
51
|
+
# # ...
|
52
|
+
# end
|
53
|
+
|
54
|
+
module ReturnBang
|
55
|
+
|
56
|
+
VERSION = '1.0'
|
57
|
+
|
58
|
+
##
|
59
|
+
# Raised when attempting to return! when you haven't registered a location
|
60
|
+
# to return to, or are trying to return to a named point that wasn't
|
61
|
+
# registered.
|
62
|
+
|
63
|
+
class NonLocalJumpError < StandardError
|
64
|
+
end
|
65
|
+
|
66
|
+
def _return_bang_names # :nodoc:
|
67
|
+
Thread.current[:return_bang_names] ||= {}
|
68
|
+
end
|
69
|
+
|
70
|
+
if {}.respond_to? :key then # 1.9
|
71
|
+
def _return_bang_pop # :nodoc:
|
72
|
+
return_point = _return_bang_stack.pop
|
73
|
+
|
74
|
+
_return_bang_names.delete _return_bang_names.key _return_bang_stack.length
|
75
|
+
|
76
|
+
return_point
|
77
|
+
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
|
+
|
83
|
+
_return_bang_names.delete _return_bang_names.index value
|
84
|
+
|
85
|
+
return_point
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def _return_bang_stack # :nodoc:
|
90
|
+
Thread.current[:return_bang_stack] ||= []
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Returns to the last return point in the stack. If no return points have
|
95
|
+
# been registered a NonLocalJumpError is raised. +value+ is returned at the
|
96
|
+
# registered return point.
|
97
|
+
|
98
|
+
def return! value = nil
|
99
|
+
raise NonLocalJumpError, 'nowhere to return to' if
|
100
|
+
_return_bang_stack.empty?
|
101
|
+
|
102
|
+
_return_bang_pop.call value
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Registers a return point to jump back to. If a +name+ is given return_to
|
107
|
+
# can jump here.
|
108
|
+
|
109
|
+
def return_here name = nil
|
110
|
+
raise ArgumentError, "#{name} is already registered as a return point" if
|
111
|
+
_return_bang_names.include? name
|
112
|
+
|
113
|
+
value = callcc do |cc|
|
114
|
+
_return_bang_names[name] = _return_bang_stack.length if name
|
115
|
+
_return_bang_stack.push cc
|
116
|
+
|
117
|
+
begin
|
118
|
+
yield
|
119
|
+
ensure
|
120
|
+
_return_bang_pop
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# here is where the magic happens
|
125
|
+
unwind_to = Thread.current[:unwind_to]
|
126
|
+
|
127
|
+
return! value if unwind_to and _return_bang_stack.length > unwind_to
|
128
|
+
|
129
|
+
return value
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Returns to the return point +name+. +value+ is returned at the registered
|
134
|
+
# return point.
|
135
|
+
|
136
|
+
def return_to name, value = nil
|
137
|
+
unwind_to = _return_bang_names.delete name
|
138
|
+
|
139
|
+
raise NonLocalJumpError, "return point :nonexistent was not set" unless
|
140
|
+
unwind_to
|
141
|
+
|
142
|
+
Thread.current[:unwind_to] = unwind_to
|
143
|
+
|
144
|
+
return! value
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'return_bang'
|
3
|
+
|
4
|
+
class TestReturnBang < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
include ReturnBang
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@after_a = false
|
10
|
+
@after_e = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
assert_empty _return_bang_stack
|
15
|
+
assert_empty _return_bang_names
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_return_bang_no_return_here
|
19
|
+
e = assert_raises NonLocalJumpError do
|
20
|
+
return!
|
21
|
+
end
|
22
|
+
|
23
|
+
assert_equal 'nowhere to return to', e.message
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_return_here
|
27
|
+
result = return_here do a end
|
28
|
+
|
29
|
+
refute @after_a, 'return! did not skip after_a'
|
30
|
+
|
31
|
+
assert_equal 42, result
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_return_here_name
|
35
|
+
result = return_here :name do d end
|
36
|
+
|
37
|
+
refute @after_e, 'return_to did not skip after_e'
|
38
|
+
|
39
|
+
assert_equal 43, result
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_return_here_name_no_return_bang
|
43
|
+
result = return_here :name do c end
|
44
|
+
|
45
|
+
assert_equal 24, result
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_return_here_nest
|
49
|
+
result = return_here do
|
50
|
+
return_here do
|
51
|
+
a
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
refute @after_a, 'return! did not skip after_a'
|
56
|
+
|
57
|
+
assert_equal 42, result
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_return_here_no_return_bang
|
61
|
+
result = return_here do c end
|
62
|
+
|
63
|
+
assert_equal 24, result
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_return_to_no_return_here
|
67
|
+
e = assert_raises NonLocalJumpError do
|
68
|
+
return_to :nonexistent
|
69
|
+
end
|
70
|
+
|
71
|
+
assert_equal 'return point :nonexistent was not set', e.message
|
72
|
+
end
|
73
|
+
|
74
|
+
def a() b; @after_a = true end
|
75
|
+
def b() return! 42 end
|
76
|
+
|
77
|
+
def c() 24 end
|
78
|
+
|
79
|
+
def d() return_here do e end; @after_e = true end
|
80
|
+
def e() return_to :name, 43 end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: return_bang
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: "1.0"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Eric Hodel
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain:
|
16
|
+
- |
|
17
|
+
-----BEGIN CERTIFICATE-----
|
18
|
+
MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
|
19
|
+
YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
|
20
|
+
ZXQwHhcNMDcxMjIxMDIwNDE0WhcNMDgxMjIwMDIwNDE0WjBBMRAwDgYDVQQDDAdk
|
21
|
+
cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
|
22
|
+
FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
|
23
|
+
LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
|
24
|
+
U5ddZCVywn5nnAQ+Ui7jMW54CYt5/H6f2US6U0hQOjJR6cpfiymgxGdfyTiVcvTm
|
25
|
+
Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
|
26
|
+
mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
|
27
|
+
g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
|
28
|
+
sCANiQ8BAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
29
|
+
BBS5k4Z75VSpdM0AclG2UvzFA/VW5DANBgkqhkiG9w0BAQUFAAOCAQEAHagT4lfX
|
30
|
+
kP/hDaiwGct7XPuVGbrOsKRVD59FF5kETBxEc9UQ1clKWngf8JoVuEoKD774dW19
|
31
|
+
bU0GOVWO+J6FMmT/Cp7nuFJ79egMf/gy4gfUfQMuvfcr6DvZUPIs9P/TlK59iMYF
|
32
|
+
DIOQ3DxdF3rMzztNUCizN4taVscEsjCcgW6WkUJnGdqlu3OHWpQxZBJkBTjPCoc6
|
33
|
+
UW6on70SFPmAy/5Cq0OJNGEWBfgD9q7rrs/X8GGwUWqXb85RXnUVi/P8Up75E0ag
|
34
|
+
14jEc90kN+C7oI/AGCBN0j6JnEtYIEJZibjjDJTSMWlUKKkj30kq7hlUC2CepJ4v
|
35
|
+
x52qPcexcYZR7w==
|
36
|
+
-----END CERTIFICATE-----
|
37
|
+
|
38
|
+
date: 2011-12-21 00:00:00 Z
|
39
|
+
dependencies:
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: minitest
|
42
|
+
prerelease: false
|
43
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 15
|
49
|
+
segments:
|
50
|
+
- 2
|
51
|
+
- 6
|
52
|
+
version: "2.6"
|
53
|
+
type: :development
|
54
|
+
version_requirements: *id001
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: hoe
|
57
|
+
prerelease: false
|
58
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ~>
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 27
|
64
|
+
segments:
|
65
|
+
- 2
|
66
|
+
- 12
|
67
|
+
version: "2.12"
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id002
|
70
|
+
description: |-
|
71
|
+
return_bang implements non-local exits from methods. Use return_bang to exit
|
72
|
+
back to a processing loop from deeply nested code, or just to confound your
|
73
|
+
enemies *and* your friends! What could possibly go wrong?
|
74
|
+
email:
|
75
|
+
- drbrain@segment7.net
|
76
|
+
executables: []
|
77
|
+
|
78
|
+
extensions: []
|
79
|
+
|
80
|
+
extra_rdoc_files:
|
81
|
+
- History.txt
|
82
|
+
- Manifest.txt
|
83
|
+
- README.rdoc
|
84
|
+
files:
|
85
|
+
- .autotest
|
86
|
+
- History.txt
|
87
|
+
- Manifest.txt
|
88
|
+
- README.rdoc
|
89
|
+
- Rakefile
|
90
|
+
- lib/return_bang.rb
|
91
|
+
- lib/return_bang/everywhere.rb
|
92
|
+
- test/test_return_bang.rb
|
93
|
+
- .gemtest
|
94
|
+
homepage: https://github.com/drbrain/return_bang
|
95
|
+
licenses: []
|
96
|
+
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options:
|
99
|
+
- --main
|
100
|
+
- README.rdoc
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
requirements: []
|
122
|
+
|
123
|
+
rubyforge_project: return_bang
|
124
|
+
rubygems_version: 1.8.12
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: return_bang implements non-local exits from methods
|
128
|
+
test_files:
|
129
|
+
- test/test_return_bang.rb
|
metadata.gz.sig
ADDED