darkhelmet-sinatra 0.9.1.1 → 0.10.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/AUTHORS +2 -0
- data/CHANGES +180 -0
- data/LICENSE +1 -1
- data/README.jp.rdoc +552 -0
- data/README.rdoc +177 -38
- data/Rakefile +18 -25
- data/lib/sinatra.rb +1 -2
- data/lib/sinatra/base.rb +405 -305
- data/lib/sinatra/main.rb +5 -24
- data/lib/sinatra/showexceptions.rb +303 -0
- data/lib/sinatra/tilt.rb +509 -0
- data/sinatra.gemspec +21 -51
- data/test/base_test.rb +123 -93
- data/test/builder_test.rb +2 -1
- data/test/contest.rb +64 -0
- data/test/erb_test.rb +1 -1
- data/test/erubis_test.rb +82 -0
- data/test/extensions_test.rb +24 -8
- data/test/filter_test.rb +99 -3
- data/test/haml_test.rb +25 -3
- data/test/helper.rb +43 -48
- data/test/helpers_test.rb +500 -424
- data/test/mapped_error_test.rb +163 -137
- data/test/middleware_test.rb +3 -3
- data/test/request_test.rb +16 -1
- data/test/response_test.rb +2 -2
- data/test/result_test.rb +1 -1
- data/test/route_added_hook_test.rb +59 -0
- data/test/routing_test.rb +170 -22
- data/test/sass_test.rb +44 -1
- data/test/server_test.rb +19 -13
- data/test/sinatra_test.rb +1 -1
- data/test/static_test.rb +9 -2
- data/test/templates_test.rb +78 -11
- data/test/views/error.builder +3 -0
- data/test/views/error.erb +3 -0
- data/test/views/error.erubis +3 -0
- data/test/views/error.haml +3 -0
- data/test/views/error.sass +2 -0
- data/test/views/foo/hello.test +1 -0
- data/test/views/hello.erubis +1 -0
- data/test/views/layout2.erubis +2 -0
- metadata +37 -55
- data/compat/app_test.rb +0 -282
- data/compat/application_test.rb +0 -262
- data/compat/builder_test.rb +0 -101
- data/compat/compat_test.rb +0 -12
- data/compat/custom_error_test.rb +0 -62
- data/compat/erb_test.rb +0 -136
- data/compat/events_test.rb +0 -78
- data/compat/filter_test.rb +0 -30
- data/compat/haml_test.rb +0 -233
- data/compat/helper.rb +0 -30
- data/compat/mapped_error_test.rb +0 -72
- data/compat/pipeline_test.rb +0 -45
- data/compat/public/foo.xml +0 -1
- data/compat/sass_test.rb +0 -57
- data/compat/sessions_test.rb +0 -42
- data/compat/streaming_test.rb +0 -133
- data/compat/sym_params_test.rb +0 -19
- data/compat/template_test.rb +0 -30
- data/compat/use_in_file_templates_test.rb +0 -47
- data/compat/views/foo.builder +0 -1
- data/compat/views/foo.erb +0 -1
- data/compat/views/foo.haml +0 -1
- data/compat/views/foo.sass +0 -2
- data/compat/views/foo_layout.erb +0 -2
- data/compat/views/foo_layout.haml +0 -2
- data/compat/views/layout_test/foo.builder +0 -1
- data/compat/views/layout_test/foo.erb +0 -1
- data/compat/views/layout_test/foo.haml +0 -1
- data/compat/views/layout_test/foo.sass +0 -2
- data/compat/views/layout_test/layout.builder +0 -3
- data/compat/views/layout_test/layout.erb +0 -1
- data/compat/views/layout_test/layout.haml +0 -1
- data/compat/views/layout_test/layout.sass +0 -2
- data/compat/views/no_layout/no_layout.builder +0 -1
- data/compat/views/no_layout/no_layout.haml +0 -1
- data/lib/sinatra/compat.rb +0 -250
- data/lib/sinatra/test.rb +0 -126
- data/lib/sinatra/test/bacon.rb +0 -19
- data/lib/sinatra/test/rspec.rb +0 -13
- data/lib/sinatra/test/spec.rb +0 -11
- data/lib/sinatra/test/unit.rb +0 -13
- data/test/data/reload_app_file.rb +0 -3
- data/test/options_test.rb +0 -374
- data/test/reload_test.rb +0 -68
- data/test/test_test.rb +0 -144
data/test/helpers_test.rb
CHANGED
@@ -1,497 +1,573 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
get '/' do
|
7
|
-
status 207
|
8
|
-
nil
|
9
|
-
end
|
10
|
-
}
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'sets the response status code' do
|
14
|
-
get '/'
|
15
|
-
assert_equal 207, response.status
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe 'Helpers#body' do
|
20
|
-
it 'takes a block for defered body generation' do
|
21
|
-
mock_app {
|
22
|
-
get '/' do
|
23
|
-
body { 'Hello World' }
|
24
|
-
end
|
25
|
-
}
|
26
|
-
|
27
|
-
get '/'
|
28
|
-
assert_equal 'Hello World', body
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'takes a String, Array, or other object responding to #each' do
|
32
|
-
mock_app {
|
33
|
-
get '/' do
|
34
|
-
body 'Hello World'
|
35
|
-
end
|
36
|
-
}
|
37
|
-
|
38
|
-
get '/'
|
39
|
-
assert_equal 'Hello World', body
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
describe 'Helpers#redirect' do
|
44
|
-
it 'uses a 302 when only a path is given' do
|
45
|
-
mock_app {
|
46
|
-
get '/' do
|
47
|
-
redirect '/foo'
|
48
|
-
fail 'redirect should halt'
|
49
|
-
end
|
50
|
-
}
|
51
|
-
|
52
|
-
get '/'
|
53
|
-
assert_equal 302, status
|
54
|
-
assert_equal '', body
|
55
|
-
assert_equal '/foo', response['Location']
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'uses the code given when specified' do
|
59
|
-
mock_app {
|
60
|
-
get '/' do
|
61
|
-
redirect '/foo', 301
|
62
|
-
fail 'redirect should halt'
|
63
|
-
end
|
64
|
-
}
|
65
|
-
|
66
|
-
get '/'
|
67
|
-
assert_equal 301, status
|
68
|
-
assert_equal '', body
|
69
|
-
assert_equal '/foo', response['Location']
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'redirects back to request.referer when passed back' do
|
73
|
-
mock_app {
|
74
|
-
get '/try_redirect' do
|
75
|
-
redirect back
|
76
|
-
end
|
77
|
-
}
|
78
|
-
|
79
|
-
request = Rack::MockRequest.new(@app)
|
80
|
-
response = request.get('/try_redirect', 'HTTP_REFERER' => '/foo')
|
81
|
-
assert_equal 302, response.status
|
82
|
-
assert_equal '/foo', response['Location']
|
83
|
-
end
|
84
|
-
|
85
|
-
end
|
86
|
-
|
87
|
-
describe 'Helpers#error' do
|
88
|
-
it 'sets a status code and halts' do
|
89
|
-
mock_app {
|
90
|
-
get '/' do
|
91
|
-
error 501
|
92
|
-
fail 'error should halt'
|
93
|
-
end
|
94
|
-
}
|
95
|
-
|
96
|
-
get '/'
|
97
|
-
assert_equal 501, status
|
98
|
-
assert_equal '', body
|
3
|
+
class HelpersTest < Test::Unit::TestCase
|
4
|
+
def test_default
|
5
|
+
assert true
|
99
6
|
end
|
100
7
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
assert_equal 501, status
|
111
|
-
assert_equal 'FAIL', body
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'uses a 500 status code when first argument is a body' do
|
115
|
-
mock_app {
|
116
|
-
get '/' do
|
117
|
-
error 'FAIL'
|
118
|
-
fail 'error should halt'
|
119
|
-
end
|
120
|
-
}
|
121
|
-
|
122
|
-
get '/'
|
123
|
-
assert_equal 500, status
|
124
|
-
assert_equal 'FAIL', body
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
describe 'Helpers#not_found' do
|
129
|
-
it 'halts with a 404 status' do
|
130
|
-
mock_app {
|
131
|
-
get '/' do
|
132
|
-
not_found
|
133
|
-
fail 'not_found should halt'
|
134
|
-
end
|
135
|
-
}
|
136
|
-
|
137
|
-
get '/'
|
138
|
-
assert_equal 404, status
|
139
|
-
assert_equal '', body
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
describe 'Helpers#headers' do
|
144
|
-
it 'sets headers on the response object when given a Hash' do
|
145
|
-
mock_app {
|
146
|
-
get '/' do
|
147
|
-
headers 'X-Foo' => 'bar', 'X-Baz' => 'bling'
|
148
|
-
'kthx'
|
149
|
-
end
|
150
|
-
}
|
8
|
+
describe 'status' do
|
9
|
+
setup do
|
10
|
+
mock_app {
|
11
|
+
get '/' do
|
12
|
+
status 207
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
151
17
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
assert_equal 'kthx', body
|
18
|
+
it 'sets the response status code' do
|
19
|
+
get '/'
|
20
|
+
assert_equal 207, response.status
|
21
|
+
end
|
157
22
|
end
|
158
23
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
24
|
+
describe 'body' do
|
25
|
+
it 'takes a block for defered body generation' do
|
26
|
+
mock_app {
|
27
|
+
get '/' do
|
28
|
+
body { 'Hello World' }
|
29
|
+
end
|
30
|
+
}
|
166
31
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
171
|
-
end
|
32
|
+
get '/'
|
33
|
+
assert_equal 'Hello World', body
|
34
|
+
end
|
172
35
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
}
|
36
|
+
it 'takes a String, Array, or other object responding to #each' do
|
37
|
+
mock_app {
|
38
|
+
get '/' do
|
39
|
+
body 'Hello World'
|
40
|
+
end
|
41
|
+
}
|
180
42
|
|
181
|
-
|
182
|
-
|
43
|
+
get '/'
|
44
|
+
assert_equal 'Hello World', body
|
45
|
+
end
|
183
46
|
end
|
184
47
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
48
|
+
describe 'redirect' do
|
49
|
+
it 'uses a 302 when only a path is given' do
|
50
|
+
mock_app {
|
51
|
+
get '/' do
|
52
|
+
redirect '/foo'
|
53
|
+
fail 'redirect should halt'
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
get '/'
|
58
|
+
assert_equal 302, status
|
59
|
+
assert_equal '', body
|
60
|
+
assert_equal '/foo', response['Location']
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'uses the code given when specified' do
|
64
|
+
mock_app {
|
65
|
+
get '/' do
|
66
|
+
redirect '/foo', 301
|
67
|
+
fail 'redirect should halt'
|
68
|
+
end
|
69
|
+
}
|
70
|
+
|
71
|
+
get '/'
|
72
|
+
assert_equal 301, status
|
73
|
+
assert_equal '', body
|
74
|
+
assert_equal '/foo', response['Location']
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'redirects back to request.referer when passed back' do
|
78
|
+
mock_app {
|
79
|
+
get '/try_redirect' do
|
80
|
+
redirect back
|
81
|
+
end
|
82
|
+
}
|
193
83
|
|
194
|
-
|
195
|
-
|
84
|
+
request = Rack::MockRequest.new(@app)
|
85
|
+
response = request.get('/try_redirect', 'HTTP_REFERER' => '/foo')
|
86
|
+
assert_equal 302, response.status
|
87
|
+
assert_equal '/foo', response['Location']
|
88
|
+
end
|
196
89
|
end
|
197
|
-
end
|
198
90
|
|
199
|
-
describe '
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
91
|
+
describe 'error' do
|
92
|
+
it 'sets a status code and halts' do
|
93
|
+
mock_app {
|
94
|
+
get '/' do
|
95
|
+
error 501
|
96
|
+
fail 'error should halt'
|
97
|
+
end
|
98
|
+
}
|
99
|
+
|
100
|
+
get '/'
|
101
|
+
assert_equal 501, status
|
102
|
+
assert_equal '', body
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'takes an optional body' do
|
106
|
+
mock_app {
|
107
|
+
get '/' do
|
108
|
+
error 501, 'FAIL'
|
109
|
+
fail 'error should halt'
|
110
|
+
end
|
111
|
+
}
|
112
|
+
|
113
|
+
get '/'
|
114
|
+
assert_equal 501, status
|
115
|
+
assert_equal 'FAIL', body
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'uses a 500 status code when first argument is a body' do
|
119
|
+
mock_app {
|
120
|
+
get '/' do
|
121
|
+
error 'FAIL'
|
122
|
+
fail 'error should halt'
|
123
|
+
end
|
124
|
+
}
|
208
125
|
|
209
|
-
|
210
|
-
|
126
|
+
get '/'
|
127
|
+
assert_equal 500, status
|
128
|
+
assert_equal 'FAIL', body
|
129
|
+
end
|
211
130
|
end
|
212
131
|
|
213
|
-
|
214
|
-
|
215
|
-
|
132
|
+
describe 'not_found' do
|
133
|
+
it 'halts with a 404 status' do
|
134
|
+
mock_app {
|
135
|
+
get '/' do
|
136
|
+
not_found
|
137
|
+
fail 'not_found should halt'
|
138
|
+
end
|
139
|
+
}
|
140
|
+
|
141
|
+
get '/'
|
142
|
+
assert_equal 404, status
|
143
|
+
assert_equal '', body
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'does not set a X-Cascade header' do
|
147
|
+
mock_app {
|
148
|
+
get '/' do
|
149
|
+
not_found
|
150
|
+
fail 'not_found should halt'
|
151
|
+
end
|
152
|
+
}
|
216
153
|
|
217
|
-
|
218
|
-
|
154
|
+
get '/'
|
155
|
+
assert_equal 404, status
|
156
|
+
assert_equal nil, response.headers['X-Cascade']
|
157
|
+
end
|
219
158
|
end
|
220
|
-
end
|
221
159
|
|
222
|
-
describe '
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
160
|
+
describe 'headers' do
|
161
|
+
it 'sets headers on the response object when given a Hash' do
|
162
|
+
mock_app {
|
163
|
+
get '/' do
|
164
|
+
headers 'X-Foo' => 'bar', 'X-Baz' => 'bling'
|
165
|
+
'kthx'
|
166
|
+
end
|
167
|
+
}
|
168
|
+
|
169
|
+
get '/'
|
170
|
+
assert ok?
|
171
|
+
assert_equal 'bar', response['X-Foo']
|
172
|
+
assert_equal 'bling', response['X-Baz']
|
173
|
+
assert_equal 'kthx', body
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'returns the response headers hash when no hash provided' do
|
177
|
+
mock_app {
|
178
|
+
get '/' do
|
179
|
+
headers['X-Foo'] = 'bar'
|
180
|
+
'kthx'
|
181
|
+
end
|
182
|
+
}
|
230
183
|
|
231
|
-
|
232
|
-
|
233
|
-
|
184
|
+
get '/'
|
185
|
+
assert ok?
|
186
|
+
assert_equal 'bar', response['X-Foo']
|
187
|
+
end
|
234
188
|
end
|
235
189
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
get '/'
|
245
|
-
assert ok?
|
246
|
-
assert_equal 'text/html;charset=utf-8', response['Content-Type']
|
247
|
-
assert_equal "<h1>Hello, World</h1>", body
|
248
|
-
end
|
190
|
+
describe 'session' do
|
191
|
+
it 'uses the existing rack.session' do
|
192
|
+
mock_app {
|
193
|
+
get '/' do
|
194
|
+
session[:foo]
|
195
|
+
end
|
196
|
+
}
|
249
197
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
get '/foo.xml' do
|
254
|
-
content_type :foo
|
255
|
-
"I AM FOO"
|
256
|
-
end
|
257
|
-
}
|
198
|
+
get '/', {}, { 'rack.session' => { :foo => 'bar' } }
|
199
|
+
assert_equal 'bar', body
|
200
|
+
end
|
258
201
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
assert_equal 'I AM FOO', body
|
263
|
-
end
|
202
|
+
it 'creates a new session when none provided' do
|
203
|
+
mock_app {
|
204
|
+
enable :sessions
|
264
205
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
end
|
271
|
-
}
|
272
|
-
assert_raise(RuntimeError) { get '/foo.xml' }
|
273
|
-
end
|
274
|
-
end
|
206
|
+
get '/' do
|
207
|
+
assert session.empty?
|
208
|
+
session[:foo] = 'bar'
|
209
|
+
redirect '/hi'
|
210
|
+
end
|
275
211
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
end
|
212
|
+
get '/hi' do
|
213
|
+
"hi #{session[:foo]}"
|
214
|
+
end
|
215
|
+
}
|
281
216
|
|
282
|
-
|
283
|
-
|
284
|
-
|
217
|
+
get '/'
|
218
|
+
follow_redirect!
|
219
|
+
assert_equal 'hi bar', body
|
220
|
+
end
|
285
221
|
end
|
286
222
|
|
287
|
-
|
288
|
-
|
289
|
-
mock_app {
|
290
|
-
get '/file.txt' do
|
291
|
-
send_file path, opts
|
292
|
-
end
|
293
|
-
}
|
294
|
-
end
|
223
|
+
describe 'mime_type' do
|
224
|
+
include Sinatra::Helpers
|
295
225
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
226
|
+
it "looks up mime types in Rack's MIME registry" do
|
227
|
+
Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
|
228
|
+
assert_equal 'application/foo', mime_type('foo')
|
229
|
+
assert_equal 'application/foo', mime_type('.foo')
|
230
|
+
assert_equal 'application/foo', mime_type(:foo)
|
231
|
+
end
|
302
232
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
assert_equal 'text/plain', response['Content-Type']
|
307
|
-
end
|
233
|
+
it 'returns nil when given nil' do
|
234
|
+
assert mime_type(nil).nil?
|
235
|
+
end
|
308
236
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
assert_equal 'Hello World'.length.to_s, response['Content-Length']
|
313
|
-
end
|
237
|
+
it 'returns nil when media type not registered' do
|
238
|
+
assert mime_type(:bizzle).nil?
|
239
|
+
end
|
314
240
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
assert_equal File.mtime(@file).httpdate, response['Last-Modified']
|
241
|
+
it 'returns the argument when given a media type string' do
|
242
|
+
assert_equal 'text/plain', mime_type('text/plain')
|
243
|
+
end
|
319
244
|
end
|
320
245
|
|
321
|
-
|
246
|
+
test 'Base.mime_type registers mime type' do
|
322
247
|
mock_app {
|
323
|
-
|
324
|
-
send_file 'this-file-does-not-exist.txt'
|
325
|
-
end
|
326
|
-
}
|
327
|
-
get '/'
|
328
|
-
assert not_found?
|
329
|
-
end
|
330
|
-
|
331
|
-
it "does not set the Content-Disposition header by default" do
|
332
|
-
send_file_app
|
333
|
-
get '/file.txt'
|
334
|
-
assert_nil response['Content-Disposition']
|
335
|
-
end
|
336
|
-
|
337
|
-
it "sets the Content-Disposition header when :disposition set to 'attachment'" do
|
338
|
-
send_file_app :disposition => 'attachment'
|
339
|
-
get '/file.txt'
|
340
|
-
assert_equal 'attachment; filename="file.txt"', response['Content-Disposition']
|
341
|
-
end
|
342
|
-
|
343
|
-
it "sets the Content-Disposition header when :filename provided" do
|
344
|
-
send_file_app :filename => 'foo.txt'
|
345
|
-
get '/file.txt'
|
346
|
-
assert_equal 'attachment; filename="foo.txt"', response['Content-Disposition']
|
347
|
-
end
|
348
|
-
end
|
248
|
+
mime_type :foo, 'application/foo'
|
349
249
|
|
350
|
-
describe 'Helpers#last_modified' do
|
351
|
-
before do
|
352
|
-
now = Time.now
|
353
|
-
mock_app {
|
354
250
|
get '/' do
|
355
|
-
|
356
|
-
last_modified now
|
357
|
-
'Boo!'
|
251
|
+
"foo is #{mime_type(:foo)}"
|
358
252
|
end
|
359
253
|
}
|
360
|
-
@now = now
|
361
|
-
end
|
362
|
-
|
363
|
-
it 'sets the Last-Modified header to a valid RFC 2616 date value' do
|
364
|
-
get '/'
|
365
|
-
assert_equal @now.httpdate, response['Last-Modified']
|
366
|
-
end
|
367
254
|
|
368
|
-
it 'returns a body when conditional get misses' do
|
369
255
|
get '/'
|
370
|
-
assert_equal
|
371
|
-
assert_equal 'Boo!', body
|
256
|
+
assert_equal 'foo is application/foo', body
|
372
257
|
end
|
373
258
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
259
|
+
describe 'content_type' do
|
260
|
+
it 'sets the Content-Type header' do
|
261
|
+
mock_app {
|
262
|
+
get '/' do
|
263
|
+
content_type 'text/plain'
|
264
|
+
'Hello World'
|
265
|
+
end
|
266
|
+
}
|
267
|
+
|
268
|
+
get '/'
|
269
|
+
assert_equal 'text/plain', response['Content-Type']
|
270
|
+
assert_equal 'Hello World', body
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'takes media type parameters (like charset=)' do
|
274
|
+
mock_app {
|
275
|
+
get '/' do
|
276
|
+
content_type 'text/html', :charset => 'utf-8'
|
277
|
+
"<h1>Hello, World</h1>"
|
278
|
+
end
|
279
|
+
}
|
280
|
+
|
281
|
+
get '/'
|
282
|
+
assert ok?
|
283
|
+
assert_equal 'text/html;charset=utf-8', response['Content-Type']
|
284
|
+
assert_equal "<h1>Hello, World</h1>", body
|
285
|
+
end
|
286
|
+
|
287
|
+
it "looks up symbols in Rack's mime types dictionary" do
|
288
|
+
Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
|
289
|
+
mock_app {
|
290
|
+
get '/foo.xml' do
|
291
|
+
content_type :foo
|
292
|
+
"I AM FOO"
|
293
|
+
end
|
294
|
+
}
|
295
|
+
|
296
|
+
get '/foo.xml'
|
297
|
+
assert ok?
|
298
|
+
assert_equal 'application/foo', response['Content-Type']
|
299
|
+
assert_equal 'I AM FOO', body
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'fails when no mime type is registered for the argument provided' do
|
303
|
+
mock_app {
|
304
|
+
get '/foo.xml' do
|
305
|
+
content_type :bizzle
|
306
|
+
"I AM FOO"
|
307
|
+
end
|
308
|
+
}
|
380
309
|
|
381
|
-
|
382
|
-
|
383
|
-
mock_app {
|
384
|
-
get '/' do
|
385
|
-
body { 'Hello World' }
|
386
|
-
etag 'FOO'
|
387
|
-
'Boo!'
|
388
|
-
end
|
389
|
-
}
|
310
|
+
assert_raise(RuntimeError) { get '/foo.xml' }
|
311
|
+
end
|
390
312
|
end
|
391
313
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
314
|
+
describe 'send_file' do
|
315
|
+
setup do
|
316
|
+
@file = File.dirname(__FILE__) + '/file.txt'
|
317
|
+
File.open(@file, 'wb') { |io| io.write('Hello World') }
|
318
|
+
end
|
396
319
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
end
|
320
|
+
def teardown
|
321
|
+
File.unlink @file
|
322
|
+
@file = nil
|
323
|
+
end
|
402
324
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
325
|
+
def send_file_app(opts={})
|
326
|
+
path = @file
|
327
|
+
mock_app {
|
328
|
+
get '/file.txt' do
|
329
|
+
send_file path, opts
|
330
|
+
end
|
331
|
+
}
|
332
|
+
end
|
333
|
+
|
334
|
+
it "sends the contents of the file" do
|
335
|
+
send_file_app
|
336
|
+
get '/file.txt'
|
337
|
+
assert ok?
|
338
|
+
assert_equal 'Hello World', body
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'sets the Content-Type response header if a mime-type can be located' do
|
342
|
+
send_file_app
|
343
|
+
get '/file.txt'
|
344
|
+
assert_equal 'text/plain', response['Content-Type']
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'sets the Content-Length response header' do
|
348
|
+
send_file_app
|
349
|
+
get '/file.txt'
|
350
|
+
assert_equal 'Hello World'.length.to_s, response['Content-Length']
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'sets the Last-Modified response header' do
|
354
|
+
send_file_app
|
355
|
+
get '/file.txt'
|
356
|
+
assert_equal File.mtime(@file).httpdate, response['Last-Modified']
|
357
|
+
end
|
358
|
+
|
359
|
+
it "returns a 404 when not found" do
|
360
|
+
mock_app {
|
361
|
+
get '/' do
|
362
|
+
send_file 'this-file-does-not-exist.txt'
|
363
|
+
end
|
364
|
+
}
|
365
|
+
get '/'
|
366
|
+
assert not_found?
|
367
|
+
end
|
368
|
+
|
369
|
+
it "does not set the Content-Disposition header by default" do
|
370
|
+
send_file_app
|
371
|
+
get '/file.txt'
|
372
|
+
assert_nil response['Content-Disposition']
|
373
|
+
end
|
374
|
+
|
375
|
+
it "sets the Content-Disposition header when :disposition set to 'attachment'" do
|
376
|
+
send_file_app :disposition => 'attachment'
|
377
|
+
get '/file.txt'
|
378
|
+
assert_equal 'attachment; filename="file.txt"', response['Content-Disposition']
|
379
|
+
end
|
380
|
+
|
381
|
+
it "sets the Content-Disposition header when :filename provided" do
|
382
|
+
send_file_app :filename => 'foo.txt'
|
383
|
+
get '/file.txt'
|
384
|
+
assert_equal 'attachment; filename="foo.txt"', response['Content-Disposition']
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
describe 'cache_control' do
|
389
|
+
setup do
|
390
|
+
mock_app {
|
391
|
+
get '/' do
|
392
|
+
cache_control :public, :no_cache, :max_age => 60
|
393
|
+
'Hello World'
|
394
|
+
end
|
395
|
+
}
|
396
|
+
end
|
408
397
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
398
|
+
it 'sets the Cache-Control header' do
|
399
|
+
get '/'
|
400
|
+
assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
|
401
|
+
end
|
413
402
|
end
|
414
403
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
404
|
+
describe 'expires' do
|
405
|
+
setup do
|
406
|
+
mock_app {
|
407
|
+
get '/' do
|
408
|
+
expires 60, :public, :no_cache
|
409
|
+
'Hello World'
|
410
|
+
end
|
411
|
+
}
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'sets the Cache-Control header' do
|
415
|
+
get '/'
|
416
|
+
assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'sets the Expires header' do
|
420
|
+
get '/'
|
421
|
+
assert_not_nil response['Expires']
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
describe 'last_modified' do
|
426
|
+
setup do
|
427
|
+
now = Time.now
|
428
|
+
mock_app {
|
429
|
+
get '/' do
|
430
|
+
body { 'Hello World' }
|
431
|
+
last_modified now
|
432
|
+
'Boo!'
|
433
|
+
end
|
434
|
+
}
|
435
|
+
@now = now
|
436
|
+
end
|
437
|
+
|
438
|
+
it 'sets the Last-Modified header to a valid RFC 2616 date value' do
|
439
|
+
get '/'
|
440
|
+
assert_equal @now.httpdate, response['Last-Modified']
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'returns a body when conditional get misses' do
|
444
|
+
get '/'
|
445
|
+
assert_equal 200, status
|
446
|
+
assert_equal 'Boo!', body
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'halts when a conditional GET matches' do
|
450
|
+
get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => @now.httpdate }
|
451
|
+
assert_equal 304, status
|
452
|
+
assert_equal '', body
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
describe 'etag' do
|
457
|
+
setup do
|
458
|
+
mock_app {
|
459
|
+
get '/' do
|
460
|
+
body { 'Hello World' }
|
461
|
+
etag 'FOO'
|
462
|
+
'Boo!'
|
463
|
+
end
|
464
|
+
}
|
465
|
+
end
|
466
|
+
|
467
|
+
it 'sets the ETag header' do
|
468
|
+
get '/'
|
469
|
+
assert_equal '"FOO"', response['ETag']
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'returns a body when conditional get misses' do
|
473
|
+
get '/'
|
474
|
+
assert_equal 200, status
|
475
|
+
assert_equal 'Boo!', body
|
476
|
+
end
|
477
|
+
|
478
|
+
it 'halts when a conditional GET matches' do
|
479
|
+
get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
|
480
|
+
assert_equal 304, status
|
481
|
+
assert_equal '', body
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'should handle multiple ETag values in If-None-Match header' do
|
485
|
+
get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
|
486
|
+
assert_equal 304, status
|
487
|
+
assert_equal '', body
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'uses a weak etag with the :weak option' do
|
491
|
+
mock_app {
|
492
|
+
get '/' do
|
493
|
+
etag 'FOO', :weak
|
494
|
+
"that's weak, dude."
|
495
|
+
end
|
496
|
+
}
|
497
|
+
get '/'
|
498
|
+
assert_equal 'W/"FOO"', response['ETag']
|
499
|
+
end
|
424
500
|
end
|
425
|
-
end
|
426
501
|
|
427
|
-
describe '
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
502
|
+
describe 'back' do
|
503
|
+
it "makes redirecting back pretty" do
|
504
|
+
mock_app {
|
505
|
+
get '/foo' do
|
506
|
+
redirect back
|
507
|
+
end
|
508
|
+
}
|
434
509
|
|
435
|
-
|
436
|
-
|
437
|
-
|
510
|
+
get '/foo', {}, 'HTTP_REFERER' => 'http://github.com'
|
511
|
+
assert redirect?
|
512
|
+
assert_equal "http://github.com", response.location
|
513
|
+
end
|
438
514
|
end
|
439
|
-
end
|
440
515
|
|
441
|
-
module HelperOne; def one; '1'; end; end
|
442
|
-
module HelperTwo; def two; '2'; end; end
|
516
|
+
module ::HelperOne; def one; '1'; end; end
|
517
|
+
module ::HelperTwo; def two; '2'; end; end
|
443
518
|
|
444
|
-
describe 'Adding new helpers' do
|
445
|
-
|
446
|
-
|
447
|
-
|
519
|
+
describe 'Adding new helpers' do
|
520
|
+
it 'takes a list of modules to mix into the app' do
|
521
|
+
mock_app {
|
522
|
+
helpers ::HelperOne, ::HelperTwo
|
448
523
|
|
449
|
-
|
450
|
-
|
451
|
-
|
524
|
+
get '/one' do
|
525
|
+
one
|
526
|
+
end
|
452
527
|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
528
|
+
get '/two' do
|
529
|
+
two
|
530
|
+
end
|
531
|
+
}
|
457
532
|
|
458
|
-
|
459
|
-
|
533
|
+
get '/one'
|
534
|
+
assert_equal '1', body
|
460
535
|
|
461
|
-
|
462
|
-
|
463
|
-
|
536
|
+
get '/two'
|
537
|
+
assert_equal '2', body
|
538
|
+
end
|
464
539
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
540
|
+
it 'takes a block to mix into the app' do
|
541
|
+
mock_app {
|
542
|
+
helpers do
|
543
|
+
def foo
|
544
|
+
'foo'
|
545
|
+
end
|
470
546
|
end
|
471
|
-
end
|
472
547
|
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
548
|
+
get '/' do
|
549
|
+
foo
|
550
|
+
end
|
551
|
+
}
|
477
552
|
|
478
|
-
|
479
|
-
|
480
|
-
|
553
|
+
get '/'
|
554
|
+
assert_equal 'foo', body
|
555
|
+
end
|
481
556
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
557
|
+
it 'evaluates the block in class context so that methods can be aliased' do
|
558
|
+
mock_app {
|
559
|
+
helpers do
|
560
|
+
alias_method :h, :escape_html
|
561
|
+
end
|
487
562
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
563
|
+
get '/' do
|
564
|
+
h('42 < 43')
|
565
|
+
end
|
566
|
+
}
|
492
567
|
|
493
|
-
|
494
|
-
|
495
|
-
|
568
|
+
get '/'
|
569
|
+
assert ok?
|
570
|
+
assert_equal '42 < 43', body
|
571
|
+
end
|
496
572
|
end
|
497
573
|
end
|