roda 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +70 -0
  3. data/README.rdoc +261 -302
  4. data/Rakefile +1 -1
  5. data/doc/release_notes/1.2.0.txt +406 -0
  6. data/lib/roda.rb +206 -124
  7. data/lib/roda/plugins/all_verbs.rb +11 -10
  8. data/lib/roda/plugins/assets.rb +5 -5
  9. data/lib/roda/plugins/backtracking_array.rb +12 -5
  10. data/lib/roda/plugins/caching.rb +10 -8
  11. data/lib/roda/plugins/class_level_routing.rb +94 -0
  12. data/lib/roda/plugins/content_for.rb +6 -0
  13. data/lib/roda/plugins/default_headers.rb +4 -11
  14. data/lib/roda/plugins/delay_build.rb +42 -0
  15. data/lib/roda/plugins/delegate.rb +64 -0
  16. data/lib/roda/plugins/drop_body.rb +33 -0
  17. data/lib/roda/plugins/empty_root.rb +48 -0
  18. data/lib/roda/plugins/environments.rb +68 -0
  19. data/lib/roda/plugins/error_email.rb +1 -2
  20. data/lib/roda/plugins/error_handler.rb +1 -1
  21. data/lib/roda/plugins/halt.rb +7 -5
  22. data/lib/roda/plugins/head.rb +4 -2
  23. data/lib/roda/plugins/header_matchers.rb +17 -9
  24. data/lib/roda/plugins/hooks.rb +16 -32
  25. data/lib/roda/plugins/json.rb +4 -10
  26. data/lib/roda/plugins/mailer.rb +233 -0
  27. data/lib/roda/plugins/match_affix.rb +48 -0
  28. data/lib/roda/plugins/multi_route.rb +9 -11
  29. data/lib/roda/plugins/multi_run.rb +81 -0
  30. data/lib/roda/plugins/named_templates.rb +93 -0
  31. data/lib/roda/plugins/not_allowed.rb +43 -48
  32. data/lib/roda/plugins/path.rb +63 -2
  33. data/lib/roda/plugins/render.rb +79 -48
  34. data/lib/roda/plugins/render_each.rb +6 -0
  35. data/lib/roda/plugins/sinatra_helpers.rb +523 -0
  36. data/lib/roda/plugins/slash_path_empty.rb +25 -0
  37. data/lib/roda/plugins/static_path_info.rb +64 -0
  38. data/lib/roda/plugins/streaming.rb +1 -1
  39. data/lib/roda/plugins/view_subdirs.rb +12 -8
  40. data/lib/roda/version.rb +1 -1
  41. data/spec/integration_spec.rb +33 -0
  42. data/spec/plugin/backtracking_array_spec.rb +24 -18
  43. data/spec/plugin/class_level_routing_spec.rb +138 -0
  44. data/spec/plugin/delay_build_spec.rb +23 -0
  45. data/spec/plugin/delegate_spec.rb +20 -0
  46. data/spec/plugin/drop_body_spec.rb +20 -0
  47. data/spec/plugin/empty_root_spec.rb +14 -0
  48. data/spec/plugin/environments_spec.rb +31 -0
  49. data/spec/plugin/h_spec.rb +1 -3
  50. data/spec/plugin/header_matchers_spec.rb +14 -0
  51. data/spec/plugin/hooks_spec.rb +3 -5
  52. data/spec/plugin/mailer_spec.rb +191 -0
  53. data/spec/plugin/match_affix_spec.rb +22 -0
  54. data/spec/plugin/multi_run_spec.rb +31 -0
  55. data/spec/plugin/named_templates_spec.rb +65 -0
  56. data/spec/plugin/path_spec.rb +66 -2
  57. data/spec/plugin/render_spec.rb +46 -1
  58. data/spec/plugin/sinatra_helpers_spec.rb +534 -0
  59. data/spec/plugin/slash_path_empty_spec.rb +22 -0
  60. data/spec/plugin/static_path_info_spec.rb +50 -0
  61. data/spec/request_spec.rb +23 -0
  62. data/spec/response_spec.rb +12 -1
  63. metadata +48 -6
@@ -59,9 +59,14 @@ describe "render plugin" do
59
59
  end
60
60
 
61
61
  it "custom default layout support" do
62
- app.render_opts[:layout] = "layout-alternative"
62
+ app.plugin :render, :layout => "layout-alternative"
63
63
  body("/home").strip.should == "<title>Alternative Layout: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
