sinatra-base 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/AUTHORS +43 -0
  2. data/CHANGES +511 -0
  3. data/LICENSE +22 -0
  4. data/README.jp.rdoc +552 -0
  5. data/README.rdoc +636 -0
  6. data/Rakefile +116 -0
  7. data/lib/sinatra.rb +7 -0
  8. data/lib/sinatra/base.rb +1167 -0
  9. data/lib/sinatra/images/404.png +0 -0
  10. data/lib/sinatra/images/500.png +0 -0
  11. data/lib/sinatra/main.rb +28 -0
  12. data/lib/sinatra/showexceptions.rb +307 -0
  13. data/lib/sinatra/tilt.rb +746 -0
  14. data/sinatra-base.gemspec +94 -0
  15. data/test/base_test.rb +160 -0
  16. data/test/builder_test.rb +65 -0
  17. data/test/contest.rb +64 -0
  18. data/test/erb_test.rb +81 -0
  19. data/test/erubis_test.rb +82 -0
  20. data/test/extensions_test.rb +100 -0
  21. data/test/filter_test.rb +221 -0
  22. data/test/haml_test.rb +95 -0
  23. data/test/helper.rb +76 -0
  24. data/test/helpers_test.rb +582 -0
  25. data/test/less_test.rb +37 -0
  26. data/test/mapped_error_test.rb +197 -0
  27. data/test/middleware_test.rb +68 -0
  28. data/test/public/favicon.ico +0 -0
  29. data/test/request_test.rb +33 -0
  30. data/test/response_test.rb +42 -0
  31. data/test/result_test.rb +98 -0
  32. data/test/route_added_hook_test.rb +59 -0
  33. data/test/routing_test.rb +860 -0
  34. data/test/sass_test.rb +85 -0
  35. data/test/server_test.rb +47 -0
  36. data/test/settings_test.rb +368 -0
  37. data/test/sinatra_test.rb +13 -0
  38. data/test/static_test.rb +93 -0
  39. data/test/templates_test.rb +159 -0
  40. data/test/views/error.builder +3 -0
  41. data/test/views/error.erb +3 -0
  42. data/test/views/error.erubis +3 -0
  43. data/test/views/error.haml +3 -0
  44. data/test/views/error.sass +2 -0
  45. data/test/views/foo/hello.test +1 -0
  46. data/test/views/hello.builder +1 -0
  47. data/test/views/hello.erb +1 -0
  48. data/test/views/hello.erubis +1 -0
  49. data/test/views/hello.haml +1 -0
  50. data/test/views/hello.less +5 -0
  51. data/test/views/hello.sass +2 -0
  52. data/test/views/hello.test +1 -0
  53. data/test/views/layout2.builder +3 -0
  54. data/test/views/layout2.erb +2 -0
  55. data/test/views/layout2.erubis +2 -0
  56. data/test/views/layout2.haml +2 -0
  57. data/test/views/layout2.test +1 -0
  58. metadata +257 -0
