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,20 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path('../helper', __FILE__)
3
+ require 'erb'
4
+
5
+ class BaseTest < Test::Unit::TestCase
6
+ setup do
7
+ @base = aldebaran.new(aldebaran::Base)
8
+ @base.set :views, File.dirname(__FILE__) + "/views"
9
+ end
10
+
11
+ it 'allows unicode strings in ascii templates per default (1.9)' do
12
+ next unless defined? Encoding
13
+ @base.new!.erb(File.read(@base.views + "/ascii.erb").encode("ASCII"), {}, :value => "åkej")
14
+ end
15
+
16
+ it 'allows ascii strings in unicode templates per default (1.9)' do
17
+ next unless defined? Encoding
18
+ @base.new!.erb(:utf8, {}, :value => "Some Lyrics".encode("ASCII"))
19
+ end
20
+ end
@@ -0,0 +1,104 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class ERBTest < Test::Unit::TestCase
4
+ def engine
5
+ Tilt::ERBTemplate
6
+ end
7
+
8
+ def setup
9
+ Tilt.prefer engine, :erb
10
+ super
11
+ end
12
+
13
+ def erb_app(&block)
14
+ mock_app {
15
+ set :views, File.dirname(__FILE__) + '/views'
16
+ get '/', &block
17
+ }
18
+ get '/'
19
+ end
20
+
21
+ it 'uses the correct engine' do
22
+ assert_equal engine, Tilt[:erb]
23
+ end
24
+
25
+ it 'renders inline ERB strings' do
26
+ erb_app { erb '<%= 1 + 1 %>' }
27
+ assert ok?
28
+ assert_equal '2', body
29
+ end
30
+
31
+ it 'renders .erb files in views path' do
32
+ erb_app { erb :hello }
33
+ assert ok?
34
+ assert_equal "Hello World\n", body
35
+ end
36
+
37
+ it 'takes a :locals option' do
38
+ erb_app {
39
+ locals = {:foo => 'Bar'}
40
+ erb '<%= foo %>', :locals => locals
41
+ }
42
+ assert ok?
43
+ assert_equal 'Bar', body
44
+ end
45
+
46
+ it "renders with inline layouts" do
47
+ mock_app {
48
+ layout { 'THIS. IS. <%= yield.upcase %>!' }
49
+ get('/') { erb 'Sparta' }
50
+ }
51
+ get '/'
52
+ assert ok?
53
+ assert_equal 'THIS. IS. SPARTA!', body
54
+ end
55
+
56
+ it "renders with file layouts" do
57
+ erb_app {
58
+ erb 'Hello World', :layout => :layout2
59
+ }
60
+ assert ok?
61
+ assert_body "ERB Layout!\nHello World"
62
+ end
63
+
64
+ it "renders erb with blocks" do
65
+ mock_app {
66
+ def container
67
+ @_out_buf << "THIS."
68
+ yield
69
+ @_out_buf << "SPARTA!"
70
+ end
71
+ def is; "IS." end
72
+ get '/' do
73
+ erb '<% container do %> <%= is %> <% end %>'
74
+ end
75
+ }
76
+ get '/'
77
+ assert ok?
78
+ assert_equal 'THIS. IS. SPARTA!', body
79
+ end
80
+
81
+ it "can be used in a nested fashion for partials and whatnot" do
82
+ mock_app {
83
+ template(:inner) { "<inner><%= 'hi' %></inner>" }
84
+ template(:outer) { "<outer><%= erb :inner %></outer>" }
85
+ get '/' do
86
+ erb :outer
87
+ end
88
+ }
89
+
90
+ get '/'
91
+ assert ok?
92
+ assert_equal '<outer><inner>hi</inner></outer>', body
93
+ end
94
+ end
95
+
96
+
97
+ begin
98
+ require 'erubis'
99
+ class ErubisTest < ERBTest
100
+ def engine; Tilt::ErubisTemplate end
101
+ end
102
+ rescue LoadError
103
+ warn "#{$!.to_s}: skipping erubis tests"
104
+ end
@@ -0,0 +1,100 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class ExtensionsTest < Test::Unit::TestCase
4
+ module FooExtensions
5
+ def foo
6
+ end
7
+
8
+ private
9
+ def im_hiding_in_ur_foos
10
+ end
11
+ end
12
+
13
+ module BarExtensions
14
+ def bar
15
+ end
16
+ end
17
+
18
+ module BazExtensions
19
+ def baz
20
+ end
21
+ end
22
+
23
+ module QuuxExtensions
24
+ def quux
25
+ end
26
+ end
27
+
28
+ module PainExtensions
29
+ def foo=(name); end
30
+ def bar?(name); end
31
+ def fizz!(name); end
32
+ end
33
+
34
+ it 'will add the methods to the DSL for the class in which you register them and its subclasses' do
35
+ aldebaran::Base.register FooExtensions
36
+ assert aldebaran::Base.respond_to?(:foo)
37
+
38
+ aldebaran::Application.register BarExtensions
39
+ assert aldebaran::Application.respond_to?(:bar)
40
+ assert aldebaran::Application.respond_to?(:foo)
41
+ assert !aldebaran::Base.respond_to?(:bar)
42
+ end
43
+
44
+ it 'allows extending by passing a block' do
45
+ aldebaran::Base.register {
46
+ def im_in_ur_anonymous_module; end
47
+ }
48
+ assert aldebaran::Base.respond_to?(:im_in_ur_anonymous_module)
49
+ end
50
+
51
+ it 'will make sure any public methods added via Application#register are delegated to aldebaran::Delegator' do
52
+ aldebaran::Application.register FooExtensions
53
+ assert aldebaran::Delegator.private_instance_methods.
54
+ map { |m| m.to_sym }.include?(:foo)
55
+ assert !aldebaran::Delegator.private_instance_methods.
56
+ map { |m| m.to_sym }.include?(:im_hiding_in_ur_foos)
57
+ end
58
+
59
+ it 'will handle special method names' do
60
+ aldebaran::Application.register PainExtensions
61
+ assert aldebaran::Delegator.private_instance_methods.
62
+ map { |m| m.to_sym }.include?(:foo=)
63
+ assert aldebaran::Delegator.private_instance_methods.
64
+ map { |m| m.to_sym }.include?(:bar?)
65
+ assert aldebaran::Delegator.private_instance_methods.
66
+ map { |m| m.to_sym }.include?(:fizz!)
67
+ end
68
+
69
+ it 'will not delegate methods on Base#register' do
70
+ aldebaran::Base.register QuuxExtensions
71
+ assert !aldebaran::Delegator.private_instance_methods.include?("quux")
72
+ end
73
+
74
+ it 'will extend the aldebaran::Application application by default' do
75
+ aldebaran.register BazExtensions
76
+ assert !aldebaran::Base.respond_to?(:baz)
77
+ assert aldebaran::Application.respond_to?(:baz)
78
+ end
79
+
80
+ module BizzleExtension
81
+ def bizzle
82
+ bizzle_option
83
+ end
84
+
85
+ def self.registered(base)
86
+ fail "base should be BizzleApp" unless base == BizzleApp
87
+ fail "base should have already extended BizzleExtension" unless base.respond_to?(:bizzle)
88
+ base.set :bizzle_option, 'bizzle!'
89
+ end
90
+ end
91
+
92
+ class BizzleApp < aldebaran::Base
93
+ end
94
+
95
+ it 'sends .registered to the extension module after extending the class' do
96
+ BizzleApp.register BizzleExtension
97
+ assert_equal 'bizzle!', BizzleApp.bizzle_option
98
+ assert_equal 'bizzle!', BizzleApp.bizzle
99
+ end
100
+ end
@@ -0,0 +1,397 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class BeforeFilterTest < Test::Unit::TestCase
4
+ it "executes filters in the order defined" do
5
+ count = 0
6
+ mock_app do
7
+ get('/') { 'Hello World' }
8
+ before {
9
+ assert_equal 0, count
10
+ count = 1
11
+ }
12
+ before {
13
+ assert_equal 1, count
14
+ count = 2
15
+ }
16
+ end
17
+
18
+ get '/'
19
+ assert ok?
20
+ assert_equal 2, count
21
+ assert_equal 'Hello World', body
22
+ end
23
+
24
+ it "can modify the request" do
25
+ mock_app {
26
+ get('/foo') { 'foo' }
27
+ get('/bar') { 'bar' }
28
+ before { request.path_info = '/bar' }
29
+ }
30
+
31
+ get '/foo'
32
+ assert ok?
33
+ assert_equal 'bar', body
34
+ end
35
+
36
+ it "can modify instance variables available to routes" do
37
+ mock_app {
38
+ before { @foo = 'bar' }
39
+ get('/foo') { @foo }
40
+ }
41
+
42
+ get '/foo'
43
+ assert ok?
44
+ assert_equal 'bar', body
45
+ end
46
+
47
+ it "allows redirects" do
48
+ mock_app {
49
+ before { redirect '/bar' }
50
+ get('/foo') do
51
+ fail 'before block should have halted processing'
52
+ 'ORLY?!'
53
+ end
54
+ }
55
+
56
+ get '/foo'
57
+ assert redirect?
58
+ assert_equal 'http://example.org/bar', response['Location']
59
+ assert_equal '', body
60
+ end
61
+
62
+ it "does not modify the response with its return value" do
63
+ mock_app {
64
+ before { 'Hello World!' }
65
+ get '/foo' do
66
+ assert_equal [], response.body
67
+ 'cool'
68
+ end
69
+ }
70
+
71
+ get '/foo'
72
+ assert ok?
73
+ assert_equal 'cool', body
74
+ end
75
+
76
+ it "does modify the response with halt" do
77
+ mock_app {
78
+ before { halt 302, 'Hi' }
79
+ get '/foo' do
80
+ "should not happen"
81
+ end
82
+ }
83
+
84
+ get '/foo'
85
+ assert_equal 302, response.status
86
+ assert_equal 'Hi', body
87
+ end
88
+
89
+ it "gives you access to params" do
90
+ mock_app {
91
+ before { @foo = params['foo'] }
92
+ get('/foo') { @foo }
93
+ }
94
+
95
+ get '/foo?foo=cool'
96
+ assert ok?
97
+ assert_equal 'cool', body
98
+ end
99
+
100
+ it "runs filters defined in superclasses" do
101
+ base = Class.new(aldebaran::Base)
102
+ base.before { @foo = 'hello from superclass' }
103
+
104
+ mock_app(base) {
105
+ get('/foo') { @foo }
106
+ }
107
+
108
+ get '/foo'
109
+ assert_equal 'hello from superclass', body
110
+ end
111
+
112
+ it 'does not run before filter when serving static files' do
113
+ ran_filter = false
114
+ mock_app {
115
+ before { ran_filter = true }
116
+ set :static, true
117
+ set :public_folder, File.dirname(__FILE__)
118
+ }
119
+ get "/#{File.basename(__FILE__)}"
120
+ assert ok?
121
+ assert_equal File.read(__FILE__), body
122
+ assert !ran_filter
123
+ end
124
+
125
+ it 'takes an optional route pattern' do
126
+ ran_filter = false
127
+ mock_app do
128
+ before("/b*") { ran_filter = true }
129
+ get('/foo') { }
130
+ get('/bar') { }
131
+ end
132
+ get '/foo'
133
+ assert !ran_filter
134
+ get '/bar'
135
+ assert ran_filter
136
+ end
137
+
138
+ it 'generates block arguments from route pattern' do
139
+ subpath = nil
140
+ mock_app do
141
+ before("/foo/:sub") { |s| subpath = s }
142
+ get('/foo/*') { }
143
+ end
144
+ get '/foo/bar'
145
+ assert_equal subpath, 'bar'
146
+ end
147
+ end
148
+
149
+ class AfterFilterTest < Test::Unit::TestCase
150
+ it "executes filters in the order defined" do
151
+ invoked = 0
152
+ mock_app do
153
+ before { invoked = 2 }
154
+ get('/') { invoked += 2 }
155
+ after { invoked *= 2 }
156
+ end
157
+
158
+ get '/'
159
+ assert ok?
160
+
161
+ assert_equal 8, invoked
162
+ end
163
+
164
+ it "executes filters in the order defined" do
165
+ count = 0
166
+ mock_app do
167
+ get('/') { 'Hello World' }
168
+ after {
169
+ assert_equal 0, count
170
+ count = 1
171
+ }
172
+ after {
173
+ assert_equal 1, count
174
+ count = 2
175
+ }
176
+ end
177
+
178
+ get '/'
179
+ assert ok?
180
+ assert_equal 2, count
181
+ assert_equal 'Hello World', body
182
+ end
183
+
184
+ it "allows redirects" do
185
+ mock_app {
186
+ get('/foo') { 'ORLY' }
187
+ after { redirect '/bar' }
188
+ }
189
+
190
+ get '/foo'
191
+ assert redirect?
192
+ assert_equal 'http://example.org/bar', response['Location']
193
+ assert_equal '', body
194
+ end
195
+
196
+ it "does not modify the response with its return value" do
197
+ mock_app {
198
+ get('/foo') { 'cool' }
199
+ after { 'Hello World!' }
200
+ }
201
+
202
+ get '/foo'
203
+ assert ok?
204
+ assert_equal 'cool', body
205
+ end
206
+
207
+ it "does modify the response with halt" do
208
+ mock_app {
209
+ get '/foo' do
210
+ "should not be returned"
211
+ end
212
+ after { halt 302, 'Hi' }
213
+ }
214
+
215
+ get '/foo'
216
+ assert_equal 302, response.status
217
+ assert_equal 'Hi', body
218
+ end
219
+
220
+ it "runs filters defined in superclasses" do
221
+ count = 2
222
+ base = Class.new(aldebaran::Base)
223
+ base.after { count *= 2 }
224
+ mock_app(base) do
225
+ get('/foo') do
226
+ count += 2
227
+ "ok"
228
+ end
229
+ end
230
+
231
+ get '/foo'
232
+ assert_equal 8, count
233
+ end
234
+
235
+ it 'does not run after filter when serving static files' do
236
+ ran_filter = false
237
+ mock_app {
238
+ after { ran_filter = true }
239
+ set :static, true
240
+ set :public_folder, File.dirname(__FILE__)
241
+ }
242
+ get "/#{File.basename(__FILE__)}"
243
+ assert ok?
244
+ assert_equal File.read(__FILE__), body
245
+ assert !ran_filter
246
+ end
247
+
248
+ it 'takes an optional route pattern' do
249
+ ran_filter = false
250
+ mock_app do
251
+ after("/b*") { ran_filter = true }
252
+ get('/foo') { }
253
+ get('/bar') { }
254
+ end
255
+ get '/foo'
256
+ assert !ran_filter
257
+ get '/bar'
258
+ assert ran_filter
259
+ end
260
+
261
+ it 'changes to path_info from a pattern matching before filter are respoected when routing' do
262
+ mock_app do
263
+ before('/foo') { request.path_info = '/bar' }
264
+ get('/bar') { 'blah' }
265
+ end
266
+ get '/foo'
267
+ assert ok?
268
+ assert_equal 'blah', body
269
+ end
270
+
271
+ it 'generates block arguments from route pattern' do
272
+ subpath = nil
273
+ mock_app do
274
+ after("/foo/:sub") { |s| subpath = s }
275
+ get('/foo/*') { }
276
+ end
277
+ get '/foo/bar'
278
+ assert_equal subpath, 'bar'
279
+ end
280
+
281
+ it 'is possible to access url params from the route param' do
282
+ ran = false
283
+ mock_app do
284
+ get('/foo/*') { }
285
+ before('/foo/:sub') do
286
+ assert_equal params[:sub], 'bar'
287
+ ran = true
288
+ end
289
+ end
290
+ get '/foo/bar'
291
+ assert ran
292
+ end
293
+
294
+ it 'is possible to apply host_name conditions to before filters with no path' do
295
+ ran = false
296
+ mock_app do
297
+ before(:host_name => 'example.com') { ran = true }
298
+ get('/') { 'welcome' }
299
+ end
300
+ get '/', {}, { 'HTTP_HOST' => 'example.org' }
301
+ assert !ran
302
+ get '/', {}, { 'HTTP_HOST' => 'example.com' }
303
+ assert ran
304
+ end
305
+
306
+ it 'is possible to apply host_name conditions to before filters with a path' do
307
+ ran = false
308
+ mock_app do
309
+ before('/foo', :host_name => 'example.com') { ran = true }
310
+ get('/') { 'welcome' }
311
+ end
312
+ get '/', {}, { 'HTTP_HOST' => 'example.com' }
313
+ assert !ran
314
+ get '/foo', {}, { 'HTTP_HOST' => 'example.org' }
315
+ assert !ran
316
+ get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
317
+ assert ran
318
+ end
319
+
320
+ it 'is possible to apply host_name conditions to after filters with no path' do
321
+ ran = false
322
+ mock_app do
323
+ after(:host_name => 'example.com') { ran = true }
324
+ get('/') { 'welcome' }
325
+ end
326
+ get '/', {}, { 'HTTP_HOST' => 'example.org' }
327
+ assert !ran
328
+ get '/', {}, { 'HTTP_HOST' => 'example.com' }
329
+ assert ran
330
+ end
331
+
332
+ it 'is possible to apply host_name conditions to after filters with a path' do
333
+ ran = false
334
+ mock_app do
335
+ after('/foo', :host_name => 'example.com') { ran = true }
336
+ get('/') { 'welcome' }
337
+ end
338
+ get '/', {}, { 'HTTP_HOST' => 'example.com' }
339
+ assert !ran
340
+ get '/foo', {}, { 'HTTP_HOST' => 'example.org' }
341
+ assert !ran
342
+ get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
343
+ assert ran
344
+ end
345
+
346
+ it 'is possible to apply user_agent conditions to before filters with no path' do
347
+ ran = false
348
+ mock_app do
349
+ before(:user_agent => /foo/) { ran = true }
350
+ get('/') { 'welcome' }
351
+ end
352
+ get '/', {}, { 'HTTP_USER_AGENT' => 'bar' }
353
+ assert !ran
354
+ get '/', {}, { 'HTTP_USER_AGENT' => 'foo' }
355
+ assert ran
356
+ end
357
+
358
+ it 'is possible to apply user_agent conditions to before filters with a path' do
359
+ ran = false
360
+ mock_app do
361
+ before('/foo', :user_agent => /foo/) { ran = true }
362
+ get('/') { 'welcome' }
363
+ end
364
+ get '/', {}, { 'HTTP_USER_AGENT' => 'foo' }
365
+ assert !ran
366
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'bar' }
367
+ assert !ran
368
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'foo' }
369
+ assert ran
370
+ end
371
+
372
+ it 'is possible to apply user_agent conditions to after filters with no path' do
373
+ ran = false
374
+ mock_app do
375
+ after(:user_agent => /foo/) { ran = true }
376
+ get('/') { 'welcome' }
377
+ end
378
+ get '/', {}, { 'HTTP_USER_AGENT' => 'bar' }
379
+ assert !ran
380
+ get '/', {}, { 'HTTP_USER_AGENT' => 'foo' }
381
+ assert ran
382
+ end
383
+
384
+ it 'is possible to apply user_agent conditions to before filters with a path' do
385
+ ran = false
386
+ mock_app do
387
+ after('/foo', :user_agent => /foo/) { ran = true }
388
+ get('/') { 'welcome' }
389
+ end
390
+ get '/', {}, { 'HTTP_USER_AGENT' => 'foo' }
391
+ assert !ran
392
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'bar' }
393
+ assert !ran
394
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'foo' }
395
+ assert ran
396
+ end
397
+ end