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.
- 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
|