roda 1.3.0 → 2.0.0

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +24 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -4
  5. data/doc/release_notes/2.0.0.txt +75 -0
  6. data/lib/roda/plugins/assets.rb +2 -2
  7. data/lib/roda/plugins/backtracking_array.rb +2 -11
  8. data/lib/roda/plugins/caching.rb +4 -2
  9. data/lib/roda/plugins/chunked.rb +4 -9
  10. data/lib/roda/plugins/class_level_routing.rb +1 -3
  11. data/lib/roda/plugins/default_headers.rb +1 -2
  12. data/lib/roda/plugins/error_email.rb +4 -14
  13. data/lib/roda/plugins/error_handler.rb +4 -4
  14. data/lib/roda/plugins/flash.rb +1 -3
  15. data/lib/roda/plugins/halt.rb +24 -5
  16. data/lib/roda/plugins/header_matchers.rb +2 -7
  17. data/lib/roda/plugins/hooks.rb +1 -3
  18. data/lib/roda/plugins/json.rb +4 -2
  19. data/lib/roda/plugins/mailer.rb +8 -7
  20. data/lib/roda/plugins/middleware.rb +21 -9
  21. data/lib/roda/plugins/not_found.rb +3 -3
  22. data/lib/roda/plugins/padrino_render.rb +60 -0
  23. data/lib/roda/plugins/param_matchers.rb +3 -3
  24. data/lib/roda/plugins/path.rb +2 -1
  25. data/lib/roda/plugins/render.rb +55 -37
  26. data/lib/roda/plugins/render_each.rb +4 -2
  27. data/lib/roda/plugins/static_path_info.rb +2 -63
  28. data/lib/roda/plugins/streaming.rb +4 -2
  29. data/lib/roda/version.rb +2 -2
  30. data/lib/roda.rb +71 -172
  31. data/spec/matchers_spec.rb +31 -82
  32. data/spec/plugin/assets_spec.rb +6 -6
  33. data/spec/plugin/error_handler_spec.rb +23 -0
  34. data/spec/plugin/halt_spec.rb +39 -0
  35. data/spec/plugin/middleware_spec.rb +7 -0
  36. data/spec/plugin/padrino_render_spec.rb +57 -0
  37. data/spec/plugin/render_each_spec.rb +1 -1
  38. data/spec/plugin/render_spec.rb +59 -5
  39. data/spec/request_spec.rb +0 -12
  40. data/spec/response_spec.rb +0 -24
  41. data/spec/views/_test.erb +1 -0
  42. metadata +7 -4
  43. data/lib/roda/plugins/delete_nil_headers.rb +0 -34
  44. data/spec/module_spec.rb +0 -29
data/lib/roda.rb CHANGED
@@ -59,18 +59,6 @@ class Roda
59
59
  @opts = {}
60
60
  @route_block = nil
61
61
 
62
- module RodaDeprecateMutation
63
- [:[]=, :clear, :compare_by_identity, :default=, :default_proc=, :delete, :delete_if,
64
- :keep_if, :merge!, :reject!, :replace, :select!, :shift, :store, :update].each do |m|
65
- class_eval(<<-END, __FILE__, __LINE__+1)
66
- def #{m}(*)
67
- RodaPlugins.deprecate("Mutating this hash (\#{inspect}) via the #{m} method is deprecated, this hash will be frozen in Roda 2.")
68
- super
69
- end
70
- END
71
- end
72
- end
73
-
74
62
  # Module in which all Roda plugins should be stored. Also contains logic for
75
63
  # registering and loading plugins.
76
64
  module RodaPlugins
@@ -98,12 +86,6 @@ class Roda
98
86
  @plugins[name] = mod
99
87
  end
100
88
 
101
- # Emit a deprecation message. By default this just calls warn. You can override this
102
- # method to log deprecation messages to a file or include backtraces (or something else).
103
- def self.deprecate(msg)
104
- warn(msg)
105
- end
106
-
107
89
  # The base plugin for Roda, implementing all default functionality.
108
90
  # Methods are put into a plugin so future plugins can easily override
109
91
  # them and call super to get the default behavior.
@@ -151,11 +133,6 @@ class Roda
151
133
  super
152
134
  end
153
135
 
