aldebaran 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,45 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+ require 'stringio'
3
+
4
+ class RequestTest < Test::Unit::TestCase
5
+ it 'responds to #user_agent' do
6
+ request = aldebaran::Request.new({'HTTP_USER_AGENT' => 'Test'})
7
+ assert request.respond_to?(:user_agent)
8
+ assert_equal 'Test', request.user_agent
9
+ end
10
+
11
+ it 'parses POST params when Content-Type is form-dataish' do
12
+ request = aldebaran::Request.new(
13
+ 'REQUEST_METHOD' => 'PUT',
14
+ 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
15
+ 'rack.input' => StringIO.new('foo=bar')
16
+ )
17
+ assert_equal 'bar', request.params['foo']
18
+ end
19
+
20
+ it 'is secure when the url scheme is https' do
21
+ request = aldebaran::Request.new('rack.url_scheme' => 'https')
22
+ assert request.secure?
23
+ end
24
+
25
+ it 'is not secure when the url scheme is http' do
26
+ request = aldebaran::Request.new('rack.url_scheme' => 'http')
27
+ assert !request.secure?
28
+ end
29
+
30
+ it 'respects X-Forwarded-Proto header for proxied SSL' do
31
+ request = aldebaran::Request.new('HTTP_X_FORWARDED_PROTO' => 'https')
32
+ assert request.secure?
33
+ end
34
+
35
+ it 'is possible to marshal params' do
36
+ request = aldebaran::Request.new(
37
+ 'REQUEST_METHOD' => 'PUT',
38
+ 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
39
+ 'rack.input' => StringIO.new('foo=bar')
40
+ )
41
+ params = aldebaran::Base.new!.send(:indifferent_hash).replace(request.params)
42
+ dumped = Marshal.dump(request.params)
43
+ assert_equal 'bar', Marshal.load(dumped)['foo']
44
+ end
45
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../helper', __FILE__)
4
+
5
+ class ResponseTest < Test::Unit::TestCase
6
+ setup do
7
+ @response = aldebaran::Response.new
8
+ end
9
+
10
+ it "initializes with 200, text/html, and empty body" do
11
+ assert_equal 200, @response.status
12
+ assert_equal 'text/html', @response['Content-Type']
13
+ assert_equal [], @response.body
14
+ end
15
+
16
+ it 'uses case insensitive headers' do
17
+ @response['content-type'] = 'application/foo'
18
+ assert_equal 'application/foo', @response['Content-Type']
19
+ assert_equal 'application/foo', @response['CONTENT-TYPE']
20
+ end
21
+
22
+ it 'writes to body' do
23
+ @response.body = 'Hello'
24
+ @response.write ' World'
25
+ assert_equal 'Hello World', @response.body.join
26
+ end
27
+
28
+ [204, 304].each do |status_code|
29
+ it "removes the Content-Type header and body when response status is #{status_code}" do
30
+ @response.status = status_code
31
+ @response.body = ['Hello World']
32
+ assert_equal [status_code, {}, []], @response.finish
33
+ end
34
+ end
35
+
36
+ it 'Calculates the Content-Length using the bytesize of the body' do
37
+ @response.body = ['Hello', 'World!', '✈']
38
+ status, headers, body = @response.finish
39
+ assert_equal '14', headers['Content-Length']
40
+ assert_equal @response.body, body
41
+ end
42
+
43
+ it 'does not call #to_ary or #inject on the body' do
44
+ object = Object.new
45
+ def object.inject(*) fail 'called' end
46
+ def object.to_ary(*) fail 'called' end
47
+ def object.each(*) end
48
+ @response.body = object
49
+ assert @response.finish
50
+ end
51
+
52
+ it 'does not nest a aldebaran::Response' do
53
+ @response.body = aldebaran::Response.new ["foo"]
54
+ assert_equal @response.body, ["foo"]
55
+ end
56
+
57
+ it 'does not nest a Rack::Response' do
58
+ @response.body = Rack::Response.new ["foo"]
59
+ assert_equal @response.body, ["foo"]
60
+ end
61
+ end
@@ -0,0 +1,98 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class ResultTest < Test::Unit::TestCase
4
+ it "sets response.body when result is a String" do
5
+ mock_app {
6
+ get '/' do
7
+ 'Hello World'
8
+ end
9
+ }
10
+
11
+ get '/'
12
+ assert ok?
13
+ assert_equal 'Hello World', body
14
+ end
15
+
16
+ it "sets response.body when result is an Array of Strings" do
17
+ mock_app {
18
+ get '/' do
19
+ ['Hello', 'World']
20
+ end
21
+ }
22
+
23
+ get '/'
24
+ assert ok?
25
+ assert_equal 'HelloWorld', body
26
+ end
27
+
28
+ it "sets response.body when result responds to #each" do
29
+ mock_app {
30
+ get '/' do
31
+ res = lambda { 'Hello World' }
32
+ def res.each ; yield call ; end
33
+ res
34
+ end
35
+ }
36
+
37
+ get '/'
38
+ assert ok?
39
+ assert_equal 'Hello World', body
40
+ end
41
+
42
+ it "sets response.body to [] when result is nil" do
43
+ mock_app {
44
+ get '/' do
45
+ nil
46
+ end
47
+ }
48
+
49
+ get '/'
50
+ assert ok?
51
+ assert_equal '', body
52
+ end
53
+
54
+ it "sets status, headers, and body when result is a Rack response tuple" do
55
+ mock_app {
56
+ get '/' do
57
+ [203, {'Content-Type' => 'foo/bar'}, 'Hello World']
58
+ end
59
+ }
60
+
61
+ get '/'
62
+ assert_equal 203, status
63
+ assert_equal 'foo/bar', response['Content-Type']
64
+ assert_equal 'Hello World', body
65
+ end
66
+
67
+ it "sets status and body when result is a two-tuple" do
68
+ mock_app {
69
+ get '/' do
70
+ [409, 'formula of']
71
+ end
72
+ }
73
+
74
+ get '/'
75
+ assert_equal 409, status
76
+ assert_equal 'formula of', body
77
+ end
78
+
79
+ it "raises a ArgumentError when result is a non two or three tuple Array" do
80
+ mock_app {
81
+ get '/' do
82
+ [409, 'formula of', 'something else', 'even more']
83
+ end
84
+ }
85
+
86
+ assert_raise(ArgumentError) { get '/' }
87
+ end
88
+
89
+ it "sets status when result is a Fixnum status code" do
90
+ mock_app {
91
+ get('/') { 205 }
92
+ }
93
+
94
+ get '/'
95
+ assert_equal 205, status
96
+ assert_equal '', body
97
+ end
98
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ module RouteAddedTest
4
+ @routes, @procs = [], []
5
+ def self.routes ; @routes ; end
6
+ def self.procs ; @procs ; end
7
+ def self.route_added(verb, path, proc)
8
+ @routes << [verb, path]
9
+ @procs << proc
10
+ end
11
+ end
12
+
13
+ class RouteAddedHookTest < Test::Unit::TestCase
14
+ setup {
15
+ RouteAddedTest.routes.clear
16
+ RouteAddedTest.procs.clear
17
+ }
18
+
19
+ it "should be notified of an added route" do
20
+ mock_app(Class.new(aldebaran::Base)) {
21
+ register RouteAddedTest
22
+ get('/') {}
23
+ }
24
+
25
+ assert_equal [["GET", "/"], ["HEAD", "/"]],
26
+ RouteAddedTest.routes
27
+ end
28
+
29
+ it "should include hooks from superclass" do
30
+ a = Class.new(Class.new(aldebaran::Base))
31
+ b = Class.new(a)
32
+
33
+ a.register RouteAddedTest
34
+ b.class_eval { post("/sub_app_route") {} }
35
+
36
+ assert_equal [["POST", "/sub_app_route"]],
37
+ RouteAddedTest.routes
38
+ end
39
+
40
+ it "should only run once per extension" do
41
+ mock_app(Class.new(aldebaran::Base)) {
42
+ register RouteAddedTest
43
+ register RouteAddedTest
44
+ get('/') {}
45
+ }
46
+
47
+ assert_equal [["GET", "/"], ["HEAD", "/"]],
48
+ RouteAddedTest.routes
49
+ end
50
+
51
+ it "should pass route blocks as an argument" do
52
+ mock_app(Class.new(aldebaran::Base)) {
53
+ register RouteAddedTest
54
+ get('/') {}
55
+ }
56
+
57
+ assert_kind_of Proc, RouteAddedTest.procs.first
58
+ end
59
+ end
@@ -0,0 +1,1096 @@
1
+ # I like coding: UTF-8
2
+ require File.expand_path('../helper', __FILE__)
3
+
4
+ # Helper method for easy route pattern matching testing
5
+ def route_def(pattern)
6
+ mock_app { get(pattern) { } }
7
+ end
8
+
9
+ class RegexpLookAlike
10
+ class MatchData
11
+ def captures
12
+ ["this", "is", "a", "test"]
13
+ end
14
+ end
15
+
16
+ def match(string)
17
+ ::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/"
18
+ end
19
+
20
+ def keys
21
+ ["one", "two", "three", "four"]
22
+ end
23
+ end
24
+
25
+ class RoutingTest < Test::Unit::TestCase
26
+ %w[get put post delete options patch].each do |verb|
27
+ it "defines #{verb.upcase} request handlers with #{verb}" do
28
+ mock_app {
29
+ send verb, '/hello' do
30
+ 'Hello World'
31
+ end
32
+ }
33
+
34
+ request = Rack::MockRequest.new(@app)
35
+ response = request.request(verb.upcase, '/hello', {})
36
+ assert response.ok?
37
+ assert_equal 'Hello World', response.body
38
+ end
39
+ end
40
+
41
+ it "defines HEAD request handlers with HEAD" do
42
+ mock_app {
43
+ head '/hello' do
44
+ response['X-Hello'] = 'World!'
45
+ 'remove me'
46
+ end
47
+ }
48
+
49
+ request = Rack::MockRequest.new(@app)
50
+ response = request.request('HEAD', '/hello', {})
51
+ assert response.ok?
52
+ assert_equal 'World!', response['X-Hello']
53
+ assert_equal '', response.body
54
+ end
55
+
56
+ it "404s when no route satisfies the request" do
57
+ mock_app {
58
+ get('/foo') { }
59
+ }
60
+ get '/bar'
61
+ assert_equal 404, status
62
+ end
63
+
64
+ it "404s and sets X-Cascade header when no route satisfies the request" do
65
+ mock_app {
66
+ get('/foo') { }
67
+ }
68
+ get '/bar'
69
+ assert_equal 404, status
70
+ assert_equal 'pass', response.headers['X-Cascade']
71
+ end
72
+
73
+ it "allows using unicode" do
74
+ mock_app do
75
+ get('/föö') { }
76
+ end
77
+ get '/f%C3%B6%C3%B6'
78
+ assert_equal 200, status
79
+ end
80
+
81
+ it "it handles encoded slashes correctly" do
82
+ mock_app { get("/:a") { |a| a } }
83
+ get '/foo%2Fbar'
84
+ assert_equal 200, status
85
+ assert_body "foo/bar"
86
+ end
87
+
88
+ it "overrides the content-type in error handlers" do
89
+ mock_app {
90
+ before { content_type 'text/plain' }
91
+ error aldebaran::NotFound do
92
+ content_type "text/html"
93
+ "<h1>Not Found</h1>"
94
+ end
95
+ }
96
+
97
+ get '/foo'
98
+ assert_equal 404, status
99
+ assert_equal 'text/html;charset=utf-8', response["Content-Type"]
100
+ assert_equal "<h1>Not Found</h1>", response.body
101
+ end
102
+
103
+ it 'matches empty PATH_INFO to "/" if no route is defined for ""' do
104
+ mock_app do
105
+ get '/' do
106
+ 'worked'
107
+ end
108
+ end
109
+
110
+ get '/', {}, "PATH_INFO" => ""
111
+ assert ok?
112
+ assert_equal 'worked', body
113
+ end
114
+
115
+ it 'matches empty PATH_INFO to "" if a route is defined for ""' do
116
+ mock_app do
117
+ get '/' do
118
+ 'did not work'
119
+ end
120
+
121
+ get '' do
122
+ 'worked'
123
+ end
124
+ end
125
+
126
+ get '/', {}, "PATH_INFO" => ""
127
+ assert ok?
128
+ assert_equal 'worked', body
129
+ end
130
+
131
+ it 'takes multiple definitions of a route' do
132
+ mock_app {
133
+ user_agent(/Foo/)
134
+ get '/foo' do
135
+ 'foo'
136
+ end
137
+
138
+ get '/foo' do
139
+ 'not foo'
140
+ end
141
+ }
142
+
143
+ get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo'
144
+ assert ok?
145
+ assert_equal 'foo', body
146
+
147
+ get '/foo'
148
+ assert ok?
149
+ assert_equal 'not foo', body
150
+ end
151
+
152
+ it "exposes params with indifferent hash" do
153
+ mock_app {
154
+ get '/:foo' do
155
+ assert_equal 'bar', params['foo']
156
+ assert_equal 'bar', params[:foo]
157
+ 'well, alright'
158
+ end
159
+ }
160
+ get '/bar'
161
+ assert_equal 'well, alright', body
162
+ end
163
+
164
+ it "merges named params and query string params in params" do
165
+ mock_app {
166
+ get '/:foo' do
167
+ assert_equal 'bar', params['foo']
168
+ assert_equal 'biz', params['baz']
169
+ end
170
+ }
171
+ get '/bar?baz=biz'
172
+ assert ok?
173
+ end
174
+
175
+ it "supports named params like /hello/:person" do
176
+ mock_app {
177
+ get '/hello/:person' do
178
+ "Hello #{params['person']}"
179
+ end
180
+ }
181
+ get '/hello/Frank'
182
+ assert_equal 'Hello Frank', body
183
+ end
184
+
185
+ it "supports optional named params like /?:foo?/?:bar?" do
186
+ mock_app {
187
+ get '/?:foo?/?:bar?' do
188
+ "foo=#{params[:foo]};bar=#{params[:bar]}"
189
+ end
190
+ }
191
+
192
+ get '/hello/world'
193
+ assert ok?
194
+ assert_equal "foo=hello;bar=world", body
195
+
196
+ get '/hello'
197
+ assert ok?
198
+ assert_equal "foo=hello;bar=", body
199
+
200
+ get '/'
201
+ assert ok?
202
+ assert_equal "foo=;bar=", body
203
+ end
204
+
205
+ it "supports named captures like %r{/hello/(?<person>[^/?#]+)} on Ruby >= 1.9" do
206
+ next if RUBY_VERSION < '1.9'
207
+ mock_app {
208
+ get Regexp.new('/hello/(?<person>[^/?#]+)') do
209
+ "Hello #{params['person']}"
210
+ end
211
+ }
212
+ get '/hello/Frank'
213
+ assert_equal 'Hello Frank', body
214
+ end
215
+
216
+ it "supports optional named captures like %r{/page(?<format>.[^/?#]+)?} on Ruby >= 1.9" do
217
+ next if RUBY_VERSION < '1.9'
218
+ mock_app {
219
+ get Regexp.new('/page(?<format>.[^/?#]+)?') do
220
+ "format=#{params[:format]}"
221
+ end
222
+ }
223
+
224
+ get '/page.html'
225
+ assert ok?
226
+ assert_equal "format=.html", body
227
+
228
+ get '/page.xml'
229
+ assert ok?
230
+ assert_equal "format=.xml", body
231
+
232
+ get '/page'
233
+ assert ok?
234
+ assert_equal "format=", body
235
+ end
236
+
237
+ it "supports single splat params like /*" do
238
+ mock_app {
239
+ get '/*' do
240
+ assert params['splat'].kind_of?(Array)
241
+ params['splat'].join "\n"
242
+ end
243
+ }
244
+
245
+ get '/foo'
246
+ assert_equal "foo", body
247
+
248
+ get '/foo/bar/baz'
249
+ assert_equal "foo/bar/baz", body
250
+ end
251
+
252
+ it "supports mixing multiple splat params like /*/foo/*/*" do
253
+ mock_app {
254
+ get '/*/foo/*/*' do
255
+ assert params['splat'].kind_of?(Array)
256
+ params['splat'].join "\n"
257
+ end
258
+ }
259
+
260
+ get '/bar/foo/bling/baz/boom'
261
+ assert_equal "bar\nbling\nbaz/boom", body
262
+
263
+ get '/bar/foo/baz'
264
+ assert not_found?
265
+ end
266
+
267
+ it "supports mixing named and splat params like /:foo/*" do
268
+ mock_app {
269
+ get '/:foo/*' do
270
+ assert_equal 'foo', params['foo']
271
+ assert_equal ['bar/baz'], params['splat']
272
+ end
273
+ }
274
+
275
+ get '/foo/bar/baz'
276
+ assert ok?
277
+ end
278
+
279
+ it "matches a dot ('.') as part of a named param" do
280
+ mock_app {
281
+ get '/:foo/:bar' do
282
+ params[:foo]
283
+ end
284
+ }
285
+
286
+ get '/user@example.com/name'
287
+ assert_equal 200, response.status
288
+ assert_equal 'user@example.com', body
289
+ end
290
+
291
+ it "matches a literal dot ('.') outside of named params" do
292
+ mock_app {
293
+ get '/:file.:ext' do
294
+ assert_equal 'pony', params[:file]
295
+ assert_equal 'jpg', params[:ext]
296
+ 'right on'
297
+ end
298
+ }
299
+
300
+ get '/pony.jpg'
301
+ assert_equal 200, response.status
302
+ assert_equal 'right on', body
303
+ end
304
+
305
+ it "literally matches dot in paths" do
306
+ route_def '/test.bar'
307
+
308
+ get '/test.bar'
309
+ assert ok?
310
+ get 'test0bar'
311
+ assert not_found?
312
+ end
313
+
314
+ it "literally matches dollar sign in paths" do
315
+ route_def '/test$/'
316
+
317
+ get '/test$/'
318
+ assert ok?
319
+ end
320
+
321
+ it "literally matches plus sign in paths" do
322
+ route_def '/te+st/'
323
+
324
+ get '/te%2Bst/'
325
+ assert ok?
326
+ get '/teeeeeeest/'
327
+ assert not_found?
328
+ end
329
+
330
+ it "literally matches parens in paths" do
331
+ route_def '/test(bar)/'
332
+
333
+ get '/test(bar)/'
334
+ assert ok?
335
+ end
336
+
337
+ it "supports basic nested params" do
338
+ mock_app {
339
+ get '/hi' do
340
+ params["person"]["name"]
341
+ end
342
+ }
343
+
344
+ get "/hi?person[name]=John+Doe"
345
+ assert ok?
346
+ assert_equal "John Doe", body
347
+ end
348
+
349
+ it "exposes nested params with indifferent hash" do
350
+ mock_app {
351
+ get '/testme' do
352
+ assert_equal 'baz', params['bar']['foo']
353
+ assert_equal 'baz', params['bar'][:foo]
354
+ 'well, alright'
355
+ end
356
+ }
357
+ get '/testme?bar[foo]=baz'
358
+ assert_equal 'well, alright', body
359
+ end
360
+
361
+ it "supports deeply nested params" do
362
+ expected_params = {
363
+ "emacs" => {
364
+ "map" => { "goto-line" => "M-g g" },
365
+ "version" => "22.3.1"
366
+ },
367
+ "browser" => {
368
+ "firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
369
+ "chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
370
+ },
371
+ "paste" => {"name"=>"hello world", "syntax"=>"ruby"}
372
+ }
373
+ mock_app {
374
+ get '/foo' do
375
+ assert_equal expected_params, params
376
+ 'looks good'
377
+ end
378
+ }
379
+ get '/foo', expected_params
380
+ assert ok?
381
+ assert_equal 'looks good', body
382
+ end
383
+
384
+ it "preserves non-nested params" do
385
+ mock_app {
386
+ get '/foo' do
387
+ assert_equal "2", params["article_id"]
388
+ assert_equal "awesome", params['comment']['body']
389
+ assert_nil params['comment[body]']
390
+ 'looks good'
391
+ end
392
+ }
393
+
394
+ get '/foo?article_id=2&comment[body]=awesome'
395
+ assert ok?
396
+ assert_equal 'looks good', body
397
+ end
398
+
399
+ it "matches paths that include spaces encoded with %20" do
400
+ mock_app {
401
+ get '/path with spaces' do
402
+ 'looks good'
403
+ end
404
+ }
405
+
406
+ get '/path%20with%20spaces'
407
+ assert ok?
408
+ assert_equal 'looks good', body
409
+ end
410
+
411
+ it "matches paths that include spaces encoded with +" do
412
+ mock_app {
413
+ get '/path with spaces' do
414
+ 'looks good'
415
+ end
416
+ }
417
+
418
+ get '/path+with+spaces'
419
+ assert ok?
420
+ assert_equal 'looks good', body
421
+ end
422
+
423
+ it "matches paths that include ampersands" do
424
+ mock_app {
425
+ get '/:name' do
426
+ 'looks good'
427
+ end
428
+ }
429
+
430
+ get '/foo&bar'
431
+ assert ok?
432
+ assert_equal 'looks good', body
433
+ end
434
+
435
+ it "URL decodes named parameters and splats" do
436
+ mock_app {
437
+ get '/:foo/*' do
438
+ assert_equal 'hello world', params['foo']
439
+ assert_equal ['how are you'], params['splat']
440
+ nil
441
+ end
442
+ }
443
+
444
+ get '/hello%20world/how%20are%20you'
445
+ assert ok?
446
+ end
447
+
448
+ it 'supports regular expressions' do
449
+ mock_app {
450
+ get(/^\/foo...\/bar$/) do
451
+ 'Hello World'
452
+ end
453
+ }
454
+
455
+ get '/foooom/bar'
456
+ assert ok?
457
+ assert_equal 'Hello World', body
458
+ end
459
+
460
+ it 'makes regular expression captures available in params[:captures]' do
461
+ mock_app {
462
+ get(/^\/fo(.*)\/ba(.*)/) do
463
+ assert_equal ['orooomma', 'f'], params[:captures]
464
+ 'right on'
465
+ end
466
+ }
467
+
468
+ get '/foorooomma/baf'
469
+ assert ok?
470
+ assert_equal 'right on', body
471
+ end
472
+
473
+ it 'supports regular expression look-alike routes' do
474
+ mock_app {
475
+ get(RegexpLookAlike.new) do
476
+ assert_equal 'this', params[:one]
477
+ assert_equal 'is', params[:two]
478
+ assert_equal 'a', params[:three]
479
+ assert_equal 'test', params[:four]
480
+ 'right on'
481
+ end
482
+ }
483
+
484
+ get '/this/is/a/test/'
485
+ assert ok?
486
+ assert_equal 'right on', body
487
+ end
488
+
489
+ it 'raises a TypeError when pattern is not a String or Regexp' do
490
+ assert_raise(TypeError) {
491
+ mock_app { get(42){} }
492
+ }
493
+ end
494
+
495
+ it "returns response immediately on halt" do
496
+ mock_app {
497
+ get '/' do
498
+ halt 'Hello World'
499
+ 'Boo-hoo World'
500
+ end
501
+ }
502
+
503
+ get '/'
504
+ assert ok?
505
+ assert_equal 'Hello World', body
506
+ end
507
+
508
+ it "halts with a response tuple" do
509
+ mock_app {
510
+ get '/' do
511
+ halt 295, {'Content-Type' => 'text/plain'}, 'Hello World'
512
+ end
513
+ }
514
+
515
+ get '/'
516
+ assert_equal 295, status
517
+ assert_equal 'text/plain', response['Content-Type']
518
+ assert_equal 'Hello World', body
519
+ end
520
+
521
+ it "halts with an array of strings" do
522
+ mock_app {
523
+ get '/' do
524
+ halt %w[Hello World How Are You]
525
+ end
526
+ }
527
+
528
+ get '/'
529
+ assert_equal 'HelloWorldHowAreYou', body
530
+ end
531
+
532
+ it "transitions to the next matching route on pass" do
533
+ mock_app {
534
+ get '/:foo' do
535
+ pass
536
+ 'Hello Foo'
537
+ end
538
+
539
+ get '/*' do
540
+ assert !params.include?('foo')
541
+ 'Hello World'
542
+ end
543
+ }
544
+
545
+ get '/bar'
546
+ assert ok?
547
+ assert_equal 'Hello World', body
548
+ end
549
+
550
+ it "transitions to 404 when passed and no subsequent route matches" do
551
+ mock_app {
552
+ get '/:foo' do
553
+ pass
554
+ 'Hello Foo'
555
+ end
556
+ }
557
+
558
+ get '/bar'
559
+ assert not_found?
560
+ end
561
+
562
+ it "transitions to 404 and sets X-Cascade header when passed and no subsequent route matches" do
563
+ mock_app {
564
+ get '/:foo' do
565
+ pass
566
+ 'Hello Foo'
567
+ end
568
+
569
+ get '/bar' do
570
+ 'Hello Bar'
571
+ end
572
+ }
573
+
574
+ get '/foo'
575
+ assert not_found?
576
+ assert_equal 'pass', response.headers['X-Cascade']
577
+ end
578
+
579
+ it "uses optional block passed to pass as route block if no other route is found" do
580
+ mock_app {
581
+ get "/" do
582
+ pass do
583
+ "this"
584
+ end
585
+ "not this"
586
+ end
587
+ }
588
+
589
+ get "/"
590
+ assert ok?
591
+ assert "this", body
592
+ end
593
+
594
+ it "passes when matching condition returns false" do
595
+ mock_app {
596
+ condition { params[:foo] == 'bar' }
597
+ get '/:foo' do
598
+ 'Hello World'
599
+ end
600
+ }
601
+
602
+ get '/bar'
603
+ assert ok?
604
+ assert_equal 'Hello World', body
605
+
606
+ get '/foo'
607
+ assert not_found?
608
+ end
609
+
610
+ it "does not pass when matching condition returns nil" do
611
+ mock_app {
612
+ condition { nil }
613
+ get '/:foo' do
614
+ 'Hello World'
615
+ end
616
+ }
617
+
618
+ get '/bar'
619
+ assert ok?
620
+ assert_equal 'Hello World', body
621
+ end
622
+
623
+ it "passes to next route when condition calls pass explicitly" do
624
+ mock_app {
625
+ condition { pass unless params[:foo] == 'bar' }
626
+ get '/:foo' do
627
+ 'Hello World'
628
+ end
629
+ }
630
+
631
+ get '/bar'
632
+ assert ok?
633
+ assert_equal 'Hello World', body
634
+
635
+ get '/foo'
636
+ assert not_found?
637
+ end
638
+
639
+ it "passes to the next route when host_name does not match" do
640
+ mock_app {
641
+ host_name 'example.com'
642
+ get '/foo' do
643
+ 'Hello World'
644
+ end
645
+ }
646
+ get '/foo'
647
+ assert not_found?
648
+
649
+ get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
650
+ assert_equal 200, status
651
+ assert_equal 'Hello World', body
652
+ end
653
+
654
+ it "passes to the next route when user_agent does not match" do
655
+ mock_app {
656
+ user_agent(/Foo/)
657
+ get '/foo' do
658
+ 'Hello World'
659
+ end
660
+ }
661
+ get '/foo'
662
+ assert not_found?
663
+
664
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
665
+ assert_equal 200, status
666
+ assert_equal 'Hello World', body
667
+ end
668
+
669
+ it "treats missing user agent like an empty string" do
670
+ mock_app do
671
+ user_agent(/.*/)
672
+ get '/' do
673
+ "Hello World"
674
+ end
675
+ end
676
+ get '/'
677
+ assert_equal 200, status
678
+ assert_equal 'Hello World', body
679
+ end
680
+
681
+ it "makes captures in user agent pattern available in params[:agent]" do
682
+ mock_app {
683
+ user_agent(/Foo (.*)/)
684
+ get '/foo' do
685
+ 'Hello ' + params[:agent].first
686
+ end
687
+ }
688
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
689
+ assert_equal 200, status
690
+ assert_equal 'Hello Bar', body
691
+ end
692
+
693
+ it "filters by accept header" do
694
+ mock_app {
695
+ get '/', :provides => :xml do
696
+ env['HTTP_ACCEPT']
697
+ end
698
+ get '/foo', :provides => :html do
699
+ env['HTTP_ACCEPT']
700
+ end
701
+ }
702
+
703
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
704
+ assert ok?
705
+ assert_equal 'application/xml', body
706
+ assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
707
+
708
+ get '/', {}, { :accept => 'text/html' }
709
+ assert !ok?
710
+
711
+ get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' }
712
+ assert ok?
713
+ assert_equal 'text/html;q=0.9', body
714
+
715
+ get '/foo', {}, { 'HTTP_ACCEPT' => '' }
716
+ assert !ok?
717
+ end
718
+
719
+ it "allows multiple mime types for accept header" do
720
+ types = ['image/jpeg', 'image/pjpeg']
721
+
722
+ mock_app {
723
+ get '/', :provides => types do
724
+ env['HTTP_ACCEPT']
725
+ end
726
+ }
727
+
728
+ types.each do |type|
729
+ get '/', {}, { 'HTTP_ACCEPT' => type }
730
+ assert ok?
731
+ assert_equal type, body
732
+ assert_equal type, response.headers['Content-Type']
733
+ end
734
+ end
735
+
736
+ it 'degrades gracefully when optional accept header is not provided' do
737
+ mock_app {
738
+ get '/', :provides => :xml do
739
+ env['HTTP_ACCEPT']
740
+ end
741
+ get '/' do
742
+ 'default'
743
+ end
744
+ }
745
+ get '/'
746
+ assert ok?
747
+ assert_equal 'default', body
748
+ end
749
+
750
+ it 'respects user agent prefferences for the content type' do
751
+ mock_app { get('/', :provides => [:png, :html]) { content_type }}
752
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' }
753
+ assert_body 'text/html;charset=utf-8'
754
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.8,text/html;q=0.5' }
755
+ assert_body 'image/png'
756
+ end
757
+
758
+ it 'accepts generic types' do
759
+ mock_app do
760
+ get('/', :provides => :xml) { content_type }
761
+ get('/') { 'no match' }
762
+ end
763
+ get '/'
764
+ assert_body 'no match'
765
+ get '/', {}, { 'HTTP_ACCEPT' => 'foo/*' }
766
+ assert_body 'no match'
767
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
768
+ assert_body 'application/xml;charset=utf-8'
769
+ get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
770
+ assert_body 'application/xml;charset=utf-8'
771
+ end
772
+
773
+ it 'prefers concrete over partly generic types' do
774
+ mock_app { get('/', :provides => [:png, :html]) { content_type }}
775
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/*, text/html' }
776
+ assert_body 'text/html;charset=utf-8'
777
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/png, text/*' }
778
+ assert_body 'image/png'
779
+ end
780
+
781
+ it 'prefers concrete over fully generic types' do
782
+ mock_app { get('/', :provides => [:png, :html]) { content_type }}
783
+ get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/html' }
784
+ assert_body 'text/html;charset=utf-8'
785
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/png, */*' }
786
+ assert_body 'image/png'
787
+ end
788
+
789
+ it 'prefers partly generic over fully generic types' do
790
+ mock_app { get('/', :provides => [:png, :html]) { content_type }}
791
+ get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/*' }
792
+ assert_body 'text/html;charset=utf-8'
793
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/*, */*' }
794
+ assert_body 'image/png'
795
+ end
796
+
797
+ it 'respects quality with generic types' do
798
+ mock_app { get('/', :provides => [:png, :html]) { content_type }}
799
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/*;q=1, text/html;q=0' }
800
+ assert_body 'image/png'
801
+ get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*;q=0.7' }
802
+ assert_body 'text/html;charset=utf-8'
803
+ end
804
+
805
+ it 'accepts both text/javascript and application/javascript for js' do
806
+ mock_app { get('/', :provides => :js) { content_type }}
807
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
808
+ assert_body 'application/javascript;charset=utf-8'
809
+ get '/', {}, { 'HTTP_ACCEPT' => 'text/javascript' }
810
+ assert_body 'text/javascript;charset=utf-8'
811
+ end
812
+
813
+ it 'accepts both text/xml and application/xml for xml' do
814
+ mock_app { get('/', :provides => :xml) { content_type }}
815
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
816
+ assert_body 'application/xml;charset=utf-8'
817
+ get '/', {}, { 'HTTP_ACCEPT' => 'text/xml' }
818
+ assert_body 'text/xml;charset=utf-8'
819
+ end
820
+
821
+ it 'passes a single url param as block parameters when one param is specified' do
822
+ mock_app {
823
+ get '/:foo' do |foo|
824
+ assert_equal 'bar', foo
825
+ end
826
+ }
827
+
828
+ get '/bar'
829
+ assert ok?
830
+ end
831
+
832
+ it 'passes multiple params as block parameters when many are specified' do
833
+ mock_app {
834
+ get '/:foo/:bar/:baz' do |foo, bar, baz|
835
+ assert_equal 'abc', foo
836
+ assert_equal 'def', bar
837
+ assert_equal 'ghi', baz
838
+ end
839
+ }
840
+
841
+ get '/abc/def/ghi'
842
+ assert ok?
843
+ end
844
+
845
+ it 'passes regular expression captures as block parameters' do
846
+ mock_app {
847
+ get(/^\/fo(.*)\/ba(.*)/) do |foo, bar|
848
+ assert_equal 'orooomma', foo
849
+ assert_equal 'f', bar
850
+ 'looks good'
851
+ end
852
+ }
853
+
854
+ get '/foorooomma/baf'
855
+ assert ok?
856
+ assert_equal 'looks good', body
857
+ end
858
+
859
+ it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do
860
+ mock_app {
861
+ get '/*/foo/*/*' do |foo, bar, baz|
862
+ assert_equal 'bar', foo
863
+ assert_equal 'bling', bar
864
+ assert_equal 'baz/boom', baz
865
+ 'looks good'
866
+ end
867
+ }
868
+
869
+ get '/bar/foo/bling/baz/boom'
870
+ assert ok?
871
+ assert_equal 'looks good', body
872
+ end
873
+
874
+ it 'raises an ArgumentError with block arity > 1 and too many values' do
875
+ mock_app do
876
+ get '/:foo/:bar/:baz' do |foo, bar|
877
+ 'quux'
878
+ end
879
+ end
880
+
881
+ assert_raise(ArgumentError) { get '/a/b/c' }
882
+ end
883
+
884
+ it 'raises an ArgumentError with block param arity > 1 and too few values' do
885
+ mock_app {
886
+ get '/:foo/:bar' do |foo, bar, baz|
887
+ 'quux'
888
+ end
889
+ }
890
+
891
+ assert_raise(ArgumentError) { get '/a/b' }
892
+ end
893
+
894
+ it 'succeeds if no block parameters are specified' do
895
+ mock_app {
896
+ get '/:foo/:bar' do
897
+ 'quux'
898
+ end
899
+ }
900
+
901
+ get '/a/b'
902
+ assert ok?
903
+ assert_equal 'quux', body
904
+ end
905
+
906
+ it 'passes all params with block param arity -1 (splat args)' do
907
+ mock_app {
908
+ get '/:foo/:bar' do |*args|
909
+ args.join
910
+ end
911
+ }
912
+
913
+ get '/a/b'
914
+ assert ok?
915
+ assert_equal 'ab', body
916
+ end
917
+
918
+ it 'allows custom route-conditions to be set via route options' do
919
+ protector = Module.new {
920
+ def protect(*args)
921
+ condition {
922
+ unless authorize(params["user"], params["password"])
923
+ halt 403, "go away"
924
+ end
925
+ }
926
+ end
927
+ }
928
+
929
+ mock_app {
930
+ register protector
931
+
932
+ helpers do
933
+ def authorize(username, password)
934
+ username == "foo" && password == "bar"
935
+ end
936
+ end
937
+
938
+ get "/", :protect => true do
939
+ "hey"
940
+ end
941
+ }
942
+
943
+ get "/"
944
+ assert forbidden?
945
+ assert_equal "go away", body
946
+
947
+ get "/", :user => "foo", :password => "bar"
948
+ assert ok?
949
+ assert_equal "hey", body
950
+ end
951
+
952
+ # NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
953
+ # param arity is lax: declaring a mismatched number of block params results
954
+ # in a warning. Under 1.9, block param arity is strict: mismatched block
955
+ # arity raises an ArgumentError.
956
+
957
+ if RUBY_VERSION >= '1.9'
958
+
959
+ it 'raises an ArgumentError with block param arity 1 and no values' do
960
+ mock_app {
961
+ get '/foo' do |foo|
962
+ 'quux'
963
+ end
964
+ }
965
+
966
+ assert_raise(ArgumentError) { get '/foo' }
967
+ end
968
+
969
+ it 'raises an ArgumentError with block param arity 1 and too many values' do
970
+ mock_app {
971
+ get '/:foo/:bar/:baz' do |foo|
972
+ 'quux'
973
+ end
974
+ }
975
+
976
+ assert_raise(ArgumentError) { get '/a/b/c' }
977
+ end
978
+
979
+ else
980
+
981
+ it 'does not raise an ArgumentError with block param arity 1 and no values' do
982
+ mock_app {
983
+ get '/foo' do |foo|
984
+ 'quux'
985
+ end
986
+ }
987
+
988
+ silence_warnings { get '/foo' }
989
+ assert ok?
990
+ assert_equal 'quux', body
991
+ end
992
+
993
+ it 'does not raise an ArgumentError with block param arity 1 and too many values' do
994
+ mock_app {
995
+ get '/:foo/:bar/:baz' do |foo|
996
+ 'quux'
997
+ end
998
+ }
999
+
1000
+ silence_warnings { get '/a/b/c' }
1001
+ assert ok?
1002
+ assert_equal 'quux', body
1003
+ end
1004
+
1005
+ end
1006
+
1007
+ it "matches routes defined in superclasses" do
1008
+ base = Class.new(aldebaran::Base)
1009
+ base.get('/foo') { 'foo in baseclass' }
1010
+
1011
+ mock_app(base) {
1012
+ get('/bar') { 'bar in subclass' }
1013
+ }
1014
+
1015
+ get '/foo'
1016
+ assert ok?
1017
+ assert_equal 'foo in baseclass', body
1018
+
1019
+ get '/bar'
1020
+ assert ok?
1021
+ assert_equal 'bar in subclass', body
1022
+ end
1023
+
1024
+ it "matches routes in subclasses before superclasses" do
1025
+ base = Class.new(aldebaran::Base)
1026
+ base.get('/foo') { 'foo in baseclass' }
1027
+ base.get('/bar') { 'bar in baseclass' }
1028
+
1029
+ mock_app(base) {
1030
+ get('/foo') { 'foo in subclass' }
1031
+ }
1032
+
1033
+ get '/foo'
1034
+ assert ok?
1035
+ assert_equal 'foo in subclass', body
1036
+
1037
+ get '/bar'
1038
+ assert ok?
1039
+ assert_equal 'bar in baseclass', body
1040
+ end
1041
+
1042
+ it "adds hostname condition when it is in options" do
1043
+ mock_app {
1044
+ get '/foo', :host => 'host' do
1045
+ 'foo'
1046
+ end
1047
+ }
1048
+
1049
+ get '/foo'
1050
+ assert not_found?
1051
+ end
1052
+
1053
+ it 'allows using call to fire another request internally' do
1054
+ mock_app do
1055
+ get '/foo' do
1056
+ status, headers, body = call env.merge("PATH_INFO" => '/bar')
1057
+ [status, headers, body.each.map(&:upcase)]
1058
+ end
1059
+
1060
+ get '/bar' do
1061
+ "bar"
1062
+ end
1063
+ end
1064
+
1065
+ get '/foo'
1066
+ assert ok?
1067
+ assert_body "BAR"
1068
+ end
1069
+
1070
+ it 'plays well with other routing middleware' do
1071
+ middleware = aldebaran.new
1072
+ inner_app = aldebaran.new { get('/foo') { 'hello' } }
1073
+ builder = Rack::Builder.new do
1074
+ use middleware
1075
+ map('/test') { run inner_app }
1076
+ end
1077
+
1078
+ @app = builder.to_app
1079
+ get '/test/foo'
1080
+ assert ok?
1081
+ assert_body 'hello'
1082
+ end
1083
+
1084
+ it 'returns the route signature' do
1085
+ signature = list = nil
1086
+
1087
+ mock_app do
1088
+ signature = post('/') { }
1089
+ list = routes['POST']
1090
+ end
1091
+
1092
+ assert_equal Array, signature.class
1093
+ assert_equal 4, signature.length
1094
+ assert list.include?(signature)
1095
+ end
1096
+ end