roda 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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