roda 2.2.0 → 2.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG +34 -0
- data/Rakefile +22 -46
- data/doc/release_notes/2.3.0.txt +109 -0
- data/lib/roda/plugins/assets.rb +2 -1
- data/lib/roda/plugins/caching.rb +1 -1
- data/lib/roda/plugins/chunked.rb +1 -1
- data/lib/roda/plugins/error_email.rb +1 -1
- data/lib/roda/plugins/head.rb +6 -0
- data/lib/roda/plugins/heartbeat.rb +40 -0
- data/lib/roda/plugins/json.rb +23 -3
- data/lib/roda/plugins/json_parser.rb +72 -0
- data/lib/roda/plugins/mailer.rb +22 -5
- data/lib/roda/plugins/named_templates.rb +2 -2
- data/lib/roda/plugins/path_rewriter.rb +82 -0
- data/lib/roda/plugins/precompile_templates.rb +87 -0
- data/lib/roda/plugins/render.rb +111 -43
- data/lib/roda/plugins/render_each.rb +1 -1
- data/lib/roda/plugins/shared_vars.rb +1 -1
- data/lib/roda/plugins/view_options.rb +28 -3
- data/lib/roda/version.rb +1 -1
- data/spec/composition_spec.rb +3 -3
- data/spec/env_spec.rb +1 -1
- data/spec/freeze_spec.rb +6 -6
- data/spec/integration_spec.rb +16 -15
- data/spec/matchers_spec.rb +110 -110
- data/spec/opts_spec.rb +8 -8
- data/spec/plugin/_erubis_escaping_spec.rb +34 -3
- data/spec/plugin/all_verbs_spec.rb +8 -8
- data/spec/plugin/assets_spec.rb +164 -150
- data/spec/plugin/backtracking_array_spec.rb +18 -18
- data/spec/plugin/caching_spec.rb +70 -70
- data/spec/plugin/chunked_spec.rb +38 -38
- data/spec/plugin/class_level_routing_spec.rb +78 -78
- data/spec/plugin/content_for_spec.rb +2 -2
- data/spec/plugin/cookies_spec.rb +4 -4
- data/spec/plugin/csrf_spec.rb +8 -8
- data/spec/plugin/default_headers_spec.rb +6 -6
- data/spec/plugin/delay_build_spec.rb +7 -6
- data/spec/plugin/delegate_spec.rb +2 -2
- data/spec/plugin/delete_empty_headers_spec.rb +2 -2
- data/spec/plugin/drop_body_spec.rb +6 -6
- data/spec/plugin/empty_root_spec.rb +3 -3
- data/spec/plugin/environments_spec.rb +7 -7
- data/spec/plugin/error_email_spec.rb +23 -23
- data/spec/plugin/error_handler_spec.rb +14 -14
- data/spec/plugin/flash_spec.rb +30 -29
- data/spec/plugin/h_spec.rb +1 -1
- data/spec/plugin/halt_spec.rb +16 -16
- data/spec/plugin/hash_matcher_spec.rb +5 -5
- data/spec/plugin/head_spec.rb +10 -10
- data/spec/plugin/header_matchers_spec.rb +13 -13
- data/spec/plugin/heartbeat_spec.rb +74 -0
- data/spec/plugin/hooks_spec.rb +20 -20
- data/spec/plugin/indifferent_params_spec.rb +1 -1
- data/spec/plugin/json_parser_spec.rb +72 -0
- data/spec/plugin/json_spec.rb +22 -9
- data/spec/plugin/mailer_spec.rb +72 -58
- data/spec/plugin/match_affix_spec.rb +2 -2
- data/spec/plugin/middleware_spec.rb +7 -7
- data/spec/plugin/module_include_spec.rb +4 -4
- data/spec/plugin/multi_route_spec.rb +66 -66
- data/spec/plugin/multi_run_spec.rb +21 -21
- data/spec/plugin/named_templates_spec.rb +6 -6
- data/spec/plugin/not_allowed_spec.rb +17 -17
- data/spec/plugin/not_found_spec.rb +14 -14
- data/spec/plugin/padrino_render_spec.rb +2 -2
- data/spec/plugin/param_matchers_spec.rb +6 -6
- data/spec/plugin/partials_spec.rb +3 -3
- data/spec/plugin/pass_spec.rb +7 -7
- data/spec/plugin/path_matchers_spec.rb +6 -6
- data/spec/plugin/path_rewriter_spec.rb +37 -0
- data/spec/plugin/path_spec.rb +41 -40
- data/spec/plugin/per_thread_caching_spec.rb +6 -6
- data/spec/plugin/precompile_templates_spec.rb +74 -0
- data/spec/plugin/render_each_spec.rb +4 -4
- data/spec/plugin/render_spec.rb +179 -76
- data/spec/plugin/shared_vars_spec.rb +4 -4
- data/spec/plugin/sinatra_helpers_spec.rb +121 -121
- data/spec/plugin/slash_path_empty_spec.rb +10 -10
- data/spec/plugin/static_spec.rb +4 -4
- data/spec/plugin/streaming_spec.rb +11 -11
- data/spec/plugin/symbol_matchers_spec.rb +24 -24
- data/spec/plugin/symbol_views_spec.rb +3 -3
- data/spec/plugin/view_options_spec.rb +10 -10
- data/spec/plugin_spec.rb +2 -2
- data/spec/redirect_spec.rb +10 -10
- data/spec/request_spec.rb +8 -8
- data/spec/response_spec.rb +23 -23
- data/spec/session_spec.rb +4 -4
- data/spec/spec_helper.rb +5 -19
- data/spec/version_spec.rb +4 -4
- data/spec/views/iv.erb +1 -0
- metadata +16 -5
@@ -18,90 +18,90 @@ describe "sinatra_helpers plugin" do
|
|
18
18
|
|
19
19
|
it 'status returns the response status code if not given an argument' do
|
20
20
|
status_app(207){status}
|
21
|
-
body.
|
21
|
+
body.must_equal "207"
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'status sets the response status code if given an argument' do
|
25
25
|
status_app 207
|
26
|
-
status.
|
26
|
+
status.must_equal 207
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'not_found? is true only if status == 404' do
|
30
30
|
status_app(404){not_found?}
|
31
|
-
body.
|
31
|
+
body.must_equal 'true'
|
32
32
|
status_app(405){not_found?}
|
33
|
-
body.
|
33
|
+
body.must_equal 'false'
|
34
34
|
status_app(403){not_found?}
|
35
|
-
body.
|
35
|
+
body.must_equal 'false'
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'informational? is true only for 1xx status' do
|
39
39
|
status_app(100 + rand(100)){informational?}
|
40
|
-
body.
|
40
|
+
body.must_equal 'true'
|
41
41
|
status_app(200 + rand(400)){informational?}
|
42
|
-
body.
|
42
|
+
body.must_equal 'false'
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'success? is true only for 2xx status' do
|
46
46
|
status_app(200 + rand(100)){success?}
|
47
|
-
body.
|
47
|
+
body.must_equal 'true'
|
48
48
|
status_app(100 + rand(100)){success?}
|
49
|
-
body.
|
49
|
+
body.must_equal 'false'
|
50
50
|
status_app(300 + rand(300)){success?}
|
51
|
-
body.
|
51
|
+
body.must_equal 'false'
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'redirect? is true only for 3xx status' do
|
55
55
|
status_app(300 + rand(100)){redirect?}
|
56
|
-
body.
|
56
|
+
body.must_equal 'true'
|
57
57
|
status_app(200 + rand(100)){redirect?}
|
58
|
-
body.
|
58
|
+
body.must_equal 'false'
|
59
59
|
status_app(400 + rand(200)){redirect?}
|
60
|
-
body.
|
60
|
+
body.must_equal 'false'
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'client_error? is true only for 4xx status' do
|
64
64
|
status_app(400 + rand(100)){client_error?}
|
65
|
-
body.
|
65
|
+
body.must_equal 'true'
|
66
66
|
status_app(200 + rand(200)){client_error?}
|
67
|
-
body.
|
67
|
+
body.must_equal 'false'
|
68
68
|
status_app(500 + rand(100)){client_error?}
|
69
|
-
body.
|
69
|
+
body.must_equal 'false'
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'server_error? is true only for 5xx status' do
|
73
73
|
status_app(500 + rand(100)){server_error?}
|
74
|
-
body.
|
74
|
+
body.must_equal 'true'
|
75
75
|
status_app(200 + rand(300)){server_error?}
|
76
|
-
body.
|
76
|
+
body.must_equal 'false'
|
77
77
|
end
|
78
78
|
|
79
79
|
describe 'body' do
|
80
80
|
it 'takes a block for deferred body generation' do
|
81
81
|
sin_app{body{'Hello World'}; nil}
|
82
|
-
body.
|
83
|
-
header('Content-Length').
|
82
|
+
body.must_equal 'Hello World'
|
83
|
+
header('Content-Length').must_equal '11'
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'supports #join' do
|
87
87
|
sin_app{body{'Hello World'}; nil}
|
88
|
-
req[2].join.
|
88
|
+
req[2].join.must_equal 'Hello World'
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'takes a String, Array, or other object responding to #each' do
|
92
92
|
sin_app{body 'Hello World'; nil}
|
93
|
-
body.
|
94
|
-
header('Content-Length').
|
93
|
+
body.must_equal 'Hello World'
|
94
|
+
header('Content-Length').must_equal '11'
|
95
95
|
|
96
96
|
sin_app{body ['Hello ', 'World']; nil}
|
97
|
-
body.
|
98
|
-
header('Content-Length').
|
97
|
+
body.must_equal 'Hello World'
|
98
|
+
header('Content-Length').must_equal '11'
|
99
99
|
|
100
100
|
o = Object.new
|
101
101
|
def o.each; yield 'Hello World' end
|
102
102
|
sin_app{body o; nil}
|
103
|
-
body.
|
104
|
-
header('Content-Length').
|
103
|
+
body.must_equal 'Hello World'
|
104
|
+
header('Content-Length').must_equal '11'
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
@@ -112,53 +112,53 @@ describe "sinatra_helpers plugin" do
|
|
112
112
|
fail 'redirect should halt'
|
113
113
|
end
|
114
114
|
|
115
|
-
status.
|
116
|
-
body.
|
117
|
-
header('Location').
|
115
|
+
status.must_equal 302
|
116
|
+
body.must_equal ''
|
117
|
+
header('Location').must_equal '/foo'
|
118
118
|
end
|
119
119
|
|
120
120
|
it 'adds script_name if given a path' do
|
121
121
|
sin_app{redirect "/foo"}
|
122
|
-
header('Location', '/bar', 'SCRIPT_NAME'=>'/foo').
|
122
|
+
header('Location', '/bar', 'SCRIPT_NAME'=>'/foo').must_equal '/foo'
|
123
123
|
end
|
124
124
|
|
125
125
|
it 'does not adds script_name if not given a path' do
|
126
126
|
sin_app{redirect}
|
127
|
-
header('Location', '/bar', 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').
|
127
|
+
header('Location', '/bar', 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').must_equal '/foo/bar'
|
128
128
|
end
|
129
129
|
|
130
130
|
it 'respects :absolute_redirects option' do
|
131
131
|
sin_app{redirect}
|
132
132
|
app.opts[:absolute_redirects] = true
|
133
|
-
header('Location', '/bar', 'HTTP_HOST'=>'example.org', 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').
|
133
|
+
header('Location', '/bar', 'HTTP_HOST'=>'example.org', 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').must_equal 'http://example.org/foo/bar'
|
134
134
|
end
|
135
135
|
|
136
136
|
it 'respects :prefixed_redirects option' do
|
137
137
|
sin_app{redirect "/bar"}
|
138
138
|
app.opts[:prefixed_redirects] = true
|
139
|
-
header('Location', 'SCRIPT_NAME'=>'/foo').
|
139
|
+
header('Location', 'SCRIPT_NAME'=>'/foo').must_equal '/foo/bar'
|
140
140
|
end
|
141
141
|
|
142
142
|
it 'ignores :prefix_redirects option if not given a path' do
|
143
143
|
sin_app{redirect}
|
144
144
|
app.opts[:prefix_redirects] = true
|
145
|
-
header('Location', "/bar", 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').
|
145
|
+
header('Location', "/bar", 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').must_equal '/foo/bar'
|
146
146
|
end
|
147
147
|
|
148
148
|
it 'uses the code given when specified' do
|
149
149
|
sin_app{redirect '/foo', 301}
|
150
|
-
status.
|
150
|
+
status.must_equal 301
|
151
151
|
end
|
152
152
|
|
153
153
|
it 'redirects back to request.referer when passed back' do
|
154
154
|
sin_app{redirect back}
|
155
|
-
header('Location', 'HTTP_REFERER' => '/foo').
|
155
|
+
header('Location', 'HTTP_REFERER' => '/foo').must_equal '/foo'
|
156
156
|
end
|
157
157
|
|
158
158
|
it 'uses 303 for post requests if request is HTTP 1.1, 302 for 1.0' do
|
159
159
|
sin_app{redirect '/foo'}
|
160
|
-
status('HTTP_VERSION' => 'HTTP/1.1', 'REQUEST_METHOD'=>'POST').
|
161
|
-
status('HTTP_VERSION' => 'HTTP/1.0', 'REQUEST_METHOD'=>'POST').
|
160
|
+
status('HTTP_VERSION' => 'HTTP/1.1', 'REQUEST_METHOD'=>'POST').must_equal 303
|
161
|
+
status('HTTP_VERSION' => 'HTTP/1.0', 'REQUEST_METHOD'=>'POST').must_equal 302
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
@@ -169,26 +169,26 @@ describe "sinatra_helpers plugin" do
|
|
169
169
|
fail 'error should halt'
|
170
170
|
end
|
171
171
|
|
172
|
-
status.
|
173
|
-
body.
|
172
|
+
status.must_equal 500
|
173
|
+
body.must_equal ''
|
174
174
|
end
|
175
175
|
|
176
176
|
it 'accepts status code' do
|
177
177
|
sin_app{error 501}
|
178
|
-
status.
|
179
|
-
body.
|
178
|
+
status.must_equal 501
|
179
|
+
body.must_equal ''
|
180
180
|
end
|
181
181
|
|
182
182
|
it 'accepts body' do
|
183
183
|
sin_app{error '501'}
|
184
|
-
status.
|
185
|
-
body.
|
184
|
+
status.must_equal 500
|
185
|
+
body.must_equal '501'
|
186
186
|
end
|
187
187
|
|
188
188
|
it 'accepts status code and body' do
|
189
189
|
sin_app{error 502, '501'}
|
190
|
-
status.
|
191
|
-
body.
|
190
|
+
status.must_equal 502
|
191
|
+
body.must_equal '501'
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
@@ -199,14 +199,14 @@ describe "sinatra_helpers plugin" do
|
|
199
199
|
fail 'not_found should halt'
|
200
200
|
end
|
201
201
|
|
202
|
-
status.
|
203
|
-
body.
|
202
|
+
status.must_equal 404
|
203
|
+
body.must_equal ''
|
204
204
|
end
|
205
205
|
|
206
206
|
it 'accepts optional body' do
|
207
207
|
sin_app{not_found 'nf'}
|
208
|
-
status.
|
209
|
-
body.
|
208
|
+
status.must_equal 404
|
209
|
+
body.must_equal 'nf'
|
210
210
|
end
|
211
211
|
end
|
212
212
|
|
@@ -217,13 +217,13 @@ describe "sinatra_helpers plugin" do
|
|
217
217
|
'kthx'
|
218
218
|
end
|
219
219
|
|
220
|
-
header('X-Foo').
|
221
|
-
body.
|
220
|
+
header('X-Foo').must_equal 'bar'
|
221
|
+
body.must_equal 'kthx'
|
222
222
|
end
|
223
223
|
|
224
224
|
it 'returns the response headers hash when no hash provided' do
|
225
225
|
sin_app{headers['X-Foo'] = 'bar'}
|
226
|
-
header('X-Foo').
|
226
|
+
header('X-Foo').must_equal 'bar'
|
227
227
|
end
|
228
228
|
end
|
229
229
|
|
@@ -234,26 +234,26 @@ describe "sinatra_helpers plugin" do
|
|
234
234
|
|
235
235
|
it "looks up mime types in Rack's MIME registry" do
|
236
236
|
Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
|
237
|
-
body('foo').
|
238
|
-
body(:foo).
|
239
|
-
body('.foo').
|
237
|
+
body('foo').must_equal 'application/foo'
|
238
|
+
body(:foo).must_equal 'application/foo'
|
239
|
+
body('.foo').must_equal 'application/foo'
|
240
240
|
end
|
241
241
|
|
242
242
|
it 'returns nil when given nil' do
|
243
|
-
body('PATH_INFO'=>nil).
|
243
|
+
body('PATH_INFO'=>nil).must_equal ''
|
244
244
|
end
|
245
245
|
|
246
246
|
it 'returns nil when media type not registered' do
|
247
|
-
body('bizzle').
|
247
|
+
body('bizzle').must_equal ''
|
248
248
|
end
|
249
249
|
|
250
250
|
it 'returns the argument when given a media type string' do
|
251
|
-
body('text/plain').
|
251
|
+
body('text/plain').must_equal 'text/plain'
|
252
252
|
end
|
253
253
|
|
254
254
|
it 'supports mime types registered at the class level' do
|
255
255
|
app.mime_type :foo, 'application/foo'
|
256
|
-
body(:foo).
|
256
|
+
body(:foo).must_equal 'application/foo'
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
@@ -264,41 +264,41 @@ describe "sinatra_helpers plugin" do
|
|
264
264
|
'Hello World'
|
265
265
|
end
|
266
266
|
|
267
|
-
header('Content-Type').
|
268
|
-
body.
|
267
|
+
header('Content-Type').must_equal 'text/plain'
|
268
|
+
body.must_equal 'Hello World'
|
269
269
|
end
|
270
270
|
|
271
271
|
it 'takes media type parameters (like charset=)' do
|
272
272
|
sin_app{content_type 'text/html', :charset => 'latin1'}
|
273
|
-
header('Content-Type').
|
273
|
+
header('Content-Type').must_equal 'text/html;charset=latin1'
|
274
274
|
end
|
275
275
|
|
276
276
|
it "looks up symbols in Rack's mime types dictionary" do
|
277
277
|
sin_app{content_type :foo}
|
278
278
|
Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
|
279
|
-
header('Content-Type').
|
279
|
+
header('Content-Type').must_equal 'application/foo'
|
280
280
|
end
|
281
281
|
|
282
282
|
it 'fails when no mime type is registered for the argument provided' do
|
283
283
|
sin_app{content_type :bizzle}
|
284
|
-
proc{body}.
|
284
|
+
proc{body}.must_raise(Roda::RodaError)
|
285
285
|
end
|
286
286
|
|
287
287
|
it 'handles already present params' do
|
288
288
|
sin_app{content_type 'foo/bar;level=1', :charset => 'utf-8'}
|
289
|
-
header('Content-Type').
|
289
|
+
header('Content-Type').must_equal 'foo/bar;level=1, charset=utf-8'
|
290
290
|
end
|
291
291
|
|
292
292
|
it 'does not add charset if present' do
|
293
293
|
sin_app{content_type 'text/plain;charset=utf-16', :charset => 'utf-8'}
|
294
|
-
header('Content-Type').
|
294
|
+
header('Content-Type').must_equal 'text/plain;charset=utf-16'
|
295
295
|
end
|
296
296
|
|
297
297
|
it 'properly encodes parameters with delimiter characters' do
|
298
298
|
sin_app{|r| content_type 'image/png', :comment => r.path }
|
299
|
-
header('Content-Type', 'Hello, world!').
|
300
|
-
header('Content-Type', 'semi;colon').
|
301
|
-
header('Content-Type', '"Whatever."').
|
299
|
+
header('Content-Type', 'Hello, world!').must_equal 'image/png;comment="Hello, world!"'
|
300
|
+
header('Content-Type', 'semi;colon').must_equal 'image/png;comment="semi;colon"'
|
301
|
+
header('Content-Type', '"Whatever."').must_equal 'image/png;comment="\"Whatever.\""'
|
302
302
|
end
|
303
303
|
end
|
304
304
|
|
@@ -308,21 +308,21 @@ describe "sinatra_helpers plugin" do
|
|
308
308
|
end
|
309
309
|
|
310
310
|
it 'sets the Content-Disposition header' do
|
311
|
-
header('Content-Disposition', '/foo/test.xml').
|
312
|
-
body.
|
311
|
+
header('Content-Disposition', '/foo/test.xml').must_equal 'attachment; filename="test.xml"'
|
312
|
+
body.must_equal 'b'
|
313
313
|
end
|
314
314
|
|
315
315
|
it 'sets the Content-Disposition header even when a filename is not given' do
|
316
316
|
sin_app{attachment}
|
317
|
-
header('Content-Disposition', '/foo/test.xml').
|
317
|
+
header('Content-Disposition', '/foo/test.xml').must_equal 'attachment'
|
318
318
|
end
|
319
319
|
|
320
320
|
it 'sets the Content-Type header' do
|
321
|
-
header('Content-Type', 'test.xml').
|
321
|
+
header('Content-Type', 'test.xml').must_equal 'application/xml'
|
322
322
|
end
|
323
323
|
|
324
324
|
it 'does not modify the default Content-Type without a file extension' do
|
325
|
-
header('Content-Type', 'README').
|
325
|
+
header('Content-Type', 'README').must_equal 'text/html'
|
326
326
|
end
|
327
327
|
|
328
328
|
it 'should not modify the Content-Type if it is already set' do
|
@@ -331,7 +331,7 @@ describe "sinatra_helpers plugin" do
|
|
331
331
|
attachment 'test.xml'
|
332
332
|
end
|
333
333
|
|
334
|
-
header('Content-Type', 'README').
|
334
|
+
header('Content-Type', 'README').must_equal 'application/atom+xml'
|
335
335
|
end
|
336
336
|
end
|
337
337
|
|
@@ -343,67 +343,67 @@ describe "sinatra_helpers plugin" do
|
|
343
343
|
end
|
344
344
|
|
345
345
|
it "sends the contents of the file" do
|
346
|
-
status.
|
347
|
-
body.
|
346
|
+
status.must_equal 200
|
347
|
+
body.must_equal @content
|
348
348
|
end
|
349
349
|
|
350
350
|
it 'sets the Content-Type response header if a mime-type can be located' do
|
351
|
-
header('Content-Type').
|
351
|
+
header('Content-Type').must_equal 'text/css'
|
352
352
|
end
|
353
353
|
|
354
354
|
it 'sets the Content-Type response header if type option is set to a file extension' do
|
355
|
-
header('Content-Type', 'OPTS'=>{:type => 'html'}).
|
355
|
+
header('Content-Type', 'OPTS'=>{:type => 'html'}).must_equal 'text/html'
|
356
356
|
end
|
357
357
|
|
358
358
|
it 'sets the Content-Type response header if type option is set to a mime type' do
|
359
|
-
header('Content-Type', 'OPTS'=>{:type => 'application/octet-stream'}).
|
359
|
+
header('Content-Type', 'OPTS'=>{:type => 'application/octet-stream'}).must_equal 'application/octet-stream'
|
360
360
|
end
|
361
361
|
|
362
362
|
it 'sets the Content-Length response header' do
|
363
|
-
header('Content-Length').
|
363
|
+
header('Content-Length').must_equal @content.length.to_s
|
364
364
|
end
|
365
365
|
|
366
366
|
it 'sets the Last-Modified response header' do
|
367
|
-
header('Last-Modified').
|
367
|
+
header('Last-Modified').must_equal File.mtime(@file).httpdate
|
368
368
|
end
|
369
369
|
|
370
370
|
it 'allows passing in a different Last-Modified response header with :last_modified' do
|
371
371
|
time = Time.now
|
372
372
|
@app.plugin :caching
|
373
|
-
header('Last-Modified', 'OPTS'=>{:last_modified => time}).
|
373
|
+
header('Last-Modified', 'OPTS'=>{:last_modified => time}).must_equal time.httpdate
|
374
374
|
end
|
375
375
|
|
376
376
|
it "returns a 404 when not found" do
|
377
377
|
sin_app{send_file 'this-file-does-not-exist.txt'}
|
378
|
-
status.
|
378
|
+
status.must_equal 404
|
379
379
|
end
|
380
380
|
|
381
381
|
it "does not set the Content-Disposition header by default" do
|
382
|
-
header('Content-Disposition').
|
382
|
+
header('Content-Disposition').must_equal nil
|
383
383
|
end
|
384
384
|
|
385
385
|
it "sets the Content-Disposition header when :disposition set to 'attachment'" do
|
386
|
-
header('Content-Disposition', 'OPTS'=>{:disposition => 'attachment'}).
|
386
|
+
header('Content-Disposition', 'OPTS'=>{:disposition => 'attachment'}).must_equal 'attachment; filename="raw.css"'
|
387
387
|
end
|
388
388
|
|
389
389
|
it "does not set add a file name if filename is false" do
|
390
|
-
header('Content-Disposition', 'OPTS'=>{:disposition => 'inline', :filename=>false}).
|
390
|
+
header('Content-Disposition', 'OPTS'=>{:disposition => 'inline', :filename=>false}).must_equal 'inline'
|
391
391
|
end
|
392
392
|
|
393
393
|
it "sets the Content-Disposition header when :disposition set to 'inline'" do
|
394
|
-
header('Content-Disposition', 'OPTS'=>{:disposition => 'inline'}).
|
394
|
+
header('Content-Disposition', 'OPTS'=>{:disposition => 'inline'}).must_equal 'inline; filename="raw.css"'
|
395
395
|
end
|
396
396
|
|
397
397
|
it "sets the Content-Disposition header when :filename provided" do
|
398
|
-
header('Content-Disposition', 'OPTS'=>{:filename => 'foo.txt'}).
|
398
|
+
header('Content-Disposition', 'OPTS'=>{:filename => 'foo.txt'}).must_equal 'attachment; filename="foo.txt"'
|
399
399
|
end
|
400
400
|
|
401
401
|
it 'allows setting a custom status code' do
|
402
|
-
status('OPTS'=>{:status=>201}).
|
402
|
+
status('OPTS'=>{:status=>201}).must_equal 201
|
403
403
|
end
|
404
404
|
|
405
405
|
it "is able to send files with unknown mime type" do
|
406
|
-
header('Content-Type', 'OPTS'=>{:type => '.foobar'}).
|
406
|
+
header('Content-Type', 'OPTS'=>{:type => '.foobar'}).must_equal 'application/octet-stream'
|
407
407
|
end
|
408
408
|
|
409
409
|
it "does not override Content-Type if already set and no explicit type is given" do
|
@@ -412,7 +412,7 @@ describe "sinatra_helpers plugin" do
|
|
412
412
|
content_type :png
|
413
413
|
send_file file
|
414
414
|
end
|
415
|
-
header('Content-Type').
|
415
|
+
header('Content-Type').must_equal 'image/png'
|
416
416
|
end
|
417
417
|
|
418
418
|
it "does override Content-Type even if already set, if explicit type is given" do
|
@@ -421,7 +421,7 @@ describe "sinatra_helpers plugin" do
|
|
421
421
|
content_type :png
|
422
422
|
send_file file, :type => :gif
|
423
423
|
end
|
424
|
-
header('Content-Type').
|
424
|
+
header('Content-Type').must_equal 'image/gif'
|
425
425
|
end
|
426
426
|
end
|
427
427
|
|
@@ -432,78 +432,78 @@ describe "sinatra_helpers plugin" do
|
|
432
432
|
end
|
433
433
|
|
434
434
|
it 'generates absolute urls' do
|
435
|
-
body('HTTP_HOST'=>'example.org').
|
435
|
+
body('HTTP_HOST'=>'example.org').must_equal 'http://example.org/'
|
436
436
|
end
|
437
437
|
|
438
438
|
it 'includes path_info' do
|
439
|
-
body('/foo', 'HTTP_HOST'=>'example.org').
|
439
|
+
body('/foo', 'HTTP_HOST'=>'example.org').must_equal 'http://example.org/foo'
|
440
440
|
end
|
441
441
|
|
442
442
|
it 'includes script_name' do
|
443
|
-
body('/bar', 'HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/foo').
|
443
|
+
body('/bar', 'HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/foo').must_equal 'http://example.org/foo/bar'
|
444
444
|
end
|
445
445
|
|
446
446
|
it 'handles standard HTTP and HTTPS ports' do
|
447
|
-
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '80').
|
448
|
-
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '443', 'HTTPS'=>'on').
|
447
|
+
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '80').must_equal 'http://example.org/'
|
448
|
+
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '443', 'HTTPS'=>'on').must_equal 'https://example.org/'
|
449
449
|
end
|
450
450
|
|
451
451
|
it 'handles non-standard HTTP port' do
|
452
|
-
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '81').
|
453
|
-
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '443').
|
452
|
+
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '81').must_equal 'http://example.org:81/'
|
453
|
+
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '443').must_equal 'http://example.org:443/'
|
454
454
|
end
|
455
455
|
|
456
456
|
it 'handles non-standard HTTPS port' do
|
457
|
-
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '444', 'HTTPS'=>'on').
|
458
|
-
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '80', 'HTTPS'=>'on').
|
457
|
+
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '444', 'HTTPS'=>'on').must_equal 'https://example.org:444/'
|
458
|
+
body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '80', 'HTTPS'=>'on').must_equal 'https://example.org:80/'
|
459
459
|
end
|
460
460
|
|
461
461
|
it 'handles reverse proxy' do
|
462
|
-
body('SERVER_NAME'=>'example.org', 'HTTP_X_FORWARDED_HOST' => 'example.com', 'SERVER_PORT' => '8080').
|
462
|
+
body('SERVER_NAME'=>'example.org', 'HTTP_X_FORWARDED_HOST' => 'example.com', 'SERVER_PORT' => '8080').must_equal 'http://example.com/'
|
463
463
|
end
|
464
464
|
end
|
465
465
|
|
466
466
|
it 'allows passing an alternative to path_info' do
|
467
467
|
sin_app{uri '/bar'}
|
468
|
-
body('HTTP_HOST'=>'example.org').
|
469
|
-
body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/foo').
|
468
|
+
body('HTTP_HOST'=>'example.org').must_equal 'http://example.org/bar'
|
469
|
+
body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/foo').must_equal 'http://example.org/foo/bar'
|
470
470
|
end
|
471
471
|
|
472
472
|
it 'handles absolute URIs' do
|
473
473
|
sin_app{uri 'http://google.com'}
|
474
|
-
body('HTTP_HOST'=>'example.org').
|
474
|
+
body('HTTP_HOST'=>'example.org').must_equal 'http://google.com'
|
475
475
|
end
|
476
476
|
|
477
477
|
it 'handles different protocols' do
|
478
478
|
sin_app{uri 'mailto:jsmith@example.com'}
|
479
|
-
body('HTTP_HOST'=>'example.org').
|
479
|
+
body('HTTP_HOST'=>'example.org').must_equal 'mailto:jsmith@example.com'
|
480
480
|
end
|
481
481
|
|
482
482
|
it 'allows turning off host' do
|
483
483
|
sin_app{uri '/foo', false}
|
484
|
-
body('HTTP_HOST'=>'example.org').
|
485
|
-
body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/bar').
|
484
|
+
body('HTTP_HOST'=>'example.org').must_equal '/foo'
|
485
|
+
body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/bar').must_equal '/bar/foo'
|
486
486
|
end
|
487
487
|
|
488
488
|
it 'allows turning off script_name' do
|
489
489
|
sin_app{uri '/foo', true, false}
|
490
|
-
body('HTTP_HOST'=>'example.org').
|
491
|
-
body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/bar').
|
490
|
+
body('HTTP_HOST'=>'example.org').must_equal 'http://example.org/foo'
|
491
|
+
body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/bar').must_equal 'http://example.org/foo'
|
492
492
|
end
|
493
493
|
|
494
494
|
it 'is aliased to #url' do
|
495
495
|
sin_app{url}
|
496
|
-
body('HTTP_HOST'=>'example.org').
|
496
|
+
body('HTTP_HOST'=>'example.org').must_equal 'http://example.org/'
|
497
497
|
end
|
498
498
|
|
499
499
|
it 'is aliased to #to' do
|
500
500
|
sin_app{to}
|
501
|
-
body('HTTP_HOST'=>'example.org').
|
501
|
+
body('HTTP_HOST'=>'example.org').must_equal 'http://example.org/'
|
502
502
|
end
|
503
503
|
|
504
504
|
it 'accepts a URI object instead of a String' do
|
505
505
|
sin_app{uri URI.parse('http://roda.jeremyevans.net')}
|
506
|
-
body.
|
506
|
+
body.must_equal 'http://roda.jeremyevans.net'
|
507
507
|
end
|
508
508
|
end
|
509
509
|
|
@@ -517,8 +517,8 @@ describe "sinatra_helpers plugin" do
|
|
517
517
|
@a
|
518
518
|
end
|
519
519
|
|
520
|
-
status('rack.logger'=>o).
|
521
|
-
o.logs.
|
520
|
+
status('rack.logger'=>o).must_equal 404
|
521
|
+
o.logs.must_equal [[:info, 'foo']]
|
522
522
|
end
|
523
523
|
|
524
524
|
it 'supports disabling delegation if :delegate=>false option is provided' do
|
@@ -531,9 +531,9 @@ describe "sinatra_helpers plugin" do
|
|
531
531
|
end
|
532
532
|
end
|
533
533
|
|
534
|
-
proc{body}.
|
535
|
-
body('/req').
|
536
|
-
body('/res').
|
534
|
+
proc{body}.must_raise(NameError)
|
535
|
+
body('/req').must_equal 'false'
|
536
|
+
body('/res').must_equal 'nil'
|
537
537
|
end
|
538
538
|
end
|
539
539
|
|