154
- def hash_matcher(key, &block)
155
- RodaPlugins.deprecate("Roda.hash_matcher is deprecated and will be removed in Roda 2. It has been moved to the hash_matcher plugin.")
156
- self::RodaRequest.send(:define_method, :"match_#{key}", &block)
157
- end
158
-
159
136
  # When inheriting Roda, copy the shared data into the subclass,
160
137
  # and setup the request and response subclasses.
161
138
  def inherited(subclass)
@@ -167,9 +144,6 @@ class Roda
167
144
  subclass.opts.to_a.each do |k,v|
168
145
  if (v.is_a?(Array) || v.is_a?(Hash)) && !v.frozen?
169
146
  subclass.opts[k] = v.dup
170
- if v.is_a?(RodaDeprecateMutation)
171
- subclass.opts[k].extend(RodaDeprecateMutation)
172
- end
173
147
  end
174
148
  end
175
149
  subclass.instance_variable_set(:@route_block, @route_block)
@@ -204,16 +178,6 @@ class Roda
204
178
  plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
205
179
  end
206
180
 
207
- def request_module(mod = nil, &block)
208
- RodaPlugins.deprecate("Roda.request_module is deprecated and will be removed in Roda 2. It has been moved to the module_include plugin.")
209
- module_include(:request, mod, &block)
210
- end
211
-
212
- def response_module(mod = nil, &block)
213
- RodaPlugins.deprecate("Roda.response_module is deprecated and will be removed in Roda 2. It has been moved to the module_include plugin.")
214
- module_include(:response, mod, &block)
215
- end
216
-
217
181
  # Setup routing tree for the current Roda application, and build the
218
182
  # underlying rack application using the stored middleware. Requires
219
183
  # a block, which is yielded the request. By convention, the block
@@ -254,49 +218,37 @@ class Roda
254
218
  if block = @route_block
255
219
  builder = Rack::Builder.new
256
220
  @middleware.each{|a, b| builder.use(*a, &b)}
257
- builder.run lambda{|env| allocate.call(env, &block)}
221
+ builder.run lambda{|env| new(env).call(&block)}
258
222
  @app = builder.to_app
259
223
  end
260
224
  end
261
-
262
- # REMOVE20
263
- def module_include(type, mod)
264
- if type == :response
265
- klass = self::RodaResponse
266
- iv = :@response_module
267
- else
268
- klass = self::RodaRequest
269
- iv = :@request_module
270
- end
271
-
272
- if mod
273
- raise RodaError, "can't provide both argument and block to response_module" if block_given?
274
- klass.send(:include, mod)
275
- else
276
- if instance_variable_defined?(iv)
277
- mod = instance_variable_get(iv)
278
- else
279
- mod = instance_variable_set(iv, Module.new)
280
- klass.send(:include, mod)
281
- end
282
-
283
- mod.module_eval(&Proc.new) if block_given?
284
- end
285
-
286
- mod
287
- end
288
225
  end
289
226
 
290
227
  # Instance methods for the Roda class.
228
+ #
229
+ # In addition to the listed methods, the following two methods are available:
230
+ #
231
+ # request :: The instance of the request class related to this request.
232
+ # This is the same object yielded by Roda.route.
233
+ # response :: The instance of the response class related to this request.
291
234
  module InstanceMethods
292
- # Create a request and response of the appopriate
293
- # class, the instance_exec the route block with
294
- # the request, handling any halts. This is not usually
295
- # called directly.
296
- def call(env, &block)
297
- @_request = self.class::RodaRequest.new(self, env)
298
- @_response = self.class::RodaResponse.new
299
- _route(&block)
235
+ # Create a request and response of the appopriate class
236
+ def initialize(env)
237
+ klass = self.class
238
+ @_request = klass::RodaRequest.new(self, env)
239
+ @_response = klass::RodaResponse.new
240
+ end
241
+
242
+ # instance_exec the route block in the scope of the
243
+ # receiver, with the related request. Catch :halt so that
244
+ # the route block can throw :halt at any point with the
245
+ # rack response to use.
246
+ def call(&block)
247
+ catch(:halt) do
248
+ r = @_request
249
+ r.block_result(instance_exec(r, &block))
250
+ @_response.finish
251
+ end
300
252
  end
301
253
 
302
254
  # The environment hash for the current request. Example:
