aldebaran 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +13 -0
  2. data/.travis.yml +16 -0
  3. data/.yardopts +4 -0
  4. data/AUTHORS +4 -0
  5. data/Gemfile +77 -0
  6. data/KNOWN_ISSUES +5 -0
  7. data/LICENSE +22 -0
  8. data/README.rdoc +1900 -0
  9. data/Rakefile +175 -0
  10. data/aldebaran.gemspec +19 -0
  11. data/lib/aldebaran.rb +7 -0
  12. data/lib/aldebaran/base.rb +1600 -0
  13. data/lib/aldebaran/images/404.png +0 -0
  14. data/lib/aldebaran/images/500.png +0 -0
  15. data/lib/aldebaran/main.rb +28 -0
  16. data/lib/aldebaran/showexceptions.rb +340 -0
  17. data/lib/aldebaran/version.rb +3 -0
  18. data/test/aldebaran_test.rb +17 -0
  19. data/test/base_test.rb +160 -0
  20. data/test/builder_test.rb +95 -0
  21. data/test/coffee_test.rb +92 -0
  22. data/test/contest.rb +98 -0
  23. data/test/creole_test.rb +65 -0
  24. data/test/delegator_test.rb +162 -0
  25. data/test/encoding_test.rb +20 -0
  26. data/test/erb_test.rb +104 -0
  27. data/test/extensions_test.rb +100 -0
  28. data/test/filter_test.rb +397 -0
  29. data/test/haml_test.rb +101 -0
  30. data/test/helper.rb +115 -0
  31. data/test/helpers_test.rb +1192 -0
  32. data/test/less_test.rb +67 -0
  33. data/test/liquid_test.rb +59 -0
  34. data/test/mapped_error_test.rb +259 -0
  35. data/test/markaby_test.rb +80 -0
  36. data/test/markdown_test.rb +81 -0
  37. data/test/middleware_test.rb +68 -0
  38. data/test/nokogiri_test.rb +69 -0
  39. data/test/public/favicon.ico +0 -0
  40. data/test/radius_test.rb +59 -0
  41. data/test/rdoc_test.rb +65 -0
  42. data/test/readme_test.rb +136 -0
  43. data/test/request_test.rb +45 -0
  44. data/test/response_test.rb +61 -0
  45. data/test/result_test.rb +98 -0
  46. data/test/route_added_hook_test.rb +59 -0
  47. data/test/routing_test.rb +1096 -0
  48. data/test/sass_test.rb +115 -0
  49. data/test/scss_test.rb +88 -0
  50. data/test/server_test.rb +48 -0
  51. data/test/settings_test.rb +493 -0
  52. data/test/slim_test.rb +98 -0
  53. data/test/static_test.rb +178 -0
  54. data/test/streaming_test.rb +100 -0
  55. data/test/templates_test.rb +298 -0
  56. data/test/textile_test.rb +65 -0
  57. data/test/views/a/in_a.str +1 -0
  58. data/test/views/ascii.erb +2 -0
  59. data/test/views/b/in_b.str +1 -0
  60. data/test/views/calc.html.erb +1 -0
  61. data/test/views/error.builder +3 -0
  62. data/test/views/error.erb +3 -0
  63. data/test/views/error.haml +3 -0
  64. data/test/views/error.sass +2 -0
  65. data/test/views/explicitly_nested.str +1 -0
  66. data/test/views/foo/hello.test +1 -0
  67. data/test/views/hello.builder +1 -0
  68. data/test/views/hello.coffee +1 -0
  69. data/test/views/hello.creole +1 -0
  70. data/test/views/hello.erb +1 -0
  71. data/test/views/hello.haml +1 -0
  72. data/test/views/hello.less +5 -0
  73. data/test/views/hello.liquid +1 -0
  74. data/test/views/hello.mab +1 -0
  75. data/test/views/hello.md +1 -0
  76. data/test/views/hello.nokogiri +1 -0
  77. data/test/views/hello.radius +1 -0
  78. data/test/views/hello.rdoc +1 -0
  79. data/test/views/hello.sass +2 -0
  80. data/test/views/hello.scss +3 -0
  81. data/test/views/hello.slim +1 -0
  82. data/test/views/hello.str +1 -0
  83. data/test/views/hello.test +1 -0
  84. data/test/views/hello.textile +1 -0
  85. data/test/views/layout2.builder +3 -0
  86. data/test/views/layout2.erb +2 -0
  87. data/test/views/layout2.haml +2 -0
  88. data/test/views/layout2.liquid +2 -0
  89. data/test/views/layout2.mab +2 -0
  90. data/test/views/layout2.nokogiri +3 -0
  91. data/test/views/layout2.radius +2 -0
  92. data/test/views/layout2.slim +3 -0
  93. data/test/views/layout2.str +2 -0
  94. data/test/views/layout2.test +1 -0
  95. data/test/views/nested.str +1 -0
  96. data/test/views/utf8.erb +2 -0
  97. metadata +231 -0
