rack 1.4.7 → 2.1.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +77 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +122 -456
- data/Rakefile +32 -31
- data/SPEC +119 -29
- data/bin/rackup +1 -0
- data/contrib/rack_logo.svg +164 -111
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +7 -5
- data/lib/rack/auth/abstract/request.rb +8 -6
- data/lib/rack/auth/basic.rb +5 -2
- data/lib/rack/auth/digest/md5.rb +10 -8
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +5 -4
- data/lib/rack/auth/digest/request.rb +4 -2
- data/lib/rack/body_proxy.rb +11 -9
- data/lib/rack/builder.rb +63 -20
- data/lib/rack/cascade.rb +10 -9
- data/lib/rack/chunked.rb +45 -11
- data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
- data/lib/rack/config.rb +7 -0
- data/lib/rack/content_length.rb +12 -6
- data/lib/rack/content_type.rb +4 -2
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +73 -42
- data/lib/rack/directory.rb +77 -56
- data/lib/rack/etag.rb +25 -13
- data/lib/rack/events.rb +156 -0
- data/lib/rack/file.rb +4 -143
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +18 -17
- data/lib/rack/handler/fastcgi.rb +21 -17
- data/lib/rack/handler/lsws.rb +14 -12
- data/lib/rack/handler/scgi.rb +27 -21
- data/lib/rack/handler/thin.rb +19 -5
- data/lib/rack/handler/webrick.rb +66 -24
- data/lib/rack/handler.rb +29 -19
- data/lib/rack/head.rb +21 -14
- data/lib/rack/lint.rb +259 -65
- data/lib/rack/lobster.rb +17 -10
- data/lib/rack/lock.rb +19 -10
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/method_override.rb +52 -0
- data/lib/rack/mime.rb +43 -6
- data/lib/rack/mock.rb +109 -44
- data/lib/rack/multipart/generator.rb +11 -12
- data/lib/rack/multipart/parser.rb +302 -115
- data/lib/rack/multipart/uploaded_file.rb +4 -3
- data/lib/rack/multipart.rb +40 -9
- data/lib/rack/null_logger.rb +39 -0
- data/lib/rack/query_parser.rb +218 -0
- data/lib/rack/recursive.rb +14 -11
- data/lib/rack/reloader.rb +12 -5
- data/lib/rack/request.rb +484 -270
- data/lib/rack/response.rb +196 -77
- data/lib/rack/rewindable_input.rb +5 -14
- data/lib/rack/runtime.rb +13 -6
- data/lib/rack/sendfile.rb +44 -20
- data/lib/rack/server.rb +175 -61
- data/lib/rack/session/abstract/id.rb +276 -133
- data/lib/rack/session/cookie.rb +75 -40
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +24 -18
- data/lib/rack/show_exceptions.rb +392 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
- data/lib/rack/static.rb +65 -38
- data/lib/rack/tempfile_reaper.rb +24 -0
- data/lib/rack/urlmap.rb +40 -15
- data/lib/rack/utils.rb +316 -285
- data/lib/rack.rb +78 -23
- data/rack.gemspec +26 -19
- metadata +44 -209
- data/KNOWN-ISSUES +0 -30
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -100
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/methodoverride.rb +0 -33
- data/lib/rack/nulllogger.rb +0 -18
- data/lib/rack/showexceptions.rb +0 -378
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/lighttpd.errors +0 -1
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth.rb +0 -57
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -69
- data/test/spec_builder.rb +0 -207
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -87
- data/test/spec_commonlogger.rb +0 -57
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -187
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -98
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -200
- data/test/spec_handler.rb +0 -59
- data/test/spec_head.rb +0 -48
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -167
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -72
- data/test/spec_mock.rb +0 -269
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -479
- data/test/spec_nulllogger.rb +0 -23
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -955
- data/test/spec_response.rb +0 -313
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -90
- data/test/spec_server.rb +0 -121
- data/test/spec_session_abstract_id.rb +0 -43
- data/test/spec_session_cookie.rb +0 -361
- data/test/spec_session_memcache.rb +0 -321
- data/test/spec_session_pool.rb +0 -209
- data/test/spec_showexceptions.rb +0 -92
- data/test/spec_showstatus.rb +0 -84
- data/test/spec_static.rb +0 -145
- data/test/spec_thin.rb +0 -86
- data/test/spec_urlmap.rb +0 -213
- data/test/spec_utils.rb +0 -554
- data/test/spec_webrick.rb +0 -143
- data/test/static/another/index.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/test/spec_file.rb
DELETED
@@ -1,200 +0,0 @@
|
|
1
|
-
require 'rack/file'
|
2
|
-
require 'rack/lint'
|
3
|
-
require 'rack/mock'
|
4
|
-
|
5
|
-
describe Rack::File do
|
6
|
-
DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT
|
7
|
-
|
8
|
-
def file(*args)
|
9
|
-
Rack::Lint.new Rack::File.new(*args)
|
10
|
-
end
|
11
|
-
|
12
|
-
should "serve files" do
|
13
|
-
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")
|
14
|
-
|
15
|
-
res.should.be.ok
|
16
|
-
res.should =~ /ruby/
|
17
|
-
end
|
18
|
-
|
19
|
-
should "set Last-Modified header" do
|
20
|
-
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")
|
21
|
-
|
22
|
-
path = File.join(DOCROOT, "/cgi/test")
|
23
|
-
|
24
|
-
res.should.be.ok
|
25
|
-
res["Last-Modified"].should.equal File.mtime(path).httpdate
|
26
|
-
end
|
27
|
-
|
28
|
-
should "return 304 if file isn't modified since last serve" do
|
29
|
-
path = File.join(DOCROOT, "/cgi/test")
|
30
|
-
res = Rack::MockRequest.new(file(DOCROOT)).
|
31
|
-
get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path).httpdate)
|
32
|
-
|
33
|
-
res.status.should.equal 304
|
34
|
-
res.body.should.be.empty
|
35
|
-
end
|
36
|
-
|
37
|
-
should "return the file if it's modified since last serve" do
|
38
|
-
path = File.join(DOCROOT, "/cgi/test")
|
39
|
-
res = Rack::MockRequest.new(file(DOCROOT)).
|
40
|
-
get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => (File.mtime(path) - 100).httpdate)
|
41
|
-
|
42
|
-
res.should.be.ok
|
43
|
-
end
|
44
|
-
|
45
|
-
should "serve files with URL encoded filenames" do
|
46
|
-
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%74%65%73%74") # "/cgi/test"
|
47
|
-
|
48
|
-
res.should.be.ok
|
49
|
-
res.should =~ /ruby/
|
50
|
-
end
|
51
|
-
|
52
|
-
should "allow safe directory traversal" do
|
53
|
-
req = Rack::MockRequest.new(file(DOCROOT))
|
54
|
-
|
55
|
-
res = req.get('/cgi/../cgi/test')
|
56
|
-
res.should.be.successful
|
57
|
-
|
58
|
-
res = req.get('.')
|
59
|
-
res.should.be.not_found
|
60
|
-
|
61
|
-
res = req.get("test/..")
|
62
|
-
res.should.be.not_found
|
63
|
-
end
|
64
|
-
|
65
|
-
should "not allow unsafe directory traversal" do
|
66
|
-
req = Rack::MockRequest.new(file(DOCROOT))
|
67
|
-
|
68
|
-
res = req.get("/../README")
|
69
|
-
res.should.be.client_error
|
70
|
-
|
71
|
-
res = req.get("../test")
|
72
|
-
res.should.be.client_error
|
73
|
-
|
74
|
-
res = req.get("..")
|
75
|
-
res.should.be.client_error
|
76
|
-
|
77
|
-
res.should.be.not_found
|
78
|
-
end
|
79
|
-
|
80
|
-
should "allow files with .. in their name" do
|
81
|
-
req = Rack::MockRequest.new(file(DOCROOT))
|
82
|
-
res = req.get("/cgi/..test")
|
83
|
-
res.should.be.not_found
|
84
|
-
|
85
|
-
res = req.get("/cgi/test..")
|
86
|
-
res.should.be.not_found
|
87
|
-
|
88
|
-
res = req.get("/cgi../test..")
|
89
|
-
res.should.be.not_found
|
90
|
-
end
|
91
|
-
|
92
|
-
should "not allow unsafe directory traversal with encoded periods" do
|
93
|
-
res = Rack::MockRequest.new(file(DOCROOT)).get("/%2E%2E/README")
|
94
|
-
|
95
|
-
res.should.be.client_error?
|
96
|
-
res.should.be.not_found
|
97
|
-
end
|
98
|
-
|
99
|
-
should "allow safe directory traversal with encoded periods" do
|
100
|
-
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%2E%2E/cgi/test")
|
101
|
-
|
102
|
-
res.should.be.successful
|
103
|
-
end
|
104
|
-
|
105
|
-
should "404 if it can't find the file" do
|
106
|
-
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/blubb")
|
107
|
-
|
108
|
-
res.should.be.not_found
|
109
|
-
end
|
110
|
-
|
111
|
-
should "detect SystemCallErrors" do
|
112
|
-
res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi")
|
113
|
-
|
114
|
-
res.should.be.not_found
|
115
|
-
end
|
116
|
-
|
117
|
-
should "return bodies that respond to #to_path" do
|
118
|
-
env = Rack::MockRequest.env_for("/cgi/test")
|
119
|
-
status, _, body = Rack::File.new(DOCROOT).call(env)
|
120
|
-
|
121
|
-
path = File.join(DOCROOT, "/cgi/test")
|
122
|
-
|
123
|
-
status.should.equal 200
|
124
|
-
body.should.respond_to :to_path
|
125
|
-
body.to_path.should.equal path
|
126
|
-
end
|
127
|
-
|
128
|
-
should "return correct byte range in body" do
|
129
|
-
env = Rack::MockRequest.env_for("/cgi/test")
|
130
|
-
env["HTTP_RANGE"] = "bytes=22-33"
|
131
|
-
res = Rack::MockResponse.new(*file(DOCROOT).call(env))
|
132
|
-
|
133
|
-
res.status.should.equal 206
|
134
|
-
res["Content-Length"].should.equal "12"
|
135
|
-
res["Content-Range"].should.equal "bytes 22-33/193"
|
136
|
-
res.body.should.equal "-*- ruby -*-"
|
137
|
-
end
|
138
|
-
|
139
|
-
should "return error for unsatisfiable byte range" do
|
140
|
-
env = Rack::MockRequest.env_for("/cgi/test")
|
141
|
-
env["HTTP_RANGE"] = "bytes=1234-5678"
|
142
|
-
res = Rack::MockResponse.new(*file(DOCROOT).call(env))
|
143
|
-
|
144
|
-
res.status.should.equal 416
|
145
|
-
res["Content-Range"].should.equal "bytes */193"
|
146
|
-
end
|
147
|
-
|
148
|
-
should "support legacy cache control options provided as string" do
|
149
|
-
env = Rack::MockRequest.env_for("/cgi/test")
|
150
|
-
status, heads, _ = file(DOCROOT, 'public, max-age=38').call(env)
|
151
|
-
|
152
|
-
status.should.equal 200
|
153
|
-
heads['Cache-Control'].should.equal 'public, max-age=38'
|
154
|
-
end
|
155
|
-
|
156
|
-
should "support custom http headers" do
|
157
|
-
env = Rack::MockRequest.env_for("/cgi/test")
|
158
|
-
status, heads, _ = file(DOCROOT, 'Cache-Control' => 'public, max-age=38',
|
159
|
-
'Access-Control-Allow-Origin' => '*').call(env)
|
160
|
-
|
161
|
-
status.should.equal 200
|
162
|
-
heads['Cache-Control'].should.equal 'public, max-age=38'
|
163
|
-
heads['Access-Control-Allow-Origin'].should.equal '*'
|
164
|
-
end
|
165
|
-
|
166
|
-
should "support not add custom http headers if none are supplied" do
|
167
|
-
env = Rack::MockRequest.env_for("/cgi/test")
|
168
|
-
status, heads, _ = file(DOCROOT).call(env)
|
169
|
-
|
170
|
-
status.should.equal 200
|
171
|
-
heads['Cache-Control'].should.equal nil
|
172
|
-
heads['Access-Control-Allow-Origin'].should.equal nil
|
173
|
-
end
|
174
|
-
|
175
|
-
should "only support GET and HEAD requests" do
|
176
|
-
req = Rack::MockRequest.new(file(DOCROOT))
|
177
|
-
|
178
|
-
forbidden = %w[post put patch delete]
|
179
|
-
forbidden.each do |method|
|
180
|
-
|
181
|
-
res = req.send(method, "/cgi/test")
|
182
|
-
res.should.be.client_error
|
183
|
-
res.should.be.method_not_allowed
|
184
|
-
end
|
185
|
-
|
186
|
-
allowed = %w[get head]
|
187
|
-
allowed.each do |method|
|
188
|
-
res = req.send(method, "/cgi/test")
|
189
|
-
res.should.be.successful
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
should "set Content-Length correctly for HEAD requests" do
|
194
|
-
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
|
195
|
-
res = req.head "/cgi/test"
|
196
|
-
res.should.be.successful
|
197
|
-
res['Content-Length'].should.equal "193"
|
198
|
-
end
|
199
|
-
|
200
|
-
end
|
data/test/spec_handler.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
require 'rack/handler'
|
2
|
-
|
3
|
-
class Rack::Handler::Lobster; end
|
4
|
-
class RockLobster; end
|
5
|
-
|
6
|
-
describe Rack::Handler do
|
7
|
-
it "has registered default handlers" do
|
8
|
-
Rack::Handler.get('cgi').should.equal Rack::Handler::CGI
|
9
|
-
Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick
|
10
|
-
|
11
|
-
begin
|
12
|
-
Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI
|
13
|
-
rescue LoadError
|
14
|
-
end
|
15
|
-
|
16
|
-
begin
|
17
|
-
Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel
|
18
|
-
rescue LoadError
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
should "raise LoadError if handler doesn't exist" do
|
23
|
-
lambda {
|
24
|
-
Rack::Handler.get('boom')
|
25
|
-
}.should.raise(LoadError)
|
26
|
-
end
|
27
|
-
|
28
|
-
should "get unregistered, but already required, handler by name" do
|
29
|
-
Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster
|
30
|
-
end
|
31
|
-
|
32
|
-
should "register custom handler" do
|
33
|
-
Rack::Handler.register('rock_lobster', 'RockLobster')
|
34
|
-
Rack::Handler.get('rock_lobster').should.equal RockLobster
|
35
|
-
end
|
36
|
-
|
37
|
-
should "not need registration for properly coded handlers even if not already required" do
|
38
|
-
begin
|
39
|
-
$LOAD_PATH.push File.expand_path('../unregistered_handler', __FILE__)
|
40
|
-
Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered
|
41
|
-
lambda {
|
42
|
-
Rack::Handler.get('UnRegistered')
|
43
|
-
}.should.raise LoadError
|
44
|
-
Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne
|
45
|
-
ensure
|
46
|
-
$LOAD_PATH.delete File.expand_path('../unregistered_handler', __FILE__)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
should "allow autoloaded handlers to be registered properly while being loaded" do
|
51
|
-
path = File.expand_path('../registering_handler', __FILE__)
|
52
|
-
begin
|
53
|
-
$LOAD_PATH.push path
|
54
|
-
Rack::Handler.get('registering_myself').should.equal Rack::Handler::RegisteringMyself
|
55
|
-
ensure
|
56
|
-
$LOAD_PATH.delete path
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/test/spec_head.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'enumerator'
|
2
|
-
require 'rack/head'
|
3
|
-
require 'rack/lint'
|
4
|
-
require 'rack/mock'
|
5
|
-
|
6
|
-
describe Rack::Head do
|
7
|
-
|
8
|
-
def test_response(headers = {})
|
9
|
-
body = StringIO.new "foo"
|
10
|
-
app = lambda do |env|
|
11
|
-
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, body]
|
12
|
-
end
|
13
|
-
request = Rack::MockRequest.env_for("/", headers)
|
14
|
-
response = Rack::Lint.new(Rack::Head.new(app)).call(request)
|
15
|
-
|
16
|
-
return response, body
|
17
|
-
end
|
18
|
-
|
19
|
-
def enum
|
20
|
-
defined?(Enumerator) ? Enumerator : Enumerable::Enumerator
|
21
|
-
end
|
22
|
-
|
23
|
-
should "pass GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
|
24
|
-
%w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
|
25
|
-
resp, _ = test_response("REQUEST_METHOD" => type)
|
26
|
-
|
27
|
-
resp[0].should.equal(200)
|
28
|
-
resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
|
29
|
-
enum.new(resp[2]).to_a.should.equal(["foo"])
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
should "remove body from HEAD requests" do
|
34
|
-
resp, _ = test_response("REQUEST_METHOD" => "HEAD")
|
35
|
-
|
36
|
-
resp[0].should.equal(200)
|
37
|
-
resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
|
38
|
-
enum.new(resp[2]).to_a.should.equal([])
|
39
|
-
end
|
40
|
-
|
41
|
-
should "close the body when it is removed" do
|
42
|
-
resp, body = test_response("REQUEST_METHOD" => "HEAD")
|
43
|
-
resp[0].should.equal(200)
|
44
|
-
resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
|
45
|
-
enum.new(resp[2]).to_a.should.equal([])
|
46
|
-
body.should.be.closed
|
47
|
-
end
|
48
|
-
end
|