adamwiggins-sinatra 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/AUTHORS +40 -0
  2. data/CHANGES +167 -0
  3. data/LICENSE +22 -0
  4. data/README.rdoc +529 -0
  5. data/Rakefile +180 -0
  6. data/compat/app_test.rb +300 -0
  7. data/compat/application_test.rb +334 -0
  8. data/compat/builder_test.rb +101 -0
  9. data/compat/custom_error_test.rb +62 -0
  10. data/compat/erb_test.rb +136 -0
  11. data/compat/events_test.rb +75 -0
  12. data/compat/filter_test.rb +30 -0
  13. data/compat/haml_test.rb +233 -0
  14. data/compat/helper.rb +21 -0
  15. data/compat/mapped_error_test.rb +72 -0
  16. data/compat/pipeline_test.rb +71 -0
  17. data/compat/public/foo.xml +1 -0
  18. data/compat/sass_test.rb +57 -0
  19. data/compat/sessions_test.rb +39 -0
  20. data/compat/streaming_test.rb +121 -0
  21. data/compat/sym_params_test.rb +19 -0
  22. data/compat/template_test.rb +30 -0
  23. data/compat/use_in_file_templates_test.rb +47 -0
  24. data/compat/views/foo.builder +1 -0
  25. data/compat/views/foo.erb +1 -0
  26. data/compat/views/foo.haml +1 -0
  27. data/compat/views/foo.sass +2 -0
  28. data/compat/views/foo_layout.erb +2 -0
  29. data/compat/views/foo_layout.haml +2 -0
  30. data/compat/views/layout_test/foo.builder +1 -0
  31. data/compat/views/layout_test/foo.erb +1 -0
  32. data/compat/views/layout_test/foo.haml +1 -0
  33. data/compat/views/layout_test/foo.sass +2 -0
  34. data/compat/views/layout_test/layout.builder +3 -0
  35. data/compat/views/layout_test/layout.erb +1 -0
  36. data/compat/views/layout_test/layout.haml +1 -0
  37. data/compat/views/layout_test/layout.sass +2 -0
  38. data/compat/views/no_layout/no_layout.builder +1 -0
  39. data/compat/views/no_layout/no_layout.haml +1 -0
  40. data/lib/sinatra/base.rb +818 -0
  41. data/lib/sinatra/compat.rb +239 -0
  42. data/lib/sinatra/images/404.png +0 -0
  43. data/lib/sinatra/images/500.png +0 -0
  44. data/lib/sinatra/main.rb +48 -0
  45. data/lib/sinatra/test/rspec.rb +2 -0
  46. data/lib/sinatra/test/spec.rb +2 -0
  47. data/lib/sinatra/test/unit.rb +11 -0
  48. data/lib/sinatra/test.rb +112 -0
  49. data/lib/sinatra.rb +3 -0
  50. data/sinatra.gemspec +107 -0
  51. data/test/base_test.rb +72 -0
  52. data/test/builder_test.rb +68 -0
  53. data/test/data/reload_app_file.rb +3 -0
  54. data/test/erb_test.rb +55 -0
  55. data/test/filter_test.rb +39 -0
  56. data/test/haml_test.rb +72 -0
  57. data/test/helpers_test.rb +368 -0
  58. data/test/mapped_error_test.rb +164 -0
  59. data/test/middleware_test.rb +63 -0
  60. data/test/options_test.rb +103 -0
  61. data/test/reload_test.rb +65 -0
  62. data/test/request_test.rb +11 -0
  63. data/test/result_test.rb +92 -0
  64. data/test/routing_test.rb +338 -0
  65. data/test/sass_test.rb +40 -0
  66. data/test/sinatra_test.rb +15 -0
  67. data/test/static_test.rb +60 -0
  68. data/test/templates_test.rb +92 -0
  69. data/test/views/hello.builder +1 -0
  70. data/test/views/hello.erb +1 -0
  71. data/test/views/hello.haml +1 -0
  72. data/test/views/hello.sass +2 -0
  73. data/test/views/hello.test +1 -0
  74. data/test/views/layout2.builder +3 -0
  75. data/test/views/layout2.erb +2 -0
  76. data/test/views/layout2.haml +2 -0
  77. data/test/views/layout2.test +1 -0
  78. metadata +159 -0