@@ -317,16 +269,13 @@ class Roda
317
269
  self.class.opts
318
270
  end
319
271
 
320
- # The instance of the request class related to this request.
321
- # This is the same object yielded by Roda.route.
322
- def request
323
- @_request
324
- end
272
+ attr_reader :_request # :nodoc:
273
+ alias request _request
274
+ remove_method :_request
325
275
 
326
- # The instance of the response class related to this request.
327
- def response
328
- @_response
329
- end
276
+ attr_reader :_response # :nodoc:
277
+ alias response _response
278
+ remove_method :_response
330
279
 
331
280
  # The session hash for the current request. Raises RodaError
332
281
  # if no session exists. Example:
@@ -335,18 +284,6 @@ class Roda
335
284
  def session
336
285
  @_request.session
337
286
  end
338
-
339
- private
340
-
341
- # Internals of #call, extracted so that plugins can override
342
- # behavior after the request and response have been setup.
343
- def _route(&block)
344
- catch(:halt) do
345
- r = @_request
346
- r.block_result(instance_exec(r, &block))
347
- @_response.finish
348
- end
349
- end
350
287
  end
351
288
 
352
289
  # Class methods for RodaRequest
@@ -418,6 +355,7 @@ class Roda
418
355
  def initialize(scope, env)
419
356
  @scope = scope
420
357
  @captures = []
358
+ @remaining_path = env[PATH_INFO]
421
359
  super(env)
422
360
  end
423
361
 
@@ -465,7 +403,7 @@ class Roda
465
403
  # executed, and when the match block returns, the rack response is
466
404
  # returned.
467
405
  #
468
- # r.path_info
406
+ # r.remaining_path
469
407
  # # => "/foo/bar"
470
408
  #
471
409
  # r.is 'foo' do
@@ -487,7 +425,7 @@ class Roda
487
425
  # Note that this matches only if the path after matching the arguments
488
426
  # is empty, not if it still contains a trailing slash:
489
427
  #
490
- # r.path_info
428
+ # r.remaining_path
491
429
  # # => "/foo/bar/"
492
430
  #
493
431
  # r.is 'foo/bar' do
@@ -526,7 +464,7 @@ class Roda
526
464
  # path, this is usually used to setup branches of the routing tree,
527
465
  # not for final handling of the request.
528
466
  #
529
- # r.path_info
467
+ # r.remaining_path
530
468
  # # => "/foo/bar"
531
469
  #
532
470
  # r.on 'foo' do
@@ -558,7 +496,8 @@ class Roda
558
496
 
559
497
  # The already matched part of the path, including the original SCRIPT_NAME.
560
498
  def matched_path
561
- @env[SCRIPT_NAME]
499
+ e = @env
500
+ e[SCRIPT_NAME] + e[PATH_INFO].chomp(@remaining_path)
562
501
  end
563
502
 
564
503
  # This an an optimized version of Rack::Request#path.
@@ -572,16 +511,8 @@ class Roda
572
511
  "#{e[SCRIPT_NAME]}#{e[PATH_INFO]}"
573
512
  end
574
513
 
575
- def full_path_info
576
- RodaPlugins.deprecate("RodaRequest#full_path_info is deprecated and will be removed in Roda 2. Switch to using #path.")
577
- path
578
- end
579
-
580
- # The current path to match requests against. This is the same as PATH_INFO
581
- # in the environment, which gets updated as the request is being routed.
582
- def remaining_path
583
- @env[PATH_INFO]
584
- end
514
+ # The current path to match requests against.
515
+ attr_reader :remaining_path
585
516
 
586
517
  # Match POST requests. If no arguments are provided, matches all POST
587
518
  # requests, otherwise, matches only POST requests where the arguments
@@ -637,7 +568,7 @@ class Roda
637
568
  # path is +/+. If it matches, the match block is executed, and when
638
569
  # the match block returns, the rack response is returned.
639
570
  #
640
- # [r.request_method, r.path_info]
571
+ # [r.request_method, r.remaining_path]
641
572
  # # => ['GET', '/']
642
573
  #
643
574
  # r.root do
@@ -646,7 +577,7 @@ class Roda
646
577
  #
647
578
  # This is usuable inside other match blocks:
648
579
  #