@@ -0,0 +1,76 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+
3
+ begin
4
+ require 'rack'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'rack'
8
+ end
9
+
10
+ testdir = File.dirname(__FILE__)
11
+ $LOAD_PATH.unshift testdir unless $LOAD_PATH.include?(testdir)
12
+
13
+ libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
14
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
15
+
16
+ require 'contest'
17
+ require 'rack/test'
18
+ require 'sinatra/base'
19
+
20
+ class Sinatra::Base
21
+ # Allow assertions in request context
22
+ include Test::Unit::Assertions
23
+ end
24
+
25
+ Sinatra::Base.set :environment, :test
26
+
27
+ class Test::Unit::TestCase
28
+ include Rack::Test::Methods
29
+
30
+ class << self
31
+ alias_method :it, :test
32
+ end
33
+
34
+ alias_method :response, :last_response
35
+
36
+ setup do
37
+ Sinatra::Base.set :environment, :test
38
+ end
39
+
40
+ # Sets up a Sinatra::Base subclass defined with the block
41
+ # given. Used in setup or individual spec methods to establish
42
+ # the application.
43
+ def mock_app(base=Sinatra::Base, &block)
44
+ @app = Sinatra.new(base, &block)
45
+ end
46
+
47
+ def app
48
+ Rack::Lint.new(@app)
49
+ end
50
+
51
+ def body
52
+ response.body.to_s
53
+ end
54
+
55
+ # Delegate other missing methods to response.
56
+ def method_missing(name, *args, &block)
57
+ if response && response.respond_to?(name)
58
+ response.send(name, *args, &block)
59
+ else
60
+ super
61
+ end
62
+ end
63
+
64
+ # Also check response since we delegate there.
65
+ def respond_to?(symbol, include_private=false)
66
+ super || (response && response.respond_to?(symbol, include_private))
67
+ end
68
+
69
+ # Do not output warnings for the duration of the block.
70
+ def silence_warnings
71
+ $VERBOSE, v = nil, $VERBOSE
72
+ yield
73
+ ensure
74
+ $VERBOSE = v
75
+ end
76
+ end
@@ -0,0 +1,582 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class HelpersTest < Test::Unit::TestCase
4
+ def test_default
5
+ assert true
6
+ end
7
+
8
+ describe 'status' do
9
+ setup do
10
+ mock_app {
11
+ get '/' do
12
+ status 207
13
+ nil
14
+ end
15
+ }
16
+ end
17
+
18
+ it 'sets the response status code' do
19
+ get '/'
20
+ assert_equal 207, response.status
21
+ end
22
+ end
23
+
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
+ }
31
+
32
+ get '/'
33
+ assert_equal 'Hello World', body
34
+ end
35
+
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
+ }
42
+
43
+ get '/'
44
+ assert_equal 'Hello World', body
45
+ end
46
+ end
47
+
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
+ }
83
+
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
89
+ end
90
+
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
+ }
125
+
126
+ get '/'
127
+ assert_equal 500, status
128
+ assert_equal 'FAIL', body
129
+ end
130
+ end
131
+
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
+ }
153
+
154
+ get '/'
155
+ assert_equal 404, status
156
+ assert_equal nil, response.headers['X-Cascade']
157
+ end
158
+ end
159
+
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
+ }
183
+
184
+ get '/'
185
+ assert ok?
186
+ assert_equal 'bar', response['X-Foo']
187
+ end
188
+ end
189
+
190
+ describe 'session' do
191
+ it 'uses the existing rack.session' do
192
+ mock_app {
193
+ get '/' do
194
+ session[:foo]
195
+ end
196
+ }
197
+
198
+ get '/', {}, { 'rack.session' => { :foo => 'bar' } }
199
+ assert_equal 'bar', body
200
+ end
201
+
202
+ it 'creates a new session when none provided' do
203
+ mock_app {
204
+ enable :sessions
205
+
206
+ get '/' do
207
+ assert session.empty?
208
+ session[:foo] = 'bar'
209
+ redirect '/hi'
210
+ end
211
+
212
+ get '/hi' do
213
+ "hi #{session[:foo]}"
214
+ end
215
+ }
216
+
217
+ get '/'
218
+ follow_redirect!
219
+ assert_equal 'hi bar', body
220
+ end
221
+ end
222
+
223
+ describe 'mime_type' do
224
+ include Sinatra::Helpers
225
+
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
232
+
233
+ it 'returns nil when given nil' do
234
+ assert mime_type(nil).nil?
235
+ end
236
+
237
+ it 'returns nil when media type not registered' do
238
+ assert mime_type(:bizzle).nil?
239
+ end
240
+
241
+ it 'returns the argument when given a media type string' do
242
+ assert_equal 'text/plain', mime_type('text/plain')
243
+ end
244
+ end
245
+
246
+ test 'Base.mime_type registers mime type' do
247
+ mock_app {
248
+ mime_type :foo, 'application/foo'
249
+
250
+ get '/' do
251
+ "foo is #{mime_type(:foo)}"
252
+ end
253
+ }
254
+
255
+ get '/'
256
+ assert_equal 'foo is application/foo', body
257
+ end
258
+
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
+ }
309
+
310
+ assert_raise(RuntimeError) { get '/foo.xml' }
311
+ end
312
+ end
313
+
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
319
+
320
+ def teardown
321
+ File.unlink @file
322
+ @file = nil
323
+ end
324
+
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
397
+
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
402
+ end
403
+
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
+
455
+ it 'ignores nil' do
456
+ mock_app {
457
+ get '/' do last_modified nil; 200; end
458
+ }
459
+
460
+ get '/'
461
+ assert ! response['Last-Modified']
462
+ end
463
+ end
464
+
465
+ describe 'etag' do
466
+ setup do
467
+ mock_app {
468
+ get '/' do
469
+ body { 'Hello World' }
470
+ etag 'FOO'
471
+ 'Boo!'
472
+ end
473
+ }
474
+ end
475
+
476
+ it 'sets the ETag header' do
477
+ get '/'
478
+ assert_equal '"FOO"', response['ETag']
479
+ end
480
+
481
+ it 'returns a body when conditional get misses' do
482
+ get '/'
483
+ assert_equal 200, status
484
+ assert_equal 'Boo!', body
485
+ end
486
+
487
+ it 'halts when a conditional GET matches' do
488
+ get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
489
+ assert_equal 304, status
490
+ assert_equal '', body
491
+ end
492
+
493
+ it 'should handle multiple ETag values in If-None-Match header' do
494
+ get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
495
+ assert_equal 304, status
496
+ assert_equal '', body
497
+ end
498
+
499
+ it 'uses a weak etag with the :weak option' do
500
+ mock_app {
501
+ get '/' do
502
+ etag 'FOO', :weak
503
+ "that's weak, dude."
504
+ end
505
+ }
506
+ get '/'
507
+ assert_equal 'W/"FOO"', response['ETag']
508
+ end
509
+ end
510
+
511
+ describe 'back' do
512
+ it "makes redirecting back pretty" do
513
+ mock_app {
514
+ get '/foo' do
515
+ redirect back
516
+ end
517
+ }
518
+
519
+ get '/foo', {}, 'HTTP_REFERER' => 'http://github.com'
520
+ assert redirect?
521
+ assert_equal "http://github.com", response.location
522
+ end
523
+ end
524
+
525
+ module ::HelperOne; def one; '1'; end; end
526
+ module ::HelperTwo; def two; '2'; end; end
527
+
528
+ describe 'Adding new helpers' do
529
+ it 'takes a list of modules to mix into the app' do
530
+ mock_app {
531
+ helpers ::HelperOne, ::HelperTwo
532
+
533
+ get '/one' do
534
+ one
535
+ end
536
+
537
+ get '/two' do
538
+ two
539
+ end
540
+ }
541
+
542
+ get '/one'
543
+ assert_equal '1', body
544
+
545
+ get '/two'
546
+ assert_equal '2', body
547
+ end
548
+
549
+ it 'takes a block to mix into the app' do
550
+ mock_app {
551
+ helpers do
552
+ def foo
553
+ 'foo'
554
+ end
555
+ end
556
+
557
+ get '/' do
558
+ foo
559
+ end
560
+ }
561
+
562
+ get '/'
563
+ assert_equal 'foo', body
564
+ end
565
+
566
+ it 'evaluates the block in class context so that methods can be aliased' do
567
+ mock_app {
568
+ helpers do
569
+ alias_method :h, :escape_html
570
+ end
571
+
572
+ get '/' do
573
+ h('42 < 43')
574
+ end
575
+ }
576
+
577
+ get '/'
578
+ assert ok?
579
+ assert_equal '42 &lt; 43', body
580
+ end
581
+ end
582
+ end