data/test/erb_test.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'test/spec'
2
+ require 'sinatra/base'
3
+ require 'sinatra/test'
4
+
5
+ describe "ERB Templates" do
6
+ include Sinatra::Test
7
+
8
+ def erb_app(&block)
9
+ mock_app {
10
+ set :views, File.dirname(__FILE__) + '/views'
11
+ get '/', &block
12
+ }
13
+ get '/'
14
+ end
15
+
16
+ it 'renders inline ERB strings' do
17
+ erb_app { erb '<%= 1 + 1 %>' }
18
+ should.be.ok
19
+ body.should.equal '2'
20
+ end
21
+
22
+ it 'renders .erb files in views path' do
23
+ erb_app { erb :hello }
24
+ should.be.ok
25
+ body.should.equal "Hello World\n"
26
+ end
27
+
28
+ it 'takes a :locals option' do
29
+ erb_app {
30
+ locals = {:foo => 'Bar'}
31
+ erb '<%= foo %>', :locals => locals
32
+ }
33
+ should.be.ok
34
+ body.should.equal 'Bar'
35
+ end
36
+
37
+ it "renders with inline layouts" do
38
+ mock_app {
39
+ layout { 'THIS. IS. <%= yield.upcase %>!' }
40
+ get('/') { erb 'Sparta' }
41
+ }
42
+ get '/'
43
+ should.be.ok
44
+ body.should.equal 'THIS. IS. SPARTA!'
45
+ end
46
+
47
+ it "renders with file layouts" do
48
+ erb_app {
49
+ erb 'Hello World', :layout => :layout2
50
+ }
51
+ should.be.ok
52
+ body.should.equal "ERB Layout!\nHello World\n"
53
+ end
54
+
55
+ end
@@ -0,0 +1,39 @@
1
+ require 'test/spec'
2
+ require 'sinatra/base'
3
+ require 'sinatra/test'
4
+
5
+ describe "Filters" do
6
+ include Sinatra::Test
7
+
8
+ it "executes filters in the order defined" do
9
+ count = 0
10
+ mock_app do
11
+ get('/') { 'Hello World' }
12
+ before {
13
+ count.should.be 0
14
+ count = 1
15
+ }
16
+ before {
17
+ count.should.be 1
18
+ count = 2
19
+ }
20
+ end
21
+
22
+ get '/'
23
+ should.be.ok
24
+ count.should.be 2
25
+ body.should.equal 'Hello World'
26
+ end
27
+
28
+ it "allows filters to modify the request" do
29
+ mock_app {
30
+ get('/foo') { 'foo' }
31
+ get('/bar') { 'bar' }
32
+ before { request.path_info = '/bar' }
33
+ }
34
+
35
+ get '/foo'
36
+ should.be.ok
37
+ body.should.be == 'bar'
38
+ end
39
+ end
data/test/haml_test.rb ADDED
@@ -0,0 +1,72 @@
1
+ require 'test/spec'
2
+ require 'sinatra/base'
3
+ require 'sinatra/test'
4
+
5
+ describe "HAML Templates" do
6
+ include Sinatra::Test
7
+
8
+ def haml_app(&block)
9
+ mock_app {
10
+ set :views, File.dirname(__FILE__) + '/views'
11
+ get '/', &block
12
+ }
13
+ get '/'
14
+ end
15
+
16
+ it 'renders inline HAML strings' do
17
+ haml_app { haml '%h1 Hiya' }
18
+ should.be.ok
19
+ body.should.equal "<h1>Hiya</h1>\n"
20
+ end
21
+
22
+ it 'renders .haml files in views path' do
23
+ haml_app { haml :hello }
24
+ should.be.ok
25
+ body.should.equal "<h1>Hello From Haml</h1>\n"
26
+ end
27
+
28
+ it "renders with inline layouts" do
29
+ mock_app {
30
+ layout { %q(%h1= 'THIS. IS. ' + yield.upcase) }
31
+ get('/') { haml '%em Sparta' }
32
+ }
33
+ get '/'
34
+ should.be.ok
35
+ body.should.equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>\n"
36
+ end
37
+
38
+ it "renders with file layouts" do
39
+ haml_app {
40
+ haml 'Hello World', :layout => :layout2
41
+ }
42
+ should.be.ok
43
+ body.should.equal "<h1>HAML Layout!</h1>\n<p>Hello World</p>\n"
44
+ end
45
+
46
+ it "raises error if template not found" do
47
+ mock_app {
48
+ get('/') { haml :no_such_template }
49
+ }
50
+ lambda { get('/') }.should.raise(Errno::ENOENT)
51
+ end
52
+
53
+ it "passes HAML options to the Haml engine" do
54
+ haml_app {
55
+ haml "!!!\n%h1 Hello World", :options => {:format => :html5}
56
+ }
57
+ should.be.ok
58
+ body.should.equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n"
59
+ end
60
+
61
+ it "passes default HAML options to the Haml engine" do
62
+ mock_app {
63
+ set :haml, {:format => :html5}
64
+ get '/' do
65
+ haml "!!!\n%h1 Hello World"
66
+ end
67
+ }
68
+ get '/'
69
+ should.be.ok
70
+ body.should.equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n"
71
+ end
72
+ end
@@ -0,0 +1,368 @@
1
+ require 'test/spec'
2
+ require 'sinatra/base'
3
+ require 'sinatra/test'
4
+
5
+ class Test::Unit::TestCase
6
+ include Sinatra::Test
7
+ end
8
+
9
+ describe 'Sinatra::Helpers' do
10
+ describe '#status' do
11
+ setup do
12
+ mock_app {
13
+ get '/' do
14
+ status 207
15
+ nil
16
+ end
17
+ }
18
+ end
19
+
20
+ it 'sets the response status code' do
21
+ get '/'
22
+ response.status.should.equal 207
23
+ end
24
+ end
25
+
26
+ describe '#body' do
27
+ it 'takes a block for defered body generation' do
28
+ mock_app {
29
+ get '/' do
30
+ body { 'Hello World' }
31
+ end
32
+ }
33
+
34
+ get '/'
35
+ body.should.equal 'Hello World'
36
+ end
37
+
38
+ it 'takes a String, Array, or other object responding to #each' do
39
+ mock_app {
40
+ get '/' do
41
+ body 'Hello World'
42
+ end
43
+ }
44
+
45
+ get '/'
46
+ body.should.equal 'Hello World'
47
+ end
48
+ end
49
+
50
+ describe '#redirect' do
51
+ it 'uses a 302 when only a path is given' do
52
+ mock_app {
53
+ get '/' do
54
+ redirect '/foo'
55
+ fail 'redirect should halt'
56
+ end
57
+ }
58
+
59
+ get '/'
60
+ status.should.equal 302
61
+ body.should.be.empty
62
+ response['Location'].should.equal '/foo'
63
+ end
64
+
65
+ it 'uses the code given when specified' do
66
+ mock_app {
67
+ get '/' do
68
+ redirect '/foo', 301
69
+ fail 'redirect should halt'
70
+ end
71
+ }
72
+
73
+ get '/'
74
+ status.should.equal 301
75
+ body.should.be.empty
76
+ response['Location'].should.equal '/foo'
77
+ end
78
+ end
79
+
80
+ describe '#error' do
81
+ it 'sets a status code and halts' do
82
+ mock_app {
83
+ get '/' do
84
+ error 501
85
+ fail 'error should halt'
86
+ end
87
+ }
88
+
89
+ get '/'
90
+ status.should.equal 501
91
+ body.should.be.empty
92
+ end
93
+
94
+ it 'takes an optional body' do
95
+ mock_app {
96
+ get '/' do
97
+ error 501, 'FAIL'
98
+ fail 'error should halt'
99
+ end
100
+ }
101
+
102
+ get '/'
103
+ status.should.equal 501
104
+ body.should.equal 'FAIL'
105
+ end
106
+
107
+ it 'uses a 500 status code when first argument is a body' do
108
+ mock_app {
109
+ get '/' do
110
+ error 'FAIL'
111
+ fail 'error should halt'
112
+ end
113
+ }
114
+
115
+ get '/'
116
+ status.should.equal 500
117
+ body.should.equal 'FAIL'
118
+ end
119
+ end
120
+
121
+ describe '#not_found' do
122
+ it 'halts with a 404 status' do
123
+ mock_app {
124
+ get '/' do
125
+ not_found
126
+ fail 'not_found should halt'
127
+ end
128
+ }
129
+
130
+ get '/'
131
+ status.should.equal 404
132
+ body.should.be.empty
133
+ end
134
+ end
135
+
136
+ describe '#session' do
137
+ it 'uses the existing rack.session' do
138
+ mock_app {
139
+ get '/' do
140
+ session[:foo]
141
+ end
142
+ }
143
+
144
+ get '/', :env => { 'rack.session' => { :foo => 'bar' } }
145
+ body.should.equal 'bar'
146
+ end
147
+
148
+ it 'creates a new session when none provided' do
149
+ mock_app {
150
+ get '/' do
151
+ session.should.be.empty
152
+ session[:foo] = 'bar'
153
+ 'Hi'
154
+ end
155
+ }
156
+
157
+ get '/'
158
+ body.should.equal 'Hi'
159
+ end
160
+ end
161
+
162
+ describe '#media_type' do
163
+ include Sinatra::Helpers
164
+ it "looks up media types in Rack's MIME registry" do
165
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
166
+ media_type('foo').should.equal 'application/foo'
167
+ media_type('.foo').should.equal 'application/foo'
168
+ media_type(:foo).should.equal 'application/foo'
169
+ end
170
+ it 'returns nil when given nil' do
171
+ media_type(nil).should.be.nil
172
+ end
173
+ it 'returns nil when media type not registered' do
174
+ media_type(:bizzle).should.be.nil
175
+ end
176
+ it 'returns the argument when given a media type string' do
177
+ media_type('text/plain').should.equal 'text/plain'
178
+ end
179
+ end
180
+
181
+ describe '#content_type' do
182
+ it 'sets the Content-Type header' do
183
+ mock_app {
184
+ get '/' do
185
+ content_type 'text/plain'
186
+ 'Hello World'
187
+ end
188
+ }
189
+
190
+ get '/'
191
+ response['Content-Type'].should.equal 'text/plain'
192
+ body.should.equal 'Hello World'
193
+ end
194
+
195
+ it 'takes media type parameters (like charset=)' do
196
+ mock_app {
197
+ get '/' do
198
+ content_type 'text/html', :charset => 'utf-8'
199
+ "<h1>Hello, World</h1>"
200
+ end
201
+ }
202
+
203
+ get '/'
204
+ should.be.ok
205
+ response['Content-Type'].should.equal 'text/html;charset=utf-8'
206
+ body.should.equal "<h1>Hello, World</h1>"
207
+ end
208
+
209
+ it "looks up symbols in Rack's mime types dictionary" do
210
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
211
+ mock_app {
212
+ get '/foo.xml' do
213
+ content_type :foo
214
+ "I AM FOO"
215
+ end
216
+ }
217
+
218
+ get '/foo.xml'
219
+ should.be.ok
220
+ response['Content-Type'].should.equal 'application/foo'
221
+ body.should.equal 'I AM FOO'
222
+ end
223
+
224
+ it 'fails when no mime type is registered for the argument provided' do
225
+ mock_app {
226
+ get '/foo.xml' do
227
+ content_type :bizzle
228
+ "I AM FOO"
229
+ end
230
+ }
231
+
232
+ lambda { get '/foo.xml' }.should.raise RuntimeError
233
+ end
234
+ end
235
+
236
+ describe '#send_file' do
237
+ before {
238
+ @file = File.dirname(__FILE__) + '/file.txt'
239
+ File.open(@file, 'wb') { |io| io.write('Hello World') }
240
+ }
241
+ after {
242
+ File.unlink @file
243
+ @file = nil
244
+ }
245
+
246
+ def send_file_app
247
+ path = @file
248
+ mock_app {
249
+ get '/file.txt' do
250
+ send_file path
251
+ end
252
+ }
253
+ end
254
+
255
+ it "sends the contents of the file" do
256
+ send_file_app
257
+ get '/file.txt'
258
+ should.be.ok
259
+ body.should.equal 'Hello World'
260
+ end
261
+
262
+ it 'sets the Content-Type response header if a mime-type can be located' do
263
+ send_file_app
264
+ get '/file.txt'
265
+ response['Content-Type'].should.equal 'text/plain'
266
+ end
267
+
268
+ it 'sets the Content-Length response header' do
269
+ send_file_app
270
+ get '/file.txt'
271
+ response['Content-Length'].should.equal 'Hello World'.length.to_s
272
+ end
273
+
274
+ it 'sets the Last-Modified response header' do
275
+ send_file_app
276
+ get '/file.txt'
277
+ response['Last-Modified'].should.equal File.mtime(@file).httpdate
278
+ end
279
+
280
+ it "returns a 404 when not found" do
281
+ mock_app {
282
+ get '/' do
283
+ send_file 'this-file-does-not-exist.txt'
284
+ end
285
+ }
286
+ get '/'
287
+ should.be.not_found
288
+ end
289
+ end
290
+
291
+ describe '#last_modified' do
292
+ before do
293
+ now = Time.now
294
+ mock_app {
295
+ get '/' do
296
+ body { 'Hello World' }
297
+ last_modified now
298
+ 'Boo!'
299
+ end
300
+ }
301
+ @now = now
302
+ end
303
+
304
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
305
+ get '/'
306
+ response['Last-Modified'].should.equal @now.httpdate
307
+ end
308
+
309
+ it 'returns a body when conditional get misses' do
310
+ get '/'
311
+ status.should.be 200
312
+ body.should.equal 'Boo!'
313
+ end
314
+
315
+ it 'halts when a conditional GET matches' do
316
+ get '/', :env => { 'HTTP_IF_MODIFIED_SINCE' => @now.httpdate }
317
+ status.should.be 304
318
+ body.should.be.empty
319
+ end
320
+ end
321
+
322
+ describe '#etag' do
323
+ before do
324
+ mock_app {
325
+ get '/' do
326
+ body { 'Hello World' }
327
+ etag 'FOO'
328
+ 'Boo!'
329
+ end
330
+ }
331
+ end
332
+
333
+ it 'sets the ETag header' do
334
+ get '/'
335
+ response['ETag'].should.equal '"FOO"'
336
+ end
337
+
338
+ it 'returns a body when conditional get misses' do
339
+ get '/'
340
+ status.should.be 200
341
+ body.should.equal 'Boo!'
342
+ end
343
+
344
+ it 'halts when a conditional GET matches' do
345
+ get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
346
+ status.should.be 304
347
+ body.should.be.empty
348
+ end
349
+
350
+ it 'should handle multiple ETag values in If-None-Match header' do
351
+ get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
352
+ status.should.be 304
353
+ body.should.be.empty
354
+ end
355
+
356
+ it 'uses a weak etag with the :weak option' do
357
+ mock_app {
358
+ get '/' do
359
+ etag 'FOO', :weak
360
+ "that's weak, dude."
361
+ end
362
+ }
363
+ get '/'
364
+ response['ETag'].should.equal 'W/"FOO"'
365
+ end
366
+
367
+ end
368
+ end
@@ -0,0 +1,164 @@
1
+ require 'test/spec'
2
+ require 'sinatra/base'
3
+ require 'sinatra/test'
4
+
5
+ describe 'Exception Mappings' do
6
+ include Sinatra::Test
7
+
8
+ class FooError < RuntimeError
9
+ end
10
+
11
+ it 'invokes handlers registered with ::error when raised' do
12
+ mock_app {
13
+ set :raise_errors, false
14
+ error(FooError) { 'Foo!' }
15
+ get '/' do
16
+ raise FooError
17
+ end
18
+ }
19
+ get '/'
20
+ status.should.equal 500
21
+ body.should.equal 'Foo!'
22
+ end
23
+
24
+ it 'uses the Exception handler if no matching handler found' do
25
+ mock_app {
26
+ set :raise_errors, false
27
+ error(Exception) { 'Exception!' }
28
+ get '/' do
29
+ raise FooError
30
+ end
31
+ }
32
+ get '/'
33
+ status.should.equal 500
34
+ body.should.equal 'Exception!'
35
+ end
36
+
37
+ it "sets env['sinatra.error'] to the rescued exception" do
38
+ mock_app {
39
+ set :raise_errors, false
40
+ error(FooError) {
41
+ env.should.include 'sinatra.error'
42
+ env['sinatra.error'].should.be.kind_of FooError
43
+ 'looks good'
44
+ }
45
+ get '/' do
46
+ raise FooError
47
+ end
48
+ }
49
+ get '/'
50
+ body.should.equal 'looks good'
51
+ end
52
+
53
+ it 'dumps errors to rack.errors when dump_errors is enabled' do
54
+ mock_app {
55
+ set :raise_errors, false
56
+ set :dump_errors, true
57
+ get('/') { raise FooError, 'BOOM!' }
58
+ }
59
+
60
+ get '/'
61
+ status.should.equal 500
62
+ @response.errors.should.match(/FooError - BOOM!:/)
63
+ end
64
+
65
+ it "raises without calling the handler when the raise_errors options is set" do
66
+ mock_app {
67
+ set :raise_errors, true
68
+ error(FooError) { "she's not there." }
69
+ get '/' do
70
+ raise FooError
71
+ end
72
+ }
73
+ lambda { get '/' }.should.raise FooError
74
+ end
75
+
76
+ it "never raises Sinatra::NotFound beyond the application" do
77
+ mock_app {
78
+ set :raise_errors, true
79
+ get '/' do
80
+ raise Sinatra::NotFound
81
+ end
82
+ }
83
+ lambda { get '/' }.should.not.raise Sinatra::NotFound
84
+ status.should.equal 404
85
+ end
86
+
87
+ class FooNotFound < Sinatra::NotFound
88
+ end
89
+
90
+ it "cascades for subclasses of Sinatra::NotFound" do
91
+ mock_app {
92
+ set :raise_errors, true
93
+ error(FooNotFound) { "foo! not found." }
94
+ get '/' do
95
+ raise FooNotFound
96
+ end
97
+ }
98
+ lambda { get '/' }.should.not.raise FooNotFound
99
+ status.should.equal 404
100
+ body.should.equal 'foo! not found.'
101
+ end
102
+
103
+ it 'has a not_found method for backwards compatibility' do
104
+ mock_app {
105
+ not_found do
106
+ "Lost, are we?"
107
+ end
108
+ }
109
+
110
+ get '/test'
111
+ status.should.equal 404
112
+ body.should.equal "Lost, are we?"
113
+ end
114
+ end
115
+
116
+ describe 'Custom Error Pages' do
117
+ it 'allows numeric status code mappings to be registered with ::error' do
118
+ mock_app {
119
+ set :raise_errors, false
120
+ error(500) { 'Foo!' }
121
+ get '/' do
122
+ [500, {}, 'Internal Foo Error']
123
+ end
124
+ }
125
+ get '/'
126
+ status.should.equal 500
127
+ body.should.equal 'Foo!'
128
+ end
129
+
130
+ it 'allows ranges of status code mappings to be registered with :error' do
131
+ mock_app {
132
+ set :raise_errors, false
133
+ error(500..550) { "Error: #{response.status}" }
134
+ get '/' do
135
+ [507, {}, 'A very special error']
136
+ end
137
+ }
138
+ get '/'
139
+ status.should.equal 507
140
+ body.should.equal 'Error: 507'
141
+ end
142
+
143
+ class FooError < RuntimeError
144
+ end
145
+
146
+ it 'runs after exception mappings and overwrites body' do
147
+ mock_app {
148
+ set :raise_errors, false
149
+ error FooError do
150
+ response.status = 502
151
+ 'from exception mapping'
152
+ end
153
+ error(500) { 'from 500 handler' }
154
+ error(502) { 'from custom error page' }
155
+
156
+ get '/' do
157
+ raise FooError
158
+ end
159
+ }
160
+ get '/'
161
+ status.should.equal 502
162
+ body.should.equal 'from custom error page'
163
+ end
164
+ end