649
- # [r.request_method, r.path_info]
580
+ # [r.request_method, r.remaining_path]
650
581
  # # => ['GET', '/foo/']
651
582
  #
652
583
  # r.on 'foo' do
@@ -657,7 +588,7 @@ class Roda
657
588
  #
658
589
  # Note that this does not match non-+GET+ requests:
659
590
  #
660
- # [r.request_method, r.path_info]
591
+ # [r.request_method, r.remaining_path]
661
592
  # # => ['POST', '/']
662
593
  #
663
594
  # r.root do
@@ -669,7 +600,7 @@ class Roda
669
600
  #
670
601
  # Nor does it match empty paths:
671
602
  #
672
- # [r.request_method, r.path_info]
603
+ # [r.request_method, r.remaining_path]
673
604
  # # => ['GET', '/foo']
674
605
  #
675
606
  # r.on 'foo' do
@@ -693,8 +624,25 @@ class Roda
693
624
  # r.run(proc{[403, {}, []]}) unless r['letmein'] == '1'
694
625
  # r.run(proc{[404, {}, []]})
695
626
  # response.status = 404 # not reached
627
+ #
628
+ # This updates SCRIPT_NAME/PATH_INFO based on the current remaining_path
629
+ # before dispatching to another rack app, so the app still works as
630
+ # a URL mapper.
696
631
  def run(app)
697
- throw :halt, app.call(@env)
632
+ e = @env
633
+ path = @remaining_path
634
+ sn = SCRIPT_NAME
635
+ pi = PATH_INFO
636
+ script_name = e[sn]
637
+ path_info = e[pi]
638
+ begin
639
+ e[sn] += path_info.chomp(path)
640
+ e[pi] = path
641
+ throw :halt, app.call(e)
642
+ ensure
643
+ e[sn] = script_name
644
+ e[pi] = path_info
645
+ end
698
646
  end
699
647
 
700
648
  # The session for the current request. Raises a RodaError if
@@ -780,7 +728,7 @@ class Roda
780
728
  # path from PATH_INFO, and updates captures with any regex captures.
781
729
  def consume(pattern)
782
730
  if matchdata = remaining_path.match(pattern)
783
- update_remaining_path(matchdata.post_match)
731
+ @remaining_path = matchdata.post_match
784
732
  @captures.concat(matchdata.captures)
785
733
  end
786
734
  end
@@ -813,29 +761,18 @@ class Roda
813
761
  # returns the rack response when the block returns. If any of
814
762
  # the match arguments doesn't match, does nothing.
815
763
  def if_match(args)
816
- keep_remaining_path do
817
- # For every block, we make sure to reset captures so that
818
- # nesting matchers won't mess with each other's captures.
819
- @captures.clear
820
-
821
- return unless match_all(args)
822
- block_result(yield(*captures))
823
- throw :halt, response.finish
824
- end
825
- end
826
-
827
- # Yield to the block, restoring SCRIPT_NAME and PATH_INFO to
828
- # their initial values before returning from the block.
829
- def keep_remaining_path
830
- env = @env
831
- script = env[sn = SCRIPT_NAME]
832
- path = env[pi = PATH_INFO]
833
- yield
764
+ path = @remaining_path
765
+ # For every block, we make sure to reset captures so that
766
+ # nesting matchers won't mess with each other's captures.
767
+ @captures.clear
768
+
769
+ return unless match_all(args)
770
+ block_result(yield(*captures))
771
+ throw :halt, response.finish
834
772
  ensure
835
- env[sn] = script
836
- env[pi] = path
773
+ @remaining_path = path
837
774
  end
838
-
775
+
839
776
  # Attempt to match the argument to the given request, handling
840
777
  # common ruby types.
841
778
  def match(matcher)
@@ -864,11 +801,6 @@ class Roda
864
801
  args.all?{|arg| match(arg)}
865
802
  end
866
803
 