64
64
  end
65
+
66
+ it "using hash for :layout" do
67
+ app.plugin :render, :layout => {:inline=> 'a<%= yield %>b'}
68
+ body("/home").strip.should == "a<h1>Home</h1>\n<p>Hello Agent Smith</p>\nb"
69
+ end
65
70
  end
66
71
 
67
72
  describe "render plugin" do
@@ -119,6 +124,46 @@ describe "render plugin" do
119
124
  body.strip.should == '<%= bar %>'
120
125
  end
121
126
 
127
+ it "template renders with :template opts" do
128
+ app(:render) do
129
+ render_opts[:views] = "./spec/views"
130
+ render(:template=>"about", :locals=>{:title => "About Roda"})
131
+ end
132
+ body.strip.should == "<h1>About Roda</h1>"
133
+ end
134
+
135
+ it "template renders with :template_class opts" do
136
+ app(:render) do
137
+ @a = 1
138
+ render(:inline=>'i#{@a}', :template_class=>::Tilt[:str])
139
+ end
140
+ body.should == "i1"
141
+ end
142
+
143
+ it "template cache respects :opts" do
144
+ c = Class.new do
145
+ def initialize(path, _, opts)
146
+ @path = path
147
+ @opts = opts
148
+ end
149
+ def render(*)
150
+ "#{@path}-#{@opts[:foo]}"
151
+ end
152
+ end
153
+
154
+ app(:render) do |r|
155
+ r.is "a" do
156
+ render(:inline=>"i", :template_class=>c, :opts=>{:foo=>'a'})
157
+ end
158
+ r.is "b" do
159
+ render(:inline=>"i", :template_class=>c, :opts=>{:foo=>'b'})
160
+ end
161
+ end
162
+
163
+ body('/a').should == "i-a"
164
+ body('/b').should == "i-b"
165
+ end
166
+
122
167
  it "render_opts inheritance" do
123
168
  c = Class.new(Roda)
124
169
  c.plugin :render
