roda 1.3.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +24 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -4
- data/doc/release_notes/2.0.0.txt +75 -0
- data/lib/roda/plugins/assets.rb +2 -2
- data/lib/roda/plugins/backtracking_array.rb +2 -11
- data/lib/roda/plugins/caching.rb +4 -2
- data/lib/roda/plugins/chunked.rb +4 -9
- data/lib/roda/plugins/class_level_routing.rb +1 -3
- data/lib/roda/plugins/default_headers.rb +1 -2
- data/lib/roda/plugins/error_email.rb +4 -14
- data/lib/roda/plugins/error_handler.rb +4 -4
- data/lib/roda/plugins/flash.rb +1 -3
- data/lib/roda/plugins/halt.rb +24 -5
- data/lib/roda/plugins/header_matchers.rb +2 -7
- data/lib/roda/plugins/hooks.rb +1 -3
- data/lib/roda/plugins/json.rb +4 -2
- data/lib/roda/plugins/mailer.rb +8 -7
- data/lib/roda/plugins/middleware.rb +21 -9
- data/lib/roda/plugins/not_found.rb +3 -3
- data/lib/roda/plugins/padrino_render.rb +60 -0
- data/lib/roda/plugins/param_matchers.rb +3 -3
- data/lib/roda/plugins/path.rb +2 -1
- data/lib/roda/plugins/render.rb +55 -37
- data/lib/roda/plugins/render_each.rb +4 -2
- data/lib/roda/plugins/static_path_info.rb +2 -63
- data/lib/roda/plugins/streaming.rb +4 -2
- data/lib/roda/version.rb +2 -2
- data/lib/roda.rb +71 -172
- data/spec/matchers_spec.rb +31 -82
- data/spec/plugin/assets_spec.rb +6 -6
- data/spec/plugin/error_handler_spec.rb +23 -0
- data/spec/plugin/halt_spec.rb +39 -0
- data/spec/plugin/middleware_spec.rb +7 -0
- data/spec/plugin/padrino_render_spec.rb +57 -0
- data/spec/plugin/render_each_spec.rb +1 -1
- data/spec/plugin/render_spec.rb +59 -5
- data/spec/request_spec.rb +0 -12
- data/spec/response_spec.rb +0 -24
- data/spec/views/_test.erb +1 -0
- metadata +7 -4
- data/lib/roda/plugins/delete_nil_headers.rb +0 -34
- 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|
|
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
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
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
|
-
|
321
|
-
|
322
|
-
|
323
|
-
@_request
|
324
|
-
end
|
272
|
+
attr_reader :_request # :nodoc:
|
273
|
+
alias request _request
|
274
|
+
remove_method :_request
|
325
275
|
|
326
|
-
|
327
|
-
|
328
|
-
|
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.
|
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.
|
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.
|
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
|
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
|
-
|
576
|
-
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
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
|
-
|
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')
|
data/spec/matchers_spec.rb
CHANGED
@@ -291,20 +291,48 @@ describe "r.on" do
|
|
291
291
|
body("/123").should == '+1'
|
292
292
|
end
|
293
293
|
|
294
|
-
it "ensures
|
294
|
+
it "ensures remaining_path is reverted if modified in failing matcher" do
|
295
295
|
app do |r|
|
296
|
-
r.on lambda {
|
296
|
+
r.on lambda { @remaining_path = "/blah"; false } do
|
297
297
|
"Unreachable"
|
298
298
|
end
|
299
299
|
|
300
300
|
r.on do
|
301
|
-
r.
|
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
|
-
|
data/spec/plugin/assets_spec.rb
CHANGED
@@ -332,11 +332,11 @@ if run_tests
|
|
332
332
|
end
|
333
333
|
|
334
334
|
it '#assets should include attributes given' do
|
335
|
-
app.
|
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.
|
339
|
+
app.allocate.assets([:js, :head], 'a'=>'b"e').should == '<script type="text/javascript" a="b"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.
|
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.
|
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.
|
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.
|
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|
|
data/spec/plugin/halt_spec.rb
CHANGED
@@ -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
|