867
- def match_extension(ext)
868
- RodaPlugins.deprecate("The :extension matcher is deprecated and will be removed in Roda 2. It has been moved to the path_matchers plugin.")
869
- consume(self.class.cached_matcher([:extension, ext]){/([^\\\/]+)\.#{ext}/})
870
- end
871
-
872
804
  # Match by request method. This can be an array if you want
873
805
  # to match on multiple methods.
874
806
  def match_method(type)
@@ -878,29 +810,6 @@ class Roda
878
810
  type.to_s.upcase == @env[REQUEST_METHOD]
879
811
  end
880
812
  end
881
-
882
- def match_param(key)
883
- RodaPlugins.deprecate("The :param matcher is deprecated and will be removed in Roda 2. It has been moved to the param_matchers plugin.")
884
- if v = self[key]
885
- @captures << v
886
- end
887
- end
888
-
889
- def match_param!(key)
890
- RodaPlugins.deprecate("The :param! matcher is deprecated and will be removed in Roda 2. It has been moved to the param_matchers plugin.")
891
- if (v = self[key]) && !v.empty?
892
- @captures << v
893
- end
894
- end
895
-
896
- # Update PATH_INFO and SCRIPT_NAME based on the matchend and remaining variables.
897
- def update_remaining_path(remaining)
898
- e = @env
899
-
900
- # Don't mutate SCRIPT_NAME, breaks try
901
- e[SCRIPT_NAME] += e[pi = PATH_INFO].chomp(remaining)
902
- e[pi] = remaining
903
- end
904
813
  end
905
814
 
906
815
  # Class methods for RodaResponse
@@ -959,11 +868,6 @@ class Roda
959
868
  DEFAULT_HEADERS
960
869
  end
961
870
 
962
- def delete_cookie(key, value = {})
963
- RodaPlugins.deprecate("RodaResponse#delete_cookie is deprecated and will be removed in Roda 2. It has been moved to the cookies plugin.")
964
- ::Rack::Utils.delete_cookie_header!(@headers, key, value)
965
- end
966
-
967
871
  # Whether the response body has been written to yet. Note
968
872
  # that writing an empty string to the response body marks
969
873
  # the response as not empty. Example:
@@ -1025,11 +929,6 @@ class Roda
1025
929
  self.class.roda_class
1026
930
  end
1027
931
 
1028
- def set_cookie(key, value)
1029
- RodaPlugins.deprecate("RodaResponse#set_cookie is deprecated and will be removed in Roda 2. It has been moved to the cookies plugin.")
1030
- ::Rack::Utils.set_cookie_header!(@headers, key, value)
1031
- end
1032
-
1033
932
  # Write to the response body. Returns nil.
1034
933
  #
1035
934
  # response.write('foo')
@@ -291,20 +291,48 @@ describe "r.on" do
291
291
  body("/123").should == '+1'
292
292
  end
293
293
 
294
- it "ensures SCRIPT_NAME and PATH_INFO are reverted" do
294
+ it "ensures remaining_path is reverted if modified in failing matcher" do
295
295
  app do |r|
296
- r.on lambda { r.env["SCRIPT_NAME"] = "/hello"; false } do
296
+ r.on lambda { @remaining_path = "/blah"; false } do
297
297
  "Unreachable"
298
298
  end
299
299
 
300
300
  r.on do
301
- r.env["SCRIPT_NAME"] + ':' + r.env["PATH_INFO"]
301
+ r.matched_path + ':' + r.remaining_path
302
302
  end
303
303
  end
304
304
 
305
305
  body("/hello").should == ':/hello'
306
306
  end
307
307
 
308
+ it "modifies matched_path/remaining_path during routing" do
309
+ app do |r|
310
+ r.on 'login', 'foo' do
311
+ "Unreachable"
312
+ end
313
+
314
+ r.on 'hello' do
315
+ r.matched_path + ':' + r.remaining_path
316
+ end
317
+ end
318
+
319
+ body("/hello/you").should == '/hello:/you'
320
+ end
321
+
322
+ it "doesn't modify SCRIPT_NAME/PATH_INFO during routing" do
323
+ app do |r|
324
+ r.on 'login', 'foo' do
325
+ "Unreachable"
326
+ end
327
+
328
+ r.on 'hello' do
329
+ r.env["SCRIPT_NAME"] + ':' + r.env["PATH_INFO"]
330
+ end
331
+ end
332
+
333
+ body("/hello/you").should == ':/hello/you'
334
+ end
335
+
308
336
  it "doesn't mutate SCRIPT_NAME or PATH_INFO after request is returned" do
309
337
  app do |r|
310
338
  r.on 'login', 'foo' do
@@ -377,44 +405,6 @@ describe "r.on" do
377
405
  end
378
406
  end
379
407
 
380
- describe "param! matcher" do
381
- it "should yield a param only if given and not empty" do
382
- app do |r|
383
- r.get "signup", :param! => "email" do |email|
384
- email
385
- end
386
-
387
- r.on do
388
- "No email"
389
- end
390
- end
391
-
392
- io = StringIO.new
393
- body("/signup", "rack.input" => io, "QUERY_STRING" => "email=john@doe.com").should == 'john@doe.com'
394
- body("/signup", "rack.input" => io, "QUERY_STRING" => "").should == 'No email'
395
- body("/signup", "rack.input" => io, "QUERY_STRING" => "email=").should == 'No email'
396
- end
397
- end
398
-
399
- describe "param matcher" do
400
- it "should yield a param only if given" do
401
- app do |r|
402
- r.get "signup", :param=>"email" do |email|
403
- email
404
- end
405
-
406
- r.on do
407
- "No email"
408
- end
409
- end
410
-
411
- io = StringIO.new
412
- body("/signup", "rack.input" => io, "QUERY_STRING" => "email=john@doe.com").should == 'john@doe.com'
413
- body("/signup", "rack.input" => io, "QUERY_STRING" => "").should == 'No email'
414
- body("/signup", "rack.input" => io, "QUERY_STRING" => "email=").should == ''
415
- end
416
- end
417
-
418
408
  describe "path matchers" do
419
409
  it "one level path" do
420
410
  app do |r|
@@ -644,21 +634,6 @@ describe "all matcher" do
644
634
  end
645
635
  end
646
636
 
647
- describe "extension matcher" do
648
- it "should match given file extensions" do
649
- app do |r|
650
- r.on "css" do
651
- r.on :extension=>"css" do |file|
652
- file
653
- end
654
- end
655
- end
656
-
657
- body("/css/reset.css").should == 'reset'
658
- status("/css/reset.bar").should == 404
659
- end
660
- end
661
-
662
637
  describe "method matcher" do
663
638
  it "should match given request types" do
664
639
  app do |r|
@@ -686,29 +661,3 @@ describe "route block that returns string" do
686
661
  body.should == '+1'
687
662
  end
688
663
  end
689
-
690
- describe "hash_matcher" do
691
- it "should enable the handling of arbitrary hash keys" do
692
- app(:bare) do
693
- hash_matcher(:foos){|v| consume(self.class.cached_matcher(:"foos-#{v}"){/((?:foo){#{v}})/})}
694
- route do |r|
695
- r.is :foos=>1 do |f|
696
- "1#{f}"
697
- end
698
- r.is :foos=>2 do |f|
699
- "2#{f}"
700
- end
701
- r.is :foos=>3 do |f|
702
- "3#{f}"
703
- end
704
- end
705
- end
706
-
707
- body("/foo").should == '1foo'
708
- body("/foofoo").should == '2foofoo'
709
- body("/foofoofoo").should == '3foofoofoo'
710
- status("/foofoofoofoo").should == 404
711
- status.should == 404
712
- end
713
- end
714
-
@@ -332,11 +332,11 @@ if run_tests
332
332
  end
333
333
 
334
334
  it '#assets should include attributes given' do
335
- app.new.assets([:js, :head], 'a'=>'b').should == '<script type="text/javascript" a="b" src="/assets/js/head/app.js"></script>'
335
+ app.allocate.assets([:js, :head], 'a'=>'b').should == '<script type="text/javascript" a="b" src="/assets/js/head/app.js"></script>'
336
336
  end
337
337
 
338
338
  it '#assets should escape attribute values given' do
339
- app.new.assets([:js, :head], 'a'=>'b"e').should == '<script type="text/javascript" a="b&quot;e" src="/assets/js/head/app.js"></script>'
339
+ app.allocate.assets([:js, :head], 'a'=>'b"e').should == '<script type="text/javascript" a="b&quot;e" src="/assets/js/head/app.js"></script>'
340
340
  end
341
341
 
342
342
  it 'requests for assets should return 304 if the asset has not been modified' do
@@ -397,17 +397,17 @@ if run_tests
397
397
  it 'should support :precompiled option' do
398
398
  app.plugin :assets, :precompiled=>metadata_file
399
399
  File.exist?(metadata_file).should == false
400
- app.new.assets([:js, :head]).should == '<script type="text/javascript" src="/assets/js/head/app.js"></script>'
400
+ app.allocate.assets([:js, :head]).should == '<script type="text/javascript" src="/assets/js/head/app.js"></script>'
401
401
 
402
402
  app.compile_assets
403
403
  File.exist?(metadata_file).should == true
404
- app.new.assets([:js, :head]).should =~ %r{src="(/assets/app\.head\.[a-f0-9]{40}\.js)"}
404
+ app.allocate.assets([:js, :head]).should =~ %r{src="(/assets/app\.head\.[a-f0-9]{40}\.js)"}
405
405
 
406
406
  app.plugin :assets, :compiled=>false, :precompiled=>false
407
- app.new.assets([:js, :head]).should == '<script type="text/javascript" src="/assets/js/head/app.js"></script>'
407
+ app.allocate.assets([:js, :head]).should == '<script type="text/javascript" src="/assets/js/head/app.js"></script>'
408
408
 
409
409
  app.plugin :assets, :precompiled=>metadata_file
410
- app.new.assets([:js, :head]).should =~ %r{src="(/assets/app\.head\.[a-f0-9]{40}\.js)"}
410
+ app.allocate.assets([:js, :head]).should =~ %r{src="(/assets/app\.head\.[a-f0-9]{40}\.js)"}
411
411
  end
412
412
  end
413
413
  end
@@ -24,6 +24,29 @@ describe "error_handler plugin" do
24
24
  status.should == 500
25
25
  end
26
26
 
27
+ it "executes on SyntaxError exceptions" do
28
+ app(:bare) do
29
+ plugin :error_handler
30
+
31
+ error do |e|
32
+ e.message
33
+ end
34
+
35
+ route do |r|
36
+ r.on "a" do
37
+ "found"
38
+ end
39
+
40
+ raise SyntaxError, 'bad idea'
41
+ end
42
+ end
43
+
44
+ body("/a").should == 'found'
45
+ status("/a").should == 200
46
+ body.should == 'bad idea'
47
+ status.should == 500
48
+ end
49
+
27
50
  it "can override status inside error block" do
28
51
  app(:bare) do
29
52
  plugin :error_handler do |e|
@@ -25,6 +25,18 @@ describe "halt plugin" do
25
25
  status.should == 300
26
26
  end
27
27
 
28
+ it "should consider other single arguments similar to block bodies" do
29
+ app(:bare) do
30
+ plugin :halt
31
+ plugin :json
32
+ route do |r|
33
+ r.halt({'a'=>1})
34
+ end
35
+ end
36
+
37
+ body.should == '{"a":1}'
38
+ end
39
+
28
40
  it "should consider 2 arguments as response status and body" do
29
41
  app(:halt) do |r|
30
42
  r.halt 300, "foo"
@@ -34,6 +46,19 @@ describe "halt plugin" do
34
46
  body.should == "foo"
35
47
  end
36
48
 
49
+ it "should handle 2nd of 2 arguments similar to block bodies" do
50
+ app(:bare) do
51
+ plugin :halt
52
+ plugin :json
53
+ route do |r|
54
+ r.halt(300, {'a'=>1})
55
+ end
56
+ end
57
+
58
+ status.should == 300
59
+ body.should == '{"a":1}'
60
+ end
61
+
37
62
  it "should consider 3 arguments as response" do
38
63
  app(:halt) do |r|
39
64
  r.halt 300, {'a'=>'b'}, "foo"
@@ -44,6 +69,20 @@ describe "halt plugin" do
44
69
  body.should == "foo"
45
70
  end
46
71
 
72
+ it "should handle 3rd of 3 arguments similar to block bodies" do
73
+ app(:bare) do
74
+ plugin :halt
75
+ plugin :json
76
+ route do |r|
77
+ r.halt(300, {'a'=>'b'}, {'a'=>1})
78
+ end
79
+ end
80
+
81
+ status.should == 300
82
+ header('a').should == 'b'
83
+ body.should == '{"a":1}'
84
+ end
85
+
47
86
  it "should raise an error for too many arguments" do
48
87
  app(:halt) do |r|
49
88
  r.halt 300, {'a'=>'b'}, "foo", 1