@@ -0,0 +1,534 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ require 'uri'
4
+
5
+ describe "sinatra_helpers plugin" do
6
+ def sin_app(&block)
7
+ app(:sinatra_helpers, &block)
8
+ end
9
+
10
+ def status_app(code, &block)
11
+ #code += 2 if [204, 205, 304].include? code
12
+ block ||= proc{}
13
+ sin_app do |r|
14
+ status code
15
+ instance_eval(&block).inspect
16
+ end
17
+ end
18
+
19
+ it 'status returns the response status code if not given an argument' do
20
+ status_app(207){status}
21
+ body.should == "207"
22
+ end
23
+
24
+ it 'status sets the response status code if given an argument' do
25
+ status_app 207
26
+ status.should == 207
27
+ end
28
+
29
+ it 'not_found? is true only if status == 404' do
30
+ status_app(404){not_found?}
31
+ body.should == 'true'
32
+ status_app(405){not_found?}
33
+ body.should == 'false'
34
+ status_app(403){not_found?}
35
+ body.should == 'false'
36
+ end
37
+
38
+ it 'informational? is true only for 1xx status' do
39
+ status_app(100 + rand(100)){informational?}
40
+ body.should == 'true'
41
+ status_app(200 + rand(400)){informational?}
42
+ body.should == 'false'
43
+ end
44
+
45
+ it 'success? is true only for 2xx status' do
46
+ status_app(200 + rand(100)){success?}
47
+ body.should == 'true'
48
+ status_app(100 + rand(100)){success?}
49
+ body.should == 'false'
50
+ status_app(300 + rand(300)){success?}
51
+ body.should == 'false'
52
+ end
53
+
54
+ it 'redirect? is true only for 3xx status' do
55
+ status_app(300 + rand(100)){redirect?}
56
+ body.should == 'true'
57
+ status_app(200 + rand(100)){redirect?}
58
+ body.should == 'false'
59
+ status_app(400 + rand(200)){redirect?}
60
+ body.should == 'false'
61
+ end
62
+
63
+ it 'client_error? is true only for 4xx status' do
64
+ status_app(400 + rand(100)){client_error?}
65
+ body.should == 'true'
66
+ status_app(200 + rand(200)){client_error?}
67
+ body.should == 'false'
68
+ status_app(500 + rand(100)){client_error?}
69
+ body.should == 'false'
70
+ end
71
+
72
+ it 'server_error? is true only for 5xx status' do
73
+ status_app(500 + rand(100)){server_error?}
74
+ body.should == 'true'
75
+ status_app(200 + rand(300)){server_error?}
76
+ body.should == 'false'
77
+ end
78
+
79
+ describe 'body' do
80
+ it 'takes a block for deferred body generation' do
81
+ sin_app{body{'Hello World'}; nil}
82
+ body.should == 'Hello World'
83
+ header('Content-Length').should == '11'
84
+ end
85
+
86
+ it 'takes a String, Array, or other object responding to #each' do
87
+ sin_app{body 'Hello World'; nil}
88
+ body.should == 'Hello World'
89
+ header('Content-Length').should == '11'
90
+
91
+ sin_app{body ['Hello ', 'World']; nil}
92
+ body.should == 'Hello World'
93
+ header('Content-Length').should == '11'
94
+
95
+ o = Object.new
96
+ def o.each; yield 'Hello World' end
97
+ sin_app{body o; nil}
98
+ body.should == 'Hello World'
99
+ header('Content-Length').should == '11'
100
+ end
101
+ end
102
+
103
+ describe 'redirect' do
104
+ it 'uses a 302 when only a path is given' do
105
+ sin_app do
106
+ redirect '/foo'
107
+ fail 'redirect should halt'
108
+ end
109
+
110
+ status.should == 302
111
+ body.should == ''
112
+ header('Location').should == '/foo'
113
+ end
114
+
115
+ it 'adds script_name if given a path' do
116
+ sin_app{redirect "/foo"}
117
+ header('Location', '/bar', 'SCRIPT_NAME'=>'/foo').should == '/foo'
118
+ end
119
+
120
+ it 'does not adds script_name if not given a path' do
121
+ sin_app{redirect}
122
+ header('Location', '/bar', 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').should == '/foo/bar'
123
+ end
124
+
125
+ it 'respects :absolute_redirects option' do
126
+ sin_app{redirect}
127
+ app.opts[:absolute_redirects] = true
128
+ header('Location', '/bar', 'HTTP_HOST'=>'example.org', 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').should == 'http://example.org/foo/bar'
129
+ end
130
+
131
+ it 'respects :prefixed_redirects option' do
132
+ sin_app{redirect "/bar"}
133
+ app.opts[:prefixed_redirects] = true
134
+ header('Location', 'SCRIPT_NAME'=>'/foo').should == '/foo/bar'
135
+ end
136
+
137
+ it 'ignores :prefix_redirects option if not given a path' do
138
+ sin_app{redirect}
139
+ app.opts[:prefix_redirects] = true
140
+ header('Location', "/bar", 'SCRIPT_NAME'=>'/foo', 'REQUEST_METHOD'=>'POST').should == '/foo/bar'
141
+ end
142
+
143
+ it 'uses the code given when specified' do
144
+ sin_app{redirect '/foo', 301}
145
+ status.should == 301
146
+ end
147
+
148
+ it 'redirects back to request.referer when passed back' do
149
+ sin_app{redirect back}
150
+ header('Location', 'HTTP_REFERER' => '/foo').should == '/foo'
151
+ end
152
+
153
+ it 'uses 303 for post requests if request is HTTP 1.1, 302 for 1.0' do
154
+ sin_app{redirect '/foo'}
155
+ status('HTTP_VERSION' => 'HTTP/1.1', 'REQUEST_METHOD'=>'POST').should == 303
156
+ status('HTTP_VERSION' => 'HTTP/1.0', 'REQUEST_METHOD'=>'POST').should == 302
157
+ end
158
+ end
159
+
160
+ describe 'error' do
161
+ it 'sets a status code and halts' do
162
+ sin_app do
163
+ error
164
+ fail 'error should halt'
165
+ end
166
+
167
+ status.should == 500
168
+ body.should == ''
169
+ end
170
+
171
+ it 'accepts status code' do
172
+ sin_app{error 501}
173
+ status.should == 501
174
+ body.should == ''
175
+ end
176
+
177
+ it 'accepts body' do
178
+ sin_app{error '501'}
179
+ status.should == 500
180
+ body.should == '501'
181
+ end
182
+
183
+ it 'accepts status code and body' do
184
+ sin_app{error 502, '501'}
185
+ status.should == 502
186
+ body.should == '501'
187
+ end
188
+ end
189
+
190
+ describe 'not_found' do
191
+ it 'halts with a 404 status' do
192
+ sin_app do
193
+ not_found
194
+ fail 'not_found should halt'
195
+ end
196
+
197
+ status.should == 404
198
+ body.should == ''
199
+ end
200
+
201
+ it 'accepts optional body' do
202
+ sin_app{not_found 'nf'}
203
+ status.should == 404
204
+ body.should == 'nf'
205
+ end
206
+ end
207
+
208
+ describe 'headers' do
209
+ it 'sets headers on the response object when given a Hash' do
210
+ sin_app do
211
+ headers 'X-Foo' => 'bar'
212
+ 'kthx'
213
+ end
214
+
215
+ header('X-Foo').should == 'bar'
216
+ body.should == 'kthx'
217
+ end
218
+
219
+ it 'returns the response headers hash when no hash provided' do
220
+ sin_app{headers['X-Foo'] = 'bar'}
221
+ header('X-Foo').should == 'bar'
222
+ end
223
+ end
224
+
225
+ describe 'mime_type' do
226
+ before do
227
+ sin_app{|r| mime_type(r.path).to_s}
228
+ end
229
+
230
+ it "looks up mime types in Rack's MIME registry" do
231
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
232
+ body('foo').should == 'application/foo'
233
+ body(:foo).should == 'application/foo'
234
+ body('.foo').should == 'application/foo'
235
+ end
236
+
237
+ it 'returns nil when given nil' do
238
+ body('PATH_INFO'=>nil).should == ''
239
+ end
240
+
241
+ it 'returns nil when media type not registered' do
242
+ body('bizzle').should == ''
243
+ end
244
+
245
+ it 'returns the argument when given a media type string' do
246
+ body('text/plain').should == 'text/plain'
247
+ end
248
+
249
+ it 'supports mime types registered at the class level' do
250
+ app.mime_type :foo, 'application/foo'
251
+ body(:foo).should == 'application/foo'
252
+ end
253
+ end
254
+
255
+ describe 'content_type' do
256
+ it 'sets the Content-Type header' do
257
+ sin_app do
258
+ content_type 'text/plain'
259
+ 'Hello World'
260
+ end
261
+
262
+ header('Content-Type').should == 'text/plain'
263
+ body.should == 'Hello World'
264
+ end
265
+
266
+ it 'takes media type parameters (like charset=)' do
267
+ sin_app{content_type 'text/html', :charset => 'latin1'}
268
+ header('Content-Type').should == 'text/html;charset=latin1'
269
+ end
270
+
271
+ it "looks up symbols in Rack's mime types dictionary" do
272
+ sin_app{content_type :foo}
273
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
274
+ header('Content-Type').should == 'application/foo'
275
+ end
276
+
277
+ it 'fails when no mime type is registered for the argument provided' do
278
+ sin_app{content_type :bizzle}
279
+ proc{body}.should raise_error(Roda::RodaError)
280
+ end
281
+
282
+ it 'handles already present params' do
283
+ sin_app{content_type 'foo/bar;level=1', :charset => 'utf-8'}
284
+ header('Content-Type').should == 'foo/bar;level=1, charset=utf-8'
285
+ end
286
+
287
+ it 'does not add charset if present' do
288
+ sin_app{content_type 'text/plain;charset=utf-16', :charset => 'utf-8'}
289
+ header('Content-Type').should == 'text/plain;charset=utf-16'
290
+ end
291
+
292
+ it 'properly encodes parameters with delimiter characters' do
293
+ sin_app{|r| content_type 'image/png', :comment => r.path }
294
+ header('Content-Type', 'Hello, world!').should == 'image/png;comment="Hello, world!"'
295
+ header('Content-Type', 'semi;colon').should == 'image/png;comment="semi;colon"'
296
+ header('Content-Type', '"Whatever."').should == 'image/png;comment="\"Whatever.\""'
297
+ end
298
+ end
299
+
300
+ describe 'attachment' do
301
+ before do
302
+ sin_app{|r| attachment r.path; 'b'}
303
+ end
304
+
305
+ it 'sets the Content-Disposition header' do
306
+ header('Content-Disposition', '/foo/test.xml').should == 'attachment; filename="test.xml"'
307
+ body.should == 'b'
308
+ end
309
+
310
+ it 'sets the Content-Disposition header even when a filename is not given' do
311
+ sin_app{attachment}
312
+ header('Content-Disposition', '/foo/test.xml').should == 'attachment'
313
+ end
314
+
315
+ it 'sets the Content-Type header' do
316
+ header('Content-Type', 'test.xml').should == 'application/xml'
317
+ end
318
+
319
+ it 'does not modify the default Content-Type without a file extension' do
320
+ header('Content-Type', 'README').should == 'text/html'
321
+ end
322
+
323
+ it 'should not modify the Content-Type if it is already set' do
324
+ sin_app do
325
+ content_type :atom
326
+ attachment 'test.xml'
327
+ end
328
+
329
+ header('Content-Type', 'README').should == 'application/atom+xml'
330
+ end
331
+ end
332
+
333
+ describe 'send_file' do
334
+ before(:all) do
335
+ file = @file = 'spec/assets/css/raw.css'
336
+ @content = File.read(@file)
337
+ sin_app{send_file file, env['OPTS'] || {}}
338
+ end
339
+
340
+ it "sends the contents of the file" do
341
+ status.should == 200
342
+ body.should == @content
343
+ end
344
+
345
+ it 'sets the Content-Type response header if a mime-type can be located' do
346
+ header('Content-Type').should == 'text/css'
347
+ end
348
+
349
+ it 'sets the Content-Type response header if type option is set to a file extension' do
350
+ header('Content-Type', 'OPTS'=>{:type => 'html'}).should == 'text/html'
351
+ end
352
+
353
+ it 'sets the Content-Type response header if type option is set to a mime type' do
354
+ header('Content-Type', 'OPTS'=>{:type => 'application/octet-stream'}).should == 'application/octet-stream'
355
+ end
356
+
357
+ it 'sets the Content-Length response header' do
358
+ header('Content-Length').should == @content.length.to_s
359
+ end
360
+
361
+ it 'sets the Last-Modified response header' do
362
+ header('Last-Modified').should == File.mtime(@file).httpdate
363
+ end
364
+
365
+ it 'allows passing in a different Last-Modified response header with :last_modified' do
366
+ time = Time.now
367
+ @app.plugin :caching
368
+ header('Last-Modified', 'OPTS'=>{:last_modified => time}).should == time.httpdate
369
+ end
370
+
371
+ it "returns a 404 when not found" do
372
+ sin_app{send_file 'this-file-does-not-exist.txt'}
373
+ status.should == 404
374
+ end
375
+
376
+ it "does not set the Content-Disposition header by default" do
377
+ header('Content-Disposition').should == nil
378
+ end
379
+
380
+ it "sets the Content-Disposition header when :disposition set to 'attachment'" do
381
+ header('Content-Disposition', 'OPTS'=>{:disposition => 'attachment'}).should == 'attachment; filename="raw.css"'
382
+ end
383
+
384
+ it "does not set add a file name if filename is false" do
385
+ header('Content-Disposition', 'OPTS'=>{:disposition => 'inline', :filename=>false}).should == 'inline'
386
+ end
387
+
388
+ it "sets the Content-Disposition header when :disposition set to 'inline'" do
389
+ header('Content-Disposition', 'OPTS'=>{:disposition => 'inline'}).should == 'inline; filename="raw.css"'
390
+ end
391
+
392
+ it "sets the Content-Disposition header when :filename provided" do
393
+ header('Content-Disposition', 'OPTS'=>{:filename => 'foo.txt'}).should == 'attachment; filename="foo.txt"'
394
+ end
395
+
396
+ it 'allows setting a custom status code' do
397
+ status('OPTS'=>{:status=>201}).should == 201
398
+ end
399
+
400
+ it "is able to send files with unknown mime type" do
401
+ header('Content-Type', 'OPTS'=>{:type => '.foobar'}).should == 'application/octet-stream'
402
+ end
403
+
404
+ it "does not override Content-Type if already set and no explicit type is given" do
405
+ file = @file
406
+ sin_app do
407
+ content_type :png
408
+ send_file file
409
+ end
410
+ header('Content-Type').should == 'image/png'
411
+ end
412
+
413
+ it "does override Content-Type even if already set, if explicit type is given" do
414
+ file = @file
415
+ sin_app do
416
+ content_type :png
417
+ send_file file, :type => :gif
418
+ end
419
+ header('Content-Type').should == 'image/gif'
420
+ end
421
+ end
422
+
423
+ describe 'uri' do
424
+ describe "without arguments" do
425
+ before do
426
+ sin_app{uri}
427
+ end
428
+
429
+ it 'generates absolute urls' do
430
+ body('HTTP_HOST'=>'example.org').should == 'http://example.org/'
431
+ end
432
+
433
+ it 'includes path_info' do
434
+ body('/foo', 'HTTP_HOST'=>'example.org').should == 'http://example.org/foo'
435
+ end
436
+
437
+ it 'includes script_name' do
438
+ body('/bar', 'HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/foo').should == 'http://example.org/foo/bar'
439
+ end
440
+
441
+ it 'handles standard HTTP and HTTPS ports' do
442
+ body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '80').should == 'http://example.org/'
443
+ body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '443', 'HTTPS'=>'on').should == 'https://example.org/'
444
+ end
445
+
446
+ it 'handles non-standard HTTP port' do
447
+ body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '81').should == 'http://example.org:81/'
448
+ body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '443').should == 'http://example.org:443/'
449
+ end
450
+
451
+ it 'handles non-standard HTTPS port' do
452
+ body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '444', 'HTTPS'=>'on').should == 'https://example.org:444/'
453
+ body('SERVER_NAME'=>'example.org', 'SERVER_PORT' => '80', 'HTTPS'=>'on').should == 'https://example.org:80/'
454
+ end
455
+
456
+ it 'handles reverse proxy' do
457
+ body('SERVER_NAME'=>'example.org', 'HTTP_X_FORWARDED_HOST' => 'example.com', 'SERVER_PORT' => '8080').should == 'http://example.com/'
458
+ end
459
+ end
460
+
461
+ it 'allows passing an alternative to path_info' do
462
+ sin_app{uri '/bar'}
463
+ body('HTTP_HOST'=>'example.org').should == 'http://example.org/bar'
464
+ body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/foo').should == 'http://example.org/foo/bar'
465
+ end
466
+
467
+ it 'handles absolute URIs' do
468
+ sin_app{uri 'http://google.com'}
469
+ body('HTTP_HOST'=>'example.org').should == 'http://google.com'
470
+ end
471
+
472
+ it 'handles different protocols' do
473
+ sin_app{uri 'mailto:jsmith@example.com'}
474
+ body('HTTP_HOST'=>'example.org').should == 'mailto:jsmith@example.com'
475
+ end
476
+
477
+ it 'allows turning off host' do
478
+ sin_app{uri '/foo', false}
479
+ body('HTTP_HOST'=>'example.org').should == '/foo'
480
+ body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/bar').should == '/bar/foo'
481
+ end
482
+
483
+ it 'allows turning off script_name' do
484
+ sin_app{uri '/foo', true, false}
485
+ body('HTTP_HOST'=>'example.org').should == 'http://example.org/foo'
486
+ body('HTTP_HOST'=>'example.org', "SCRIPT_NAME" => '/bar').should == 'http://example.org/foo'
487
+ end
488
+
489
+ it 'is aliased to #url' do
490
+ sin_app{url}
491
+ body('HTTP_HOST'=>'example.org').should == 'http://example.org/'
492
+ end
493
+
494
+ it 'is aliased to #to' do
495
+ sin_app{to}
496
+ body('HTTP_HOST'=>'example.org').should == 'http://example.org/'
497
+ end
498
+
499
+ it 'accepts a URI object instead of a String' do
500
+ sin_app{uri URI.parse('http://roda.jeremyevans.net')}
501
+ body.should == 'http://roda.jeremyevans.net'
502
+ end
503
+ end
504
+
505
+ it 'logger logs to rack.logger' do
506
+ sin_app{logger.info "foo"}
507
+ o = Object.new
508
+ def o.method_missing(*a)
509
+ (@a ||= []) << a
510
+ end
511
+ def o.logs
512
+ @a
513
+ end
514
+
515
+ status('rack.logger'=>o).should == 404
516
+ o.logs.should == [[:info, 'foo']]
517
+ end
518
+
519
+ it 'supports disabling delegation if :delegate=>false option is provided' do
520
+ app(:bare) do
521
+ plugin :sinatra_helpers, :delegate=>false
522
+ route do |r|
523
+ r.root{content_type}
524
+ r.is("req"){r.ssl?.to_s}
525
+ r.is("res"){response.not_found?.inspect}
526
+ end
527
+ end
528
+
529
+ proc{body}.should raise_error(NameError)
530
+ body('/req').should == 'false'
531
+ body('/res').should == 'nil'
532
+ end
533
+ end
534
+