@@ -0,0 +1,101 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ begin
4
+ require 'haml'
5
+
6
+ class HAMLTest < Test::Unit::TestCase
7
+ def haml_app(&block)
8
+ mock_app {
9
+ set :views, File.dirname(__FILE__) + '/views'
10
+ get '/', &block
11
+ }
12
+ get '/'
13
+ end
14
+
15
+ it 'renders inline HAML strings' do
16
+ haml_app { haml '%h1 Hiya' }
17
+ assert ok?
18
+ assert_equal "<h1>Hiya</h1>\n", body
19
+ end
20
+
21
+ it 'renders .haml files in views path' do
22
+ haml_app { haml :hello }
23
+ assert ok?
24
+ assert_equal "<h1>Hello From Haml</h1>\n", body
25
+ end
26
+
27
+ it "renders with inline layouts" do
28
+ mock_app {
29
+ layout { %q(%h1= 'THIS. IS. ' + yield.upcase) }
30
+ get('/') { haml '%em Sparta' }
31
+ }
32
+ get '/'
33
+ assert ok?
34
+ assert_equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>\n", body
35
+ end
36
+
37
+ it "renders with file layouts" do
38
+ haml_app {
39
+ haml 'Hello World', :layout => :layout2
40
+ }
41
+ assert ok?
42
+ assert_equal "<h1>HAML Layout!</h1>\n<p>Hello World</p>\n", body
43
+ end
44
+
45
+ it "raises error if template not found" do
46
+ mock_app {
47
+ get('/') { haml :no_such_template }
48
+ }
49
+ assert_raise(Errno::ENOENT) { get('/') }
50
+ end
51
+
52
+ it "passes HAML options to the Haml engine" do
53
+ mock_app {
54
+ get '/' do
55
+ haml "!!!\n%h1 Hello World", :format => :html5
56
+ end
57
+ }
58
+ get '/'
59
+ assert ok?
60
+ assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
61
+ end
62
+
63
+ it "passes default HAML options to the Haml engine" do
64
+ mock_app {
65
+ set :haml, {:format => :html5}
66
+ get '/' do
67
+ haml "!!!\n%h1 Hello World"
68
+ end
69
+ }
70
+ get '/'
71
+ assert ok?
72
+ assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
73
+ end
74
+
75
+ it "merges the default HAML options with the overrides and passes them to the Haml engine" do
76
+ mock_app {
77
+ set :haml, {:format => :html5, :attr_wrapper => '"'} # default HAML attr are <tag attr='single-quoted'>
78
+ get '/' do
79
+ haml "!!!\n%h1{:class => :header} Hello World"
80
+ end
81
+ get '/html4' do
82
+ haml "!!!\n%h1{:class => 'header'} Hello World", :format => :html4
83
+ end
84
+ }
85
+ get '/'
86
+ assert ok?
87
+ assert_equal "<!DOCTYPE html>\n<h1 class=\"header\">Hello World</h1>\n", body
88
+ get '/html4'
89
+ assert ok?
90
+ assert_match(/^<!DOCTYPE html PUBLIC (.*) HTML 4.01/, body)
91
+ end
92
+
93
+ it "is possible to pass locals" do
94
+ haml_app { haml "= foo", :locals => { :foo => 'bar' }}
95
+ assert_equal "bar\n", body
96
+ end
97
+ end
98
+
99
+ rescue LoadError
100
+ warn "#{$!.to_s}: skipping haml tests"
101
+ end
@@ -0,0 +1,115 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+ Encoding.default_external = "UTF-8" if defined? Encoding
3
+
4
+ RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
5
+
6
+ begin
7
+ require 'rack'
8
+ rescue LoadError
9
+ require 'rubygems'
10
+ require 'rack'
11
+ end
12
+
13
+ testdir = File.dirname(__FILE__)
14
+ $LOAD_PATH.unshift testdir unless $LOAD_PATH.include?(testdir)
15
+
16
+ libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
17
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
18
+
19
+ require 'contest'
20
+ require 'rack/test'
21
+ require 'aldebaran/base'
22
+
23
+ class aldebaran::Base
24
+ # Allow assertions in request context
25
+ include Test::Unit::Assertions
26
+ end
27
+
28
+ class Rack::Builder
29
+ def include?(middleware)
30
+ @ins.any? { |m| p m ; middleware === m }
31
+ end
32
+ end
33
+
34
+ aldebaran::Base.set :environment, :test
35
+
36
+ class Test::Unit::TestCase
37
+ include Rack::Test::Methods
38
+
39
+ class << self
40
+ alias_method :it, :test
41
+ alias_method :section, :context
42
+ end
43
+
44
+ def self.example(desc = nil, &block)
45
+ @example_count = 0 unless instance_variable_defined? :@example_count
46
+ @example_count += 1
47
+ it(desc || "Example #{@example_count}", &block)
48
+ end
49
+
50
+ alias_method :response, :last_response
51
+
52
+ setup do
53
+ aldebaran::Base.set :environment, :test
54
+ end
55
+
56
+ # Sets up a aldebaran::Base subclass defined with the block
57
+ # given. Used in setup or individual spec methods to establish
58
+ # the application.
59
+ def mock_app(base=aldebaran::Base, &block)
60
+ @app = aldebaran.new(base, &block)
61
+ end
62
+
63
+ def app
64
+ Rack::Lint.new(@app)
65
+ end
66
+
67
+ def body
68
+ response.body.to_s
69
+ end
70
+
71
+ def assert_body(value)
72
+ assert_equal value.lstrip.gsub(/\s*\n\s*/, ""), body.lstrip.gsub(/\s*\n\s*/, "")
73
+ end
74
+
75
+ def assert_like(a,b)
76
+ pattern = /id=['"][^"']*["']|\s+/
77
+ assert_equal a.strip.gsub(pattern, ""), b.strip.gsub(pattern, "")
78
+ end
79
+
80
+ def assert_include(str, substr)
81
+ assert str.include?(substr), "expected #{str.inspect} to include #{substr.inspect}"
82
+ end
83
+
84
+ def options(uri, params = {}, env = {}, &block)
85
+ request(uri, env.merge(:method => "OPTIONS", :params => params), &block)
86
+ end
87
+
88
+ def patch(uri, params = {}, env = {}, &block)
89
+ request(uri, env.merge(:method => "PATCH", :params => params), &block)
90
+ end
91
+
92
+ # Delegate other missing methods to response.
93
+ def method_missing(name, *args, &block)
94
+ if response && response.respond_to?(name)
95
+ response.send(name, *args, &block)
96
+ else
97
+ super
98
+ end
99
+ rescue Rack::Test::Error
100
+ super
101
+ end
102
+
103
+ # Also check response since we delegate there.
104
+ def respond_to?(symbol, include_private=false)
105
+ super || (response && response.respond_to?(symbol, include_private))
106
+ end
107
+
108
+ # Do not output warnings for the duration of the block.
109
+ def silence_warnings
110
+ $VERBOSE, v = nil, $VERBOSE
111
+ yield
112
+ ensure
113
+ $VERBOSE = v
114
+ end
115
+ end
@@ -0,0 +1,1192 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+ require 'date'
3
+
4
+ class HelpersTest < Test::Unit::TestCase
5
+ def test_default
6
+ assert true
7
+ end
8
+
9
+ def status_app(code, &block)
10
+ code += 2 if [204, 205, 304].include? code
11
+ block ||= proc { }
12
+ mock_app do
13
+ get '/' do
14
+ status code
15
+ instance_eval(&block).inspect
16
+ end
17
+ end
18
+ get '/'
19
+ end
20
+
21
+ describe 'status' do
22
+ it 'sets the response status code' do
23
+ status_app 207
24
+ assert_equal 207, response.status
25
+ end
26
+ end
27
+
28
+ describe 'not_found?' do
29
+ it 'is true for status == 404' do
30
+ status_app(404) { not_found? }
31
+ assert_body 'true'
32
+ end
33
+
34
+ it 'is false for status > 404' do
35
+ status_app(405) { not_found? }
36
+ assert_body 'false'
37
+ end
38
+
39
+ it 'is false for status < 404' do
40
+ status_app(403) { not_found? }
41
+ assert_body 'false'
42
+ end
43
+ end
44
+
45
+ describe 'informational?' do
46
+ it 'is true for 1xx status' do
47
+ status_app(100 + rand(100)) { informational? }
48
+ assert_body 'true'
49
+ end
50
+
51
+ it 'is false for status > 199' do
52
+ status_app(200 + rand(400)) { informational? }
53
+ assert_body 'false'
54
+ end
55
+ end
56
+
57
+ describe 'success?' do
58
+ it 'is true for 2xx status' do
59
+ status_app(200 + rand(100)) { success? }
60
+ assert_body 'true'
61
+ end
62
+
63
+ it 'is false for status < 200' do
64
+ status_app(100 + rand(100)) { success? }
65
+ assert_body 'false'
66
+ end
67
+
68
+ it 'is false for status > 299' do
69
+ status_app(300 + rand(300)) { success? }
70
+ assert_body 'false'
71
+ end
72
+ end
73
+
74
+ describe 'redirect?' do
75
+ it 'is true for 3xx status' do
76
+ status_app(300 + rand(100)) { redirect? }
77
+ assert_body 'true'
78
+ end
79
+
80
+ it 'is false for status < 300' do
81
+ status_app(200 + rand(100)) { redirect? }
82
+ assert_body 'false'
83
+ end
84
+
85
+ it 'is false for status > 399' do
86
+ status_app(400 + rand(200)) { redirect? }
87
+ assert_body 'false'
88
+ end
89
+ end
90
+
91
+ describe 'client_error?' do
92
+ it 'is true for 4xx status' do
93
+ status_app(400 + rand(100)) { client_error? }
94
+ assert_body 'true'
95
+ end
96
+
97
+ it 'is false for status < 400' do
98
+ status_app(200 + rand(200)) { client_error? }
99
+ assert_body 'false'
100
+ end
101
+
102
+ it 'is false for status > 499' do
103
+ status_app(500 + rand(100)) { client_error? }
104
+ assert_body 'false'
105
+ end
106
+ end
107
+
108
+ describe 'server_error?' do
109
+ it 'is true for 5xx status' do
110
+ status_app(500 + rand(100)) { server_error? }
111
+ assert_body 'true'
112
+ end
113
+
114
+ it 'is false for status < 500' do
115
+ status_app(200 + rand(300)) { server_error? }
116
+ assert_body 'false'
117
+ end
118
+ end
119
+
120
+ describe 'body' do
121
+ it 'takes a block for defered body generation' do
122
+ mock_app {
123
+ get '/' do
124
+ body { 'Hello World' }
125
+ end
126
+ }
127
+
128
+ get '/'
129
+ assert_equal 'Hello World', body
130
+ end
131
+
132
+ it 'takes a String, Array, or other object responding to #each' do
133
+ mock_app {
134
+ get '/' do
135
+ body 'Hello World'
136
+ end
137
+ }
138
+
139
+ get '/'
140
+ assert_equal 'Hello World', body
141
+ end
142
+ end
143
+
144
+ describe 'redirect' do
145
+ it 'uses a 302 when only a path is given' do
146
+ mock_app {
147
+ get '/' do
148
+ redirect '/foo'
149
+ fail 'redirect should halt'
150
+ end
151
+ }
152
+
153
+ get '/'
154
+ assert_equal 302, status
155
+ assert_equal '', body
156
+ assert_equal 'http://example.org/foo', response['Location']
157
+ end
158
+
159
+ it 'uses the code given when specified' do
160
+ mock_app {
161
+ get '/' do
162
+ redirect '/foo', 301
163
+ fail 'redirect should halt'
164
+ end
165
+ }
166
+
167
+ get '/'
168
+ assert_equal 301, status
169
+ assert_equal '', body
170
+ assert_equal 'http://example.org/foo', response['Location']
171
+ end
172
+
173
+ it 'redirects back to request.referer when passed back' do
174
+ mock_app {
175
+ get '/try_redirect' do
176
+ redirect back
177
+ end
178
+ }
179
+
180
+ request = Rack::MockRequest.new(@app)
181
+ response = request.get('/try_redirect', 'HTTP_REFERER' => '/foo')
182
+ assert_equal 302, response.status
183
+ assert_equal 'http://example.org/foo', response['Location']
184
+ end
185
+
186
+ it 'redirects using a non-standard HTTP port' do
187
+ mock_app {
188
+ get '/' do
189
+ redirect '/foo'
190
+ end
191
+ }
192
+
193
+ request = Rack::MockRequest.new(@app)
194
+ response = request.get('/', 'SERVER_PORT' => '81')
195
+ assert_equal 'http://example.org:81/foo', response['Location']
196
+ end
197
+
198
+ it 'redirects using a non-standard HTTPS port' do
199
+ mock_app {
200
+ get '/' do
201
+ redirect '/foo'
202
+ end
203
+ }
204
+
205
+ request = Rack::MockRequest.new(@app)
206
+ response = request.get('/', 'SERVER_PORT' => '444')
207
+ assert_equal 'http://example.org:444/foo', response['Location']
208
+ end
209
+
210
+ it 'uses 303 for post requests if request is HTTP 1.1' do
211
+ mock_app { post('/') { redirect '/'} }
212
+ post '/', {}, 'HTTP_VERSION' => 'HTTP/1.1'
213
+ assert_equal 303, status
214
+ assert_equal '', body
215
+ assert_equal 'http://example.org/', response['Location']
216
+ end
217
+
218
+ it 'uses 302 for post requests if request is HTTP 1.0' do
219
+ mock_app { post('/') { redirect '/'} }
220
+ post '/', {}, 'HTTP_VERSION' => 'HTTP/1.0'
221
+ assert_equal 302, status
222
+ assert_equal '', body
223
+ assert_equal 'http://example.org/', response['Location']
224
+ end
225
+
226
+ it 'works behind a reverse proxy' do
227
+ mock_app do
228
+ get '/' do
229
+ redirect '/foo'
230
+ end
231
+ end
232
+
233
+ request = Rack::MockRequest.new(@app)
234
+ response = request.get('/', 'HTTP_X_FORWARDED_HOST' => 'example.com', 'SERVER_PORT' => '8080')
235
+ assert_equal 'http://example.com/foo', response['Location']
236
+ end
237
+
238
+ it 'accepts absolute URIs' do
239
+ mock_app do
240
+ get '/' do
241
+ redirect 'http://google.com'
242
+ fail 'redirect should halt'
243
+ end
244
+ end
245
+
246
+ get '/'
247
+ assert_equal 302, status
248
+ assert_equal '', body
249
+ assert_equal 'http://google.com', response['Location']
250
+ end
251
+
252
+ it 'accepts absolute URIs with a different schema' do
253
+ mock_app do
254
+ get '/' do
255
+ redirect 'mailto:jsmith@example.com'
256
+ fail 'redirect should halt'
257
+ end
258
+ end
259
+
260
+ get '/'
261
+ assert_equal 302, status
262
+ assert_equal '', body
263
+ assert_equal 'mailto:jsmith@example.com', response['Location']
264
+ end
265
+ end
266
+
267
+ describe 'error' do
268
+ it 'sets a status code and halts' do
269
+ mock_app {
270
+ get '/' do
271
+ error 501
272
+ fail 'error should halt'
273
+ end
274
+ }
275
+
276
+ get '/'
277
+ assert_equal 501, status
278
+ assert_equal '', body
279
+ end
280
+
281
+ it 'takes an optional body' do
282
+ mock_app {
283
+ get '/' do
284
+ error 501, 'FAIL'
285
+ fail 'error should halt'
286
+ end
287
+ }
288
+
289
+ get '/'
290
+ assert_equal 501, status
291
+ assert_equal 'FAIL', body
292
+ end
293
+
294
+ it 'uses a 500 status code when first argument is a body' do
295
+ mock_app {
296
+ get '/' do
297
+ error 'FAIL'
298
+ fail 'error should halt'
299
+ end
300
+ }
301
+
302
+ get '/'
303
+ assert_equal 500, status
304
+ assert_equal 'FAIL', body
305
+ end
306
+ end
307
+
308
+ describe 'not_found' do
309
+ it 'halts with a 404 status' do
310
+ mock_app {
311
+ get '/' do
312
+ not_found
313
+ fail 'not_found should halt'
314
+ end
315
+ }
316
+
317
+ get '/'
318
+ assert_equal 404, status
319
+ assert_equal '', body
320
+ end
321
+
322
+ it 'does not set a X-Cascade header' do
323
+ mock_app {
324
+ get '/' do
325
+ not_found
326
+ fail 'not_found should halt'
327
+ end
328
+ }
329
+
330
+ get '/'
331
+ assert_equal 404, status
332
+ assert_equal nil, response.headers['X-Cascade']
333
+ end
334
+ end
335
+
336
+ describe 'headers' do
337
+ it 'sets headers on the response object when given a Hash' do
338
+ mock_app {
339
+ get '/' do
340
+ headers 'X-Foo' => 'bar', 'X-Baz' => 'bling'
341
+ 'kthx'
342
+ end
343
+ }
344
+
345
+ get '/'
346
+ assert ok?
347
+ assert_equal 'bar', response['X-Foo']
348
+ assert_equal 'bling', response['X-Baz']
349
+ assert_equal 'kthx', body
350
+ end
351
+
352
+ it 'returns the response headers hash when no hash provided' do
353
+ mock_app {
354
+ get '/' do
355
+ headers['X-Foo'] = 'bar'
356
+ 'kthx'
357
+ end
358
+ }
359
+
360
+ get '/'
361
+ assert ok?
362
+ assert_equal 'bar', response['X-Foo']
363
+ end
364
+ end
365
+
366
+ describe 'session' do
367
+ it 'uses the existing rack.session' do
368
+ mock_app {
369
+ get '/' do
370
+ session[:foo]
371
+ end
372
+ }
373
+
374
+ get '/', {}, { 'rack.session' => { :foo => 'bar' } }
375
+ assert_equal 'bar', body
376
+ end
377
+
378
+ it 'creates a new session when none provided' do
379
+ mock_app {
380
+ enable :sessions
381
+
382
+ get '/' do
383
+ assert session.empty?
384
+ session[:foo] = 'bar'
385
+ redirect '/hi'
386
+ end
387
+
388
+ get '/hi' do
389
+ "hi #{session[:foo]}"
390
+ end
391
+ }
392
+
393
+ get '/'
394
+ follow_redirect!
395
+ assert_equal 'hi bar', body
396
+ end
397
+
398
+ it 'inserts session middleware' do
399
+ mock_app do
400
+ enable :sessions
401
+ get '/' do
402
+ assert env['rack.session']
403
+ assert env['rack.session.options']
404
+ 'ok'
405
+ end
406
+ end
407
+
408
+ get '/'
409
+ assert_body 'ok'
410
+ end
411
+
412
+ it 'sets a default session secret' do
413
+ mock_app do
414
+ enable :sessions
415
+ get '/' do
416
+ secret = env['rack.session.options'][:secret]
417
+ assert secret
418
+ assert_equal secret, settings.session_secret
419
+ 'ok'
420
+ end
421
+ end
422
+
423
+ get '/'
424
+ assert_body 'ok'
425
+ end
426
+
427
+ it 'allows disabling session secret' do
428
+ mock_app do
429
+ enable :sessions
430
+ disable :session_secret
431
+ get '/' do
432
+ assert !env['rack.session.options'].include?(:session_secret)
433
+ 'ok'
434
+ end
435
+ end
436
+
437
+ get '/'
438
+ assert_body 'ok'
439
+ end
440
+
441
+ it 'accepts an options hash' do
442
+ mock_app do
443
+ set :sessions, :foo => :bar
444
+ get '/' do
445
+ assert_equal env['rack.session.options'][:foo], :bar
446
+ 'ok'
447
+ end
448
+ end
449
+
450
+ get '/'
451
+ assert_body 'ok'
452
+ end
453
+ end
454
+
455
+ describe 'mime_type' do
456
+ include aldebaran::Helpers
457
+
458
+ it "looks up mime types in Rack's MIME registry" do
459
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
460
+ assert_equal 'application/foo', mime_type('foo')
461
+ assert_equal 'application/foo', mime_type('.foo')
462
+ assert_equal 'application/foo', mime_type(:foo)
463
+ end
464
+
465
+ it 'returns nil when given nil' do
466
+ assert mime_type(nil).nil?
467
+ end
468
+
469
+ it 'returns nil when media type not registered' do
470
+ assert mime_type(:bizzle).nil?
471
+ end
472
+
473
+ it 'returns the argument when given a media type string' do
474
+ assert_equal 'text/plain', mime_type('text/plain')
475
+ end
476
+ end
477
+
478
+ test 'Base.mime_type registers mime type' do
479
+ mock_app {
480
+ mime_type :foo, 'application/foo'
481
+
482
+ get '/' do
483
+ "foo is #{mime_type(:foo)}"
484
+ end
485
+ }
486
+
487
+ get '/'
488
+ assert_equal 'foo is application/foo', body
489
+ end
490
+
491
+ describe 'content_type' do
492
+ it 'sets the Content-Type header' do
493
+ mock_app {
494
+ get '/' do
495
+ content_type 'text/plain'
496
+ 'Hello World'
497
+ end
498
+ }
499
+
500
+ get '/'
501
+ assert_equal 'text/plain;charset=utf-8', response['Content-Type']
502
+ assert_equal 'Hello World', body
503
+ end
504
+
505
+ it 'takes media type parameters (like charset=)' do
506
+ mock_app {
507
+ get '/' do
508
+ content_type 'text/html', :charset => 'latin1'
509
+ "<h1>Hello, World</h1>"
510
+ end
511
+ }
512
+
513
+ get '/'
514
+ assert ok?
515
+ assert_equal 'text/html;charset=latin1', response['Content-Type']
516
+ assert_equal "<h1>Hello, World</h1>", body
517
+ end
518
+
519
+ it "looks up symbols in Rack's mime types dictionary" do
520
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
521
+ mock_app {
522
+ get '/foo.xml' do
523
+ content_type :foo
524
+ "I AM FOO"
525
+ end
526
+ }
527
+
528
+ get '/foo.xml'
529
+ assert ok?
530
+ assert_equal 'application/foo', response['Content-Type']
531
+ assert_equal 'I AM FOO', body
532
+ end
533
+
534
+ it 'fails when no mime type is registered for the argument provided' do
535
+ mock_app {
536
+ get '/foo.xml' do
537
+ content_type :bizzle
538
+ "I AM FOO"
539
+ end
540
+ }
541
+
542
+ assert_raise(RuntimeError) { get '/foo.xml' }
543
+ end
544
+
545
+ it 'only sets default charset for specific mime types' do
546
+ tests_ran = false
547
+ mock_app do
548
+ mime_type :foo, 'text/foo'
549
+ mime_type :bar, 'application/bar'
550
+ mime_type :baz, 'application/baz'
551
+ add_charset << mime_type(:baz)
552
+ get '/' do
553
+ assert_equal content_type(:txt), 'text/plain;charset=utf-8'
554
+ assert_equal content_type(:css), 'text/css;charset=utf-8'
555
+ assert_equal content_type(:html), 'text/html;charset=utf-8'
556
+ assert_equal content_type(:foo), 'text/foo;charset=utf-8'
557
+ assert_equal content_type(:xml), 'application/xml;charset=utf-8'
558
+ assert_equal content_type(:xhtml), 'application/xhtml+xml;charset=utf-8'
559
+ assert_equal content_type(:js), 'application/javascript;charset=utf-8'
560
+ assert_equal content_type(:json), 'application/json;charset=utf-8'
561
+ assert_equal content_type(:bar), 'application/bar'
562
+ assert_equal content_type(:png), 'image/png'
563
+ assert_equal content_type(:baz), 'application/baz;charset=utf-8'
564
+ tests_ran = true
565
+ "done"
566
+ end
567
+ end
568
+ get '/'
569
+ assert tests_ran
570
+ end
571
+
572
+ it 'handles already present params' do
573
+ mock_app do
574
+ get '/' do
575
+ content_type 'foo/bar;level=1', :charset => 'utf-8'
576
+ 'ok'
577
+ end
578
+ end
579
+ get '/'
580
+ assert_equal 'foo/bar;level=1, charset=utf-8', response['Content-Type']
581
+ end
582
+
583
+ it 'does not add charset if present' do
584
+ mock_app do
585
+ get '/' do
586
+ content_type 'text/plain;charset=utf-16'
587
+ 'ok'
588
+ end
589
+ end
590
+ get '/'
591
+ assert_equal 'text/plain;charset=utf-16', response['Content-Type']
592
+ end
593
+ end
594
+
595
+ describe 'attachment' do
596
+ def attachment_app(filename=nil)
597
+ mock_app {
598
+ get '/attachment' do
599
+ attachment filename
600
+ response.write("<aldebaran></aldebaran>")
601
+ end
602
+ }
603
+ end
604
+
605
+ it 'sets the Content-Type response header' do
606
+ attachment_app('test.xml')
607
+ get '/attachment'
608
+ assert_equal 'application/xml;charset=utf-8', response['Content-Type']
609
+ assert_equal '<aldebaran></aldebaran>', body
610
+ end
611
+
612
+ it 'sets the Content-Type response header without extname' do
613
+ attachment_app('test')
614
+ get '/attachment'
615
+ assert_equal 'text/html;charset=utf-8', response['Content-Type']
616
+ assert_equal '<aldebaran></aldebaran>', body
617
+ end
618
+
619
+ it 'sets the Content-Type response header without extname' do
620
+ mock_app do
621
+ get '/attachment' do
622
+ content_type :atom
623
+ attachment 'test.xml'
624
+ response.write("<aldebaran></aldebaran>")
625
+ end
626
+ end
627
+ get '/attachment'
628
+ assert_equal 'application/atom+xml', response['Content-Type']
629
+ assert_equal '<aldebaran></aldebaran>', body
630
+ end
631
+
632
+ end
633
+
634
+ describe 'send_file' do
635
+ setup do
636
+ @file = File.dirname(__FILE__) + '/file.txt'
637
+ File.open(@file, 'wb') { |io| io.write('Hello World') }
638
+ end
639
+
640
+ def teardown
641
+ File.unlink @file
642
+ @file = nil
643
+ end
644
+
645
+ def send_file_app(opts={})
646
+ path = @file
647
+ mock_app {
648
+ get '/file.txt' do
649
+ send_file path, opts
650
+ end
651
+ }
652
+ end
653
+
654
+ it "sends the contents of the file" do
655
+ send_file_app
656
+ get '/file.txt'
657
+ assert ok?
658
+ assert_equal 'Hello World', body
659
+ end
660
+
661
+ it 'sets the Content-Type response header if a mime-type can be located' do
662
+ send_file_app
663
+ get '/file.txt'
664
+ assert_equal 'text/plain;charset=utf-8', response['Content-Type']
665
+ end
666
+
667
+ it 'sets the Content-Type response header if type option is set to a file extesion' do
668
+ send_file_app :type => 'html'
669
+ get '/file.txt'
670
+ assert_equal 'text/html;charset=utf-8', response['Content-Type']
671
+ end
672
+
673
+ it 'sets the Content-Type response header if type option is set to a mime type' do
674
+ send_file_app :type => 'application/octet-stream'
675
+ get '/file.txt'
676
+ assert_equal 'application/octet-stream', response['Content-Type']
677
+ end
678
+
679
+ it 'sets the Content-Length response header' do
680
+ send_file_app
681
+ get '/file.txt'
682
+ assert_equal 'Hello World'.length.to_s, response['Content-Length']
683
+ end
684
+
685
+ it 'sets the Last-Modified response header' do
686
+ send_file_app
687
+ get '/file.txt'
688
+ assert_equal File.mtime(@file).httpdate, response['Last-Modified']
689
+ end
690
+
691
+ it 'allows passing in a differen Last-Modified response header with :last_modified' do
692
+ time = Time.now
693
+ send_file_app :last_modified => time
694
+ get '/file.txt'
695
+ assert_equal time.httpdate, response['Last-Modified']
696
+ end
697
+
698
+ it "returns a 404 when not found" do
699
+ mock_app {
700
+ get '/' do
701
+ send_file 'this-file-does-not-exist.txt'
702
+ end
703
+ }
704
+ get '/'
705
+ assert not_found?
706
+ end
707
+
708
+ it "does not set the Content-Disposition header by default" do
709
+ send_file_app
710
+ get '/file.txt'
711
+ assert_nil response['Content-Disposition']
712
+ end
713
+
714
+ it "sets the Content-Disposition header when :disposition set to 'attachment'" do
715
+ send_file_app :disposition => 'attachment'
716
+ get '/file.txt'
717
+ assert_equal 'attachment; filename="file.txt"', response['Content-Disposition']
718
+ end
719
+
720
+ it "sets the Content-Disposition header when :disposition set to 'inline'" do
721
+ send_file_app :disposition => 'inline'
722
+ get '/file.txt'
723
+ assert_equal 'inline', response['Content-Disposition']
724
+ end
725
+
726
+ it "sets the Content-Disposition header when :filename provided" do
727
+ send_file_app :filename => 'foo.txt'
728
+ get '/file.txt'
729
+ assert_equal 'attachment; filename="foo.txt"', response['Content-Disposition']
730
+ end
731
+
732
+ it "is able to send files with unkown mime type" do
733
+ @file = File.dirname(__FILE__) + '/file.foobar'
734
+ File.open(@file, 'wb') { |io| io.write('Hello World') }
735
+ send_file_app
736
+ get '/file.txt'
737
+ assert_equal 'application/octet-stream', response['Content-Type']
738
+ end
739
+
740
+ it "does not override Content-Type if already set and no explicit type is given" do
741
+ path = @file
742
+ mock_app do
743
+ get '/' do
744
+ content_type :png
745
+ send_file path
746
+ end
747
+ end
748
+ get '/'
749
+ assert_equal 'image/png', response['Content-Type']
750
+ end
751
+
752
+ it "does override Content-Type even if already set, if explicit type is given" do
753
+ path = @file
754
+ mock_app do
755
+ get '/' do
756
+ content_type :png
757
+ send_file path, :type => :gif
758
+ end
759
+ end
760
+ get '/'
761
+ assert_equal 'image/gif', response['Content-Type']
762
+ end
763
+ end
764
+
765
+ describe 'cache_control' do
766
+ setup do
767
+ mock_app do
768
+ get '/foo' do
769
+ cache_control :public, :no_cache, :max_age => 60.0
770
+ 'Hello World'
771
+ end
772
+
773
+ get '/bar' do
774
+ cache_control :public, :no_cache
775
+ 'Hello World'
776
+ end
777
+ end
778
+ end
779
+
780
+ it 'sets the Cache-Control header' do
781
+ get '/foo'
782
+ assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
783
+ end
784
+
785
+ it 'last argument does not have to be a hash' do
786
+ get '/bar'
787
+ assert_equal ['public', 'no-cache'], response['Cache-Control'].split(', ')
788
+ end
789
+ end
790
+
791
+ describe 'expires' do
792
+ setup do
793
+ mock_app do
794
+ get '/foo' do
795
+ expires 60, :public, :no_cache
796
+ 'Hello World'
797
+ end
798
+
799
+ get '/bar' do
800
+ expires Time.now
801
+ end
802
+
803
+ get '/baz' do
804
+ expires Time.at(0)
805
+ end
806
+
807
+ get '/blah' do
808
+ obj = Object.new
809
+ def obj.method_missing(*a, &b) 60.send(*a, &b) end
810
+ def obj.is_a?(thing) 60.is_a?(thing) end
811
+ expires obj, :public, :no_cache
812
+ 'Hello World'
813
+ end
814
+
815
+ get '/boom' do
816
+ expires '9999'
817
+ end
818
+ end
819
+ end
820
+
821
+ it 'sets the Cache-Control header' do
822
+ get '/foo'
823
+ assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
824
+ end
825
+
826
+ it 'sets the Expires header' do
827
+ get '/foo'
828
+ assert_not_nil response['Expires']
829
+ end
830
+
831
+ it 'allows passing time objects' do
832
+ get '/bar'
833
+ assert_not_nil response['Expires']
834
+ end
835
+
836
+ it 'allows passing time objects' do
837
+ get '/baz'
838
+ assert_equal 'Thu, 01 Jan 1970 00:00:00 GMT', response['Expires']
839
+ end
840
+
841
+ it 'accepts values pretending to be a Numeric (like ActiveSupport::Duration)' do
842
+ get '/blah'
843
+ assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
844
+ end
845
+
846
+ it 'fails when Time.parse raises an ArgumentError' do
847
+ assert_raise(ArgumentError) { get '/boom' }
848
+ end
849
+ end
850
+
851
+ describe 'last_modified' do
852
+ it 'ignores nil' do
853
+ mock_app do
854
+ get '/' do last_modified nil; 200; end
855
+ end
856
+
857
+ get '/'
858
+ assert ! response['Last-Modified']
859
+ end
860
+
861
+ [Time.now, DateTime.now, Date.today, Time.now.to_i,
862
+ Struct.new(:to_time).new(Time.now) ].each do |last_modified_time|
863
+ describe "with #{last_modified_time.class.name}" do
864
+ setup do
865
+ mock_app do
866
+ get '/' do
867
+ last_modified last_modified_time
868
+ 'Boo!'
869
+ end
870
+ end
871
+ wrapper = Object.new.extend aldebaran::Helpers
872
+ @last_modified_time = wrapper.time_for last_modified_time
873
+ end
874
+
875
+ # fixes strange missing test error when running complete test suite.
876
+ it("does not complain about missing tests") { }
877
+
878
+ context "when there's no If-Modified-Since header" do
879
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
880
+ get '/'
881
+ assert_equal @last_modified_time.httpdate, response['Last-Modified']
882
+ end
883
+
884
+ it 'conditional GET misses and returns a body' do
885
+ get '/'
886
+ assert_equal 200, status
887
+ assert_equal 'Boo!', body
888
+ end
889
+ end
890
+
891
+ context "when there's an invalid If-Modified-Since header" do
892
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
893
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'a really weird date' }
894
+ assert_equal @last_modified_time.httpdate, response['Last-Modified']
895
+ end
896
+
897
+ it 'conditional GET misses and returns a body' do
898
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'a really weird date' }
899
+ assert_equal 200, status
900
+ assert_equal 'Boo!', body
901
+ end
902
+ end
903
+
904
+ context "when the resource has been modified since the If-Modified-Since header date" do
905
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
906
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time - 1).httpdate }
907
+ assert_equal @last_modified_time.httpdate, response['Last-Modified']
908
+ end
909
+
910
+ it 'conditional GET misses and returns a body' do
911
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time - 1).httpdate }
912
+ assert_equal 200, status
913
+ assert_equal 'Boo!', body
914
+ end
915
+
916
+ it 'does not rely on string comparison' do
917
+ mock_app do
918
+ get '/compare' do
919
+ last_modified "Mon, 18 Oct 2010 20:57:11 GMT"
920
+ "foo"
921
+ end
922
+ end
923
+
924
+ get '/compare', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2010 23:43:52 GMT' }
925
+ assert_equal 200, status
926
+ assert_equal 'foo', body
927
+ get '/compare', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT' }
928
+ assert_equal 304, status
929
+ assert_equal '', body
930
+ end
931
+ end
932
+
933
+ context "when the resource has been modified on the exact If-Modified-Since header date" do
934
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
935
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => @last_modified_time.httpdate }
936
+ assert_equal @last_modified_time.httpdate, response['Last-Modified']
937
+ end
938
+
939
+ it 'conditional GET matches and halts' do
940
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => @last_modified_time.httpdate }
941
+ assert_equal 304, status
942
+ assert_equal '', body
943
+ end
944
+ end
945
+
946
+ context "when the resource hasn't been modified since the If-Modified-Since header date" do
947
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
948
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time + 1).httpdate }
949
+ assert_equal @last_modified_time.httpdate, response['Last-Modified']
950
+ end
951
+
952
+ it 'conditional GET matches and halts' do
953
+ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time + 1).httpdate }
954
+ assert_equal 304, status
955
+ assert_equal '', body
956
+ end
957
+ end
958
+ end
959
+ end
960
+ end
961
+
962
+ describe 'etag' do
963
+ setup do
964
+ mock_app {
965
+ get '/' do
966
+ body { 'Hello World' }
967
+ etag 'FOO'
968
+ 'Boo!'
969
+ end
970
+
971
+ post '/' do
972
+ etag 'FOO'
973
+ 'Matches!'
974
+ end
975
+ }
976
+ end
977
+
978
+ it 'sets the ETag header' do
979
+ get '/'
980
+ assert_equal '"FOO"', response['ETag']
981
+ end
982
+
983
+ it 'returns a body when conditional get misses' do
984
+ get '/'
985
+ assert_equal 200, status
986
+ assert_equal 'Boo!', body
987
+ end
988
+
989
+ it 'returns a body when posting with no If-None-Match header' do
990
+ post '/'
991
+ assert_equal 200, status
992
+ assert_equal 'Matches!', body
993
+ end
994
+
995
+ it 'returns a body when conditional post matches' do
996
+ post '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
997
+ assert_equal 200, status
998
+ assert_equal 'Matches!', body
999
+ end
1000
+
1001
+ it 'halts with 412 when conditional post misses' do
1002
+ post '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR"' }
1003
+ assert_equal 412, status
1004
+ assert_equal '', body
1005
+ end
1006
+
1007
+ it 'halts when a conditional GET matches' do
1008
+ get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
1009
+ assert_equal 304, status
1010
+ assert_equal '', body
1011
+ end
1012
+
1013
+ it 'should handle multiple ETag values in If-None-Match header' do
1014
+ get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
1015
+ assert_equal 304, status
1016
+ assert_equal '', body
1017
+ end
1018
+
1019
+ it 'uses a weak etag with the :weak option' do
1020
+ mock_app {
1021
+ get '/' do
1022
+ etag 'FOO', :weak
1023
+ "that's weak, dude."
1024
+ end
1025
+ }
1026
+ get '/'
1027
+ assert_equal 'W/"FOO"', response['ETag']
1028
+ end
1029
+
1030
+ it 'raises an ArgumentError for an invalid strength' do
1031
+ mock_app do
1032
+ get '/' do
1033
+ etag 'FOO', :w00t
1034
+ "that's weak, dude."
1035
+ end
1036
+ end
1037
+ assert_raise(ArgumentError) { get '/' }
1038
+ end
1039
+ end
1040
+
1041
+ describe 'back' do
1042
+ it "makes redirecting back pretty" do
1043
+ mock_app {
1044
+ get '/foo' do
1045
+ redirect back
1046
+ end
1047
+ }
1048
+
1049
+ get '/foo', {}, 'HTTP_REFERER' => 'http://github.com'
1050
+ assert redirect?
1051
+ assert_equal "http://github.com", response.location
1052
+ end
1053
+ end
1054
+
1055
+ describe 'uri' do
1056
+ it 'generates absolute urls' do
1057
+ mock_app { get('/') { uri }}
1058
+ get '/'
1059
+ assert_equal 'http://example.org/', body
1060
+ end
1061
+
1062
+ it 'includes path_info' do
1063
+ mock_app { get('/:name') { uri }}
1064
+ get '/foo'
1065
+ assert_equal 'http://example.org/foo', body
1066
+ end
1067
+
1068
+ it 'allows passing an alternative to path_info' do
1069
+ mock_app { get('/:name') { uri '/bar' }}
1070
+ get '/foo'
1071
+ assert_equal 'http://example.org/bar', body
1072
+ end
1073
+
1074
+ it 'includes script_name' do
1075
+ mock_app { get('/:name') { uri '/bar' }}
1076
+ get '/foo', {}, { "SCRIPT_NAME" => '/foo' }
1077
+ assert_equal 'http://example.org/foo/bar', body
1078
+ end
1079
+
1080
+ it 'handles absolute URIs' do
1081
+ mock_app { get('/') { uri 'http://google.com' }}
1082
+ get '/'
1083
+ assert_equal 'http://google.com', body
1084
+ end
1085
+
1086
+ it 'handles different protocols' do
1087
+ mock_app { get('/') { uri 'mailto:jsmith@example.com' }}
1088
+ get '/'
1089
+ assert_equal 'mailto:jsmith@example.com', body
1090
+ end
1091
+
1092
+ it 'is aliased to #url' do
1093
+ mock_app { get('/') { url }}
1094
+ get '/'
1095
+ assert_equal 'http://example.org/', body
1096
+ end
1097
+
1098
+ it 'is aliased to #to' do
1099
+ mock_app { get('/') { to }}
1100
+ get '/'
1101
+ assert_equal 'http://example.org/', body
1102
+ end
1103
+ end
1104
+
1105
+ describe 'logger' do
1106
+ it 'logging works when logging is enabled' do
1107
+ mock_app do
1108
+ enable :logging
1109
+ get '/' do
1110
+ logger.info "Program started"
1111
+ logger.warn "Nothing to do!"
1112
+ end
1113
+ end
1114
+ io = StringIO.new
1115
+ get '/', {}, 'rack.errors' => io
1116
+ assert io.string.include?("INFO -- : Program started")
1117
+ assert io.string.include?("WARN -- : Nothing to do")
1118
+ end
1119
+
1120
+ it 'logging works when logging is disable, but no output is produced' do
1121
+ mock_app do
1122
+ disable :logging
1123
+ get '/' do
1124
+ logger.info "Program started"
1125
+ logger.warn "Nothing to do!"
1126
+ end
1127
+ end
1128
+ io = StringIO.new
1129
+ get '/', {}, 'rack.errors' => io
1130
+ assert !io.string.include?("INFO -- : Program started")
1131
+ assert !io.string.include?("WARN -- : Nothing to do")
1132
+ end
1133
+ end
1134
+
1135
+ module ::HelperOne; def one; '1'; end; end
1136
+ module ::HelperTwo; def two; '2'; end; end
1137
+
1138
+ describe 'Adding new helpers' do
1139
+ it 'takes a list of modules to mix into the app' do
1140
+ mock_app {
1141
+ helpers ::HelperOne, ::HelperTwo
1142
+
1143
+ get '/one' do
1144
+ one
1145
+ end
1146
+
1147
+ get '/two' do
1148
+ two
1149
+ end
1150
+ }
1151
+
1152
+ get '/one'
1153
+ assert_equal '1', body
1154
+
1155
+ get '/two'
1156
+ assert_equal '2', body
1157
+ end
1158
+
1159
+ it 'takes a block to mix into the app' do
1160
+ mock_app {
1161
+ helpers do
1162
+ def foo
1163
+ 'foo'
1164
+ end
1165
+ end
1166
+
1167
+ get '/' do
1168
+ foo
1169
+ end
1170
+ }
1171
+
1172
+ get '/'
1173
+ assert_equal 'foo', body
1174
+ end
1175
+
1176
+ it 'evaluates the block in class context so that methods can be aliased' do
1177
+ mock_app {
1178
+ helpers do
1179
+ alias_method :h, :escape_html
1180
+ end
1181
+
1182
+ get '/' do
1183
+ h('42 < 43')
1184
+ end
1185
+ }
1186
+
1187
+ get '/'
1188
+ assert ok?
1189
+ assert_equal '42 &lt; 43', body
1190
+ end
1191
+ end
1192
+ end