roda 2.21.0 → 2.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +8 -0
- data/README.rdoc +30 -24
- data/doc/release_notes/2.22.0.txt +41 -0
- data/lib/roda.rb +22 -3
- data/lib/roda/plugins/caching.rb +2 -2
- data/lib/roda/plugins/chunked.rb +1 -1
- data/lib/roda/plugins/class_level_routing.rb +1 -0
- data/lib/roda/plugins/mailer.rb +2 -2
- data/lib/roda/plugins/params_capturing.rb +9 -7
- data/lib/roda/plugins/pass.rb +1 -1
- data/lib/roda/plugins/symbol_matchers.rb +19 -11
- data/lib/roda/plugins/websockets.rb +1 -1
- data/lib/roda/version.rb +1 -1
- data/spec/composition_spec.rb +1 -1
- data/spec/matchers_spec.rb +108 -0
- data/spec/plugin/caching_spec.rb +2 -0
- data/spec/plugin/class_level_routing_spec.rb +11 -11
- data/spec/plugin/mailer_spec.rb +1 -1
- data/spec/plugin/optimized_string_matchers_spec.rb +14 -11
- data/spec/plugin/params_capturing_spec.rb +18 -7
- data/spec/plugin/pass_spec.rb +1 -1
- data/spec/plugin/sinatra_helpers_spec.rb +1 -1
- data/spec/plugin/symbol_matchers_spec.rb +53 -4
- data/spec/plugin/symbol_status_spec.rb +2 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6bf01ef06d46bc9ee153b2b737b44a19b6dc8e3f
|
4
|
+
data.tar.gz: 32979a928ec7cc15994b5c1a937b8601946f6451
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d730f1511a4c437b8f5bc8e3df705a2286703277ab22a985e9d331de934c8c30810f657fe06510b928f731086d824fdbd2b1996833751085fef898ecd540507d
|
7
|
+
data.tar.gz: 39d8d2b3c9b26f610d212b00ce087973030d17a1aadc0867baf0a613a1dfb9d1f54a8bc1792dad30802dcc305b1850cfab8a67052591c5699926389ab90ca591
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= 2.22.0 (2017-01-20)
|
2
|
+
|
3
|
+
* Add support for :verbatim_string_matcher option, for making all string matchers match verbatim (jeremyevans)
|
4
|
+
|
5
|
+
* Add support for :unsupported_matcher => :raise option, for raising on unsupported matcher values (jeremyevans)
|
6
|
+
|
7
|
+
* Add support for :unsupported_block_result => :raise option, for raising on unsupported route/match block return values (jeremyevans)
|
8
|
+
|
1
9
|
= 2.21.0 (2016-12-16)
|
2
10
|
|
3
11
|
* Add handle_stream_error method to streaming plugin, for handling errors when using stream(:loop=>true) (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -56,8 +56,6 @@ Here's a simple application, showing how the routing tree works:
|
|
56
56
|
require "roda"
|
57
57
|
|
58
58
|
class App < Roda
|
59
|
-
use Rack::Session::Cookie, :secret => ENV['SECRET']
|
60
|
-
|
61
59
|
route do |r|
|
62
60
|
# GET / request
|
63
61
|
r.root do
|
@@ -95,9 +93,6 @@ Here's a simple application, showing how the routing tree works:
|
|
95
93
|
|
96
94
|
Here's a breakdown of what is going on in the block above:
|
97
95
|
|
98
|
-
After requiring the library and subclassing Roda, the +use+ method is called.
|
99
|
-
This loads a Rack middleware into the current application.
|
100
|
-
|
101
96
|
The +route+ block is called whenever a new request comes in.
|
102
97
|
It is yielded an instance of a subclass of <tt>Rack::Request</tt>
|
103
98
|
with some additional methods for matching routes.
|
@@ -232,12 +227,12 @@ Here's an example showcasing how different matchers work:
|
|
232
227
|
end
|
233
228
|
|
234
229
|
# GET /post/2011/02/16/hello
|
235
|
-
r.get "post
|
230
|
+
r.get "post", :y, :m, :d, :slug do |y, m, d, slug|
|
236
231
|
"#{y}-#{m}-#{d} #{slug}" #=> "2011-02-16 hello"
|
237
232
|
end
|
238
233
|
|
239
234
|
# GET /username/foobar branch
|
240
|
-
r.on "username
|
235
|
+
r.on "username", :username, :method=>:get do |username|
|
241
236
|
user = User.find_by_username(username)
|
242
237
|
|
243
238
|
# GET /username/foobar/posts
|
@@ -290,9 +285,9 @@ If a string contains any slashes, it matches one additional segment for each sla
|
|
290
285
|
"foo/bar" # matches "/foo/bar"
|
291
286
|
"foo/bar" # does not match "/foo/bard"
|
292
287
|
|
293
|
-
|
294
|
-
the colon and remaining <tt>\\w</tt> characters match any
|
295
|
-
that contains at least one character:
|
288
|
+
For backwards compatibility, if a string contains a colon followed by any
|
289
|
+
<tt>\\w</tt> characters, the colon and remaining <tt>\\w</tt> characters match any
|
290
|
+
nonempty segment that contains at least one character:
|
296
291
|
|
297
292
|
"foo/:id" # matches "/foo/bar", "/foo/baz", etc.
|
298
293
|
"foo/:id" # does not match "/fo/bar"
|
@@ -302,22 +297,17 @@ You can use multiple colons in a string:
|
|
302
297
|
":x/:y" # matches "/foo/bar", "/bar/foo" etc.
|
303
298
|
":x/:y" # does not match "/foo", "/bar/"
|
304
299
|
|
305
|
-
|
306
|
-
|
307
|
-
"foo:x/bar:y" # matches "/food/bard", "/fool/bart", etc.
|
308
|
-
"foo:x/bar:y" # does not match "/foo/bart", "/fool/bar", etc.
|
309
|
-
|
310
|
-
If any colons are used, the block will yield one argument
|
311
|
-
for each segment matched containing the matched text, so:
|
312
|
-
|
313
|
-
"foo:x/:y" # matching "/fool/bar" yields "l", "bar"
|
300
|
+
Note that instead of using colons in strings, it is recommended to use separate
|
301
|
+
symbol arguments, as it is faster and simpler:
|
314
302
|
|
315
|
-
|
303
|
+
"foo", :id # instead of "foo/:id"
|
304
|
+
:x, :y # instead of ":x/:y"
|
316
305
|
|
317
|
-
|
306
|
+
It is possible in future versions of Roda, colons will not be treated specially
|
307
|
+
in strings, and will just match a literal colon character.
|
318
308
|
|
319
|
-
Note that other than colons
|
320
|
-
|
309
|
+
Note that other than colons followed by a <tt>\\w</tt> character, strings do no
|
310
|
+
handle regular expression syntax, they are matched verbatim:
|
321
311
|
|
322
312
|
"\\d+(/\\w+)?" # matches "/\d+(/\w+)?"
|
323
313
|
"\\d+(/\\w+)?" # does not match "/123/abc"
|
@@ -641,12 +631,28 @@ The plugins that ship with Roda freeze their settings and only allow modificatio
|
|
641
631
|
to their settings by reloading the plugin, and external plugins are encouraged
|
642
632
|
to follow this approach.
|
643
633
|
|
644
|
-
The following options are respected by multiple plugins:
|
634
|
+
The following options are respected by the default library or multiple plugins:
|
645
635
|
|
646
636
|
:add_script_name :: Prepend the SCRIPT_NAME for the request to paths. This is
|
647
637
|
useful if you mount the app as a path under another app.
|
648
638
|
:root :: Set the root path for the app. This defaults to the current working
|
649
639
|
directory of the process.
|
640
|
+
:unsupported_block_result :: If set to :raise, raises an error if a match or
|
641
|
+
route block returns an object that is not handled.
|
642
|
+
By default, String, nil, and false are handled,
|
643
|
+
and other types can be handled via plugins. Setting
|
644
|
+
this option can alert you to possible issues in your
|
645
|
+
application.
|
646
|
+
:unsupported_matcher :: If set to :raise, raises an error if a matcher is used that
|
647
|
+
is not handled. By default, String, Symbol, Regexp, Hash,
|
648
|
+
Array, Proc, true, false, and nil are handled. Setting
|
649
|
+
this option can alert you to possible issues in your
|
650
|
+
application.
|
651
|
+
:verbatim_string_matcher :: If set to true, makes all string matchers match
|
652
|
+
verbatim strings, disallowing the use of colons
|
653
|
+
for placeholders. In general, it is recommended
|
654
|
+
to use separate symbol matchers instead of
|
655
|
+
embedding placeholders in string matchers.
|
650
656
|
|
651
657
|
There may be other options supported by individual plugins, if so it will be
|
652
658
|
mentioned in the documentation for the plugin.
|
@@ -0,0 +1,41 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An :unsupported_block_result => :raise option is now supported
|
4
|
+
for Roda applications. This will raise a RodaError if an
|
5
|
+
unsupported value is returned from a match block or the
|
6
|
+
route block. This can make it easier to discover potential
|
7
|
+
problems in the routing tree. This option may become the
|
8
|
+
default behavior in Roda 3.
|
9
|
+
|
10
|
+
* An :unsupported_matcher => :raise option is now supported for
|
11
|
+
Roda applications. This will raise a RodaError if you use an
|
12
|
+
unsupported value as a matcher. This can make it easier to
|
13
|
+
discover potential problems in the routing tree. This option
|
14
|
+
may become the default behavior in Roda 3.
|
15
|
+
|
16
|
+
* A :verbatim_string_matcher option is now supported for Roda
|
17
|
+
applications. This will make all string matchers only match
|
18
|
+
the path verbatim, disallowing the use of a colon for
|
19
|
+
placeholders in the string. It's recommended that users
|
20
|
+
switch to using separate symbol arguments for placeholders.
|
21
|
+
|
22
|
+
If you enable this option, you need to change code such as:
|
23
|
+
|
24
|
+
r.is "foo/:bar" do |bar|
|
25
|
+
end
|
26
|
+
|
27
|
+
to:
|
28
|
+
|
29
|
+
r.is "foo", :bar do |bar|
|
30
|
+
end
|
31
|
+
|
32
|
+
If you are looking to convert an existing routing tree
|
33
|
+
from using placeholders in strings to separate symbol
|
34
|
+
arguments, you can scan your routing tree for potential
|
35
|
+
usage of placeholders in strings:
|
36
|
+
|
37
|
+
grep ' r\..*['\''"].*:.*['\''"]' app.rb
|
38
|
+
|
39
|
+
This option may become the default behavior in Roda 3, with
|
40
|
+
a plugin added to support the current default behavior of
|
41
|
+
allowing placeholders in string matchers.
|
data/lib/roda.rb
CHANGED
@@ -683,7 +683,7 @@ class Roda
|
|
683
683
|
# string so that regexp metacharacters are not matched, and recognizes
|
684
684
|
# colon tokens for placeholders.
|
685
685
|
def _match_string(str)
|
686
|
-
if str.index(COLON)
|
686
|
+
if str.index(COLON) && placeholder_string_matcher?
|
687
687
|
consume(self.class.cached_matcher(str){Regexp.escape(str).gsub(/:(\w+)/){|m| _match_symbol_regexp($1)}})
|
688
688
|
else
|
689
689
|
rp = @remaining_path
|
@@ -750,12 +750,19 @@ class Roda
|
|
750
750
|
throw :halt, response.finish
|
751
751
|
end
|
752
752
|
|
753
|
-
# The body to use for the response if the response does not
|
753
|
+
# The body to use for the response if the response does not already have
|
754
754
|
# a body. By default, a String is returned directly, and nil is
|
755
755
|
# returned otherwise.
|
756
756
|
def block_result_body(result)
|
757
|
-
|
757
|
+
case result
|
758
|
+
when String
|
758
759
|
result
|
760
|
+
when nil, false
|
761
|
+
# nothing
|
762
|
+
else
|
763
|
+
if roda_class.opts[:unsupported_block_result] == :raise
|
764
|
+
raise RodaError, "unsupported block result: #{result.inspect}"
|
765
|
+
end
|
759
766
|
end
|
760
767
|
end
|
761
768
|
|
@@ -830,7 +837,12 @@ class Roda
|
|
830
837
|
_match_array(matcher)
|
831
838
|
when Proc
|
832
839
|
matcher.call
|
840
|
+
when true, false, nil
|
841
|
+
matcher
|
833
842
|
else
|
843
|
+
if roda_class.opts[:unsupported_matcher] == :raise
|
844
|
+
raise RodaError, "unsupported matcher: #{matcher.inspect}"
|
845
|
+
end
|
834
846
|
matcher
|
835
847
|
end
|
836
848
|
end
|
@@ -849,6 +861,12 @@ class Roda
|
|
849
861
|
type.to_s.upcase == @env[REQUEST_METHOD]
|
850
862
|
end
|
851
863
|
end
|
864
|
+
|
865
|
+
# Whether string matchers are used verbatim, without handling
|
866
|
+
# placeholders via colons.
|
867
|
+
def placeholder_string_matcher?
|
868
|
+
!roda_class.opts[:verbatim_string_matcher]
|
869
|
+
end
|
852
870
|
end
|
853
871
|
|
854
872
|
# Class methods for RodaResponse
|
@@ -976,6 +994,7 @@ class Roda
|
|
976
994
|
def redirect(path, status = 302)
|
977
995
|
@headers[LOCATION] = path
|
978
996
|
@status = status
|
997
|
+
nil
|
979
998
|
end
|
980
999
|
|
981
1000
|
# Return the Roda class related to this response.
|
data/lib/roda/plugins/caching.rb
CHANGED
@@ -8,7 +8,7 @@ class Roda
|
|
8
8
|
# For proper caching, you should use either the +last_modified+ or
|
9
9
|
# +etag+ request methods.
|
10
10
|
#
|
11
|
-
# r.get 'albums
|
11
|
+
# r.get 'albums', :d do |album_id|
|
12
12
|
# @album = Album[album_id]
|
13
13
|
# r.last_modified @album.updated_at
|
14
14
|
# view('album')
|
@@ -16,7 +16,7 @@ class Roda
|
|
16
16
|
#
|
17
17
|
# # or
|
18
18
|
#
|
19
|
-
# r.get 'albums
|
19
|
+
# r.get 'albums', :d do |album_id|
|
20
20
|
# @album = Album[album_id]
|
21
21
|
# r.etag @album.sha1
|
22
22
|
# view('album')
|
data/lib/roda/plugins/chunked.rb
CHANGED
@@ -44,7 +44,7 @@ class Roda
|
|
44
44
|
# block will be delayed until rendering the content template. This is
|
45
45
|
# useful if you want to delay execution for all routes under a branch:
|
46
46
|
#
|
47
|
-
# r.on 'albums
|
47
|
+
# r.on 'albums', :d do |album_id|
|
48
48
|
# delay do
|
49
49
|
# @album = Album[album_id]
|
50
50
|
# end
|
data/lib/roda/plugins/mailer.rb
CHANGED
@@ -60,7 +60,7 @@ class Roda
|
|
60
60
|
# email bodies, you can use all of Roda's usual routing tree features
|
61
61
|
# to DRY up your code:
|
62
62
|
#
|
63
|
-
# r.on "albums
|
63
|
+
# r.on "albums", :d do |album_id|
|
64
64
|
# @album = Album[album_id.to_i]
|
65
65
|
# from 'from@example.com'
|
66
66
|
# to 'to@example.com'
|
@@ -92,7 +92,7 @@ class Roda
|
|
92
92
|
# If while preparing the email you figure out you don't want to send an
|
93
93
|
# email, call +no_mail!+:
|
94
94
|
#
|
95
|
-
# r.mail '
|
95
|
+
# r.mail 'welcome', :d do |id|
|
96
96
|
# no_mail! unless user = User[id]
|
97
97
|
# # ...
|
98
98
|
# end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
class Roda
|
5
5
|
module RodaPlugins
|
6
|
-
# The params_capturing plugin makes
|
6
|
+
# The params_capturing plugin makes symbol matchers
|
7
7
|
# update the request params with the value of the captured segments,
|
8
8
|
# using the matcher as the key:
|
9
9
|
#
|
@@ -11,7 +11,7 @@ class Roda
|
|
11
11
|
#
|
12
12
|
# route do |r|
|
13
13
|
# # GET /foo/123/abc/67
|
14
|
-
# r.on("foo
|
14
|
+
# r.on("foo", :bar, :baz, :quux) do
|
15
15
|
# r[:bar] #=> '123'
|
16
16
|
# r[:baz] #=> 'abc'
|
17
17
|
# r[:quux] #=> '67'
|
@@ -23,10 +23,9 @@ class Roda
|
|
23
23
|
# or strings.
|
24
24
|
#
|
25
25
|
# All matchers will update the request params by adding all
|
26
|
-
# captured segments to the +captures+ key
|
27
|
-
# symbol and string matchers:
|
26
|
+
# captured segments to the +captures+ key:
|
28
27
|
#
|
29
|
-
# r.on(:x, /(\d+)\/(\w+)/,
|
28
|
+
# r.on(:x, /(\d+)\/(\w+)/, :y) do
|
30
29
|
# r[:x] #=> nil
|
31
30
|
# r[:y] #=> nil
|
32
31
|
# r[:captures] #=> ["foo", "123", "abc", "67"]
|
@@ -53,6 +52,9 @@ class Roda
|
|
53
52
|
# strings and not symbols (<tt>r[]</tt> converts the argument
|
54
53
|
# to a string before looking it up in +r.params+).
|
55
54
|
#
|
55
|
+
# This plugin will also handle string matchers if placeholders in
|
56
|
+
# string matchers are supported.
|
57
|
+
#
|
56
58
|
# Also note that this plugin will not work correctly if you are using
|
57
59
|
# the symbol_matchers plugin with custom symbol matching and are using
|
58
60
|
# symbols that capture multiple values or no values.
|
@@ -73,7 +75,7 @@ class Roda
|
|
73
75
|
# Add the capture names from this string to list of param
|
74
76
|
# capture names if param capturing.
|
75
77
|
def _match_string(str)
|
76
|
-
if pc = @_params_captures
|
78
|
+
if (pc = @_params_captures) && placeholder_string_matcher?
|
77
79
|
pc.concat(str.scan(STRING_PARAM_CAPTURE_REGEXP))
|
78
80
|
end
|
79
81
|
super
|
@@ -86,7 +88,7 @@ class Roda
|
|
86
88
|
STRING_PARAM_CAPTURE_RANGE = 1..-1
|
87
89
|
|
88
90
|
def _match_string(str)
|
89
|
-
if pc = @_params_captures
|
91
|
+
if (pc = @_params_captures) && placeholder_string_matcher?
|
90
92
|
pc.concat(str.scan(/:\w+/).map{|s| s[STRING_PARAM_CAPTURE_RANGE]})
|
91
93
|
end
|
92
94
|
super
|
data/lib/roda/plugins/pass.rb
CHANGED
@@ -19,7 +19,13 @@ class Roda
|
|
19
19
|
# Then the route will only if the path is +/foobar123+, but not if it is
|
20
20
|
# +/foo+, +/FooBar123+, or +/foobar_123+.
|
21
21
|
#
|
22
|
-
#
|
22
|
+
# By default, this plugin sets up the following symbol matchers:
|
23
|
+
#
|
24
|
+
# :d :: <tt>/(\d+)/</tt>, a decimal segment
|
25
|
+
# :rest :: <tt>/(.*)/</tt>, all remaining characters, if any
|
26
|
+
# :w :: <tt>/(\w+)/</tt>, a alphanumeric segment
|
27
|
+
#
|
28
|
+
# If placeholder string matchers are supported, this feature also applies to
|
23
29
|
# embedded colons in strings, so the following:
|
24
30
|
#
|
25
31
|
# r.on "users/:username" do
|
@@ -29,17 +35,16 @@ class Roda
|
|
29
35
|
# Would match +/users/foobar123+, but not +/users/foo+, +/users/FooBar123+,
|
30
36
|
# or +/users/foobar_123+.
|
31
37
|
#
|
32
|
-
#
|
38
|
+
# If placeholder string matchers are supported, it also adds the following
|
39
|
+
# symbol matchers:
|
33
40
|
#
|
34
|
-
# :d :: <tt>/(\d+)/</tt>, a decimal segment
|
35
41
|
# :format :: <tt>/(?:\.(\w+))?/</tt>, an optional format/extension
|
36
42
|
# :opt :: <tt>/(?:\/([^\/]+))?</tt>, an optional segment
|
37
43
|
# :optd :: <tt>/(?:\/(\d+))?</tt>, an optional decimal segment
|
38
|
-
# :rest :: <tt>/(.*)/</tt>, all remaining characters, if any
|
39
|
-
# :w :: <tt>/(\w+)/</tt>, a alphanumeric segment
|
40
44
|
#
|
41
|
-
#
|
42
|
-
#
|
45
|
+
# These are only added when placeholder string matchers are supported,
|
46
|
+
# because they only make sense when used inside of a string, due to how
|
47
|
+
# segment matching works. Example:
|
43
48
|
#
|
44
49
|
# r.is "album:opt" do |id| end
|
45
50
|
# # matches /album (yielding nil) and /album/foo (yielding "foo")
|
@@ -50,11 +55,14 @@ class Roda
|
|
50
55
|
module SymbolMatchers
|
51
56
|
def self.configure(app)
|
52
57
|
app.symbol_matcher(:d, /(\d+)/)
|
53
|
-
app.symbol_matcher(:format, /(?:\.(\w+))?/)
|
54
|
-
app.symbol_matcher(:opt, /(?:\/([^\/]+))?/)
|
55
|
-
app.symbol_matcher(:optd, /(?:\/(\d+))?/)
|
56
|
-
app.symbol_matcher(:rest, /(.*)/)
|
57
58
|
app.symbol_matcher(:w, /(\w+)/)
|
59
|
+
app.symbol_matcher(:rest, /(.*)/)
|
60
|
+
|
61
|
+
if !app.opts[:verbatim_string_matcher]
|
62
|
+
app.symbol_matcher(:format, /(?:\.(\w+))?/)
|
63
|
+
app.symbol_matcher(:opt, /(?:\/([^\/]+))?/)
|
64
|
+
app.symbol_matcher(:optd, /(?:\/(\d+))?/)
|
65
|
+
end
|
58
66
|
end
|
59
67
|
|
60
68
|
module ClassMethods
|
data/lib/roda/version.rb
CHANGED
data/spec/composition_spec.rb
CHANGED
data/spec/matchers_spec.rb
CHANGED
@@ -136,18 +136,39 @@ describe "matchers" do
|
|
136
136
|
id
|
137
137
|
end
|
138
138
|
end
|
139
|
+
app.opts[:verbatim_string_matcher] = false
|
139
140
|
|
140
141
|
body('/posts/123').must_equal '123'
|
141
142
|
status('/post/123').must_equal 404
|
142
143
|
body('/responses-123').must_equal '123'
|
143
144
|
end
|
144
145
|
|
146
|
+
it "should not handle string with embedded param if :verbatim_string_matcher option is set" do
|
147
|
+
app do |r|
|
148
|
+
r.on "posts/:id" do
|
149
|
+
'1'
|
150
|
+
end
|
151
|
+
|
152
|
+
r.on "responses-:id" do
|
153
|
+
'2'
|
154
|
+
end
|
155
|
+
end
|
156
|
+
app.opts[:verbatim_string_matcher] = true
|
157
|
+
|
158
|
+
status('/post/123').must_equal 404
|
159
|
+
status('/posts/123').must_equal 404
|
160
|
+
body('/posts/:id').must_equal '1'
|
161
|
+
status('/responses-123').must_equal 404
|
162
|
+
body('/responses-:id').must_equal '2'
|
163
|
+
end
|
164
|
+
|
145
165
|
it "should handle multiple params in single string" do
|
146
166
|
app do |r|
|
147
167
|
r.on "u/:uid/posts/:id" do |uid, id|
|
148
168
|
uid + id
|
149
169
|
end
|
150
170
|
end
|
171
|
+
app.opts[:verbatim_string_matcher] = false
|
151
172
|
|
152
173
|
body("/u/jdoe/posts/123").must_equal 'jdoe123'
|
153
174
|
status("/u/jdoe/pots/123").must_equal 404
|
@@ -159,6 +180,7 @@ describe "matchers" do
|
|
159
180
|
uid + id
|
160
181
|
end
|
161
182
|
end
|
183
|
+
app.opts[:verbatim_string_matcher] = false
|
162
184
|
|
163
185
|
body("/u/jdoe/posts?/123").must_equal 'jdoe123'
|
164
186
|
status("/u/jdoe/post/123").must_equal 404
|
@@ -170,6 +192,7 @@ describe "matchers" do
|
|
170
192
|
uid + id
|
171
193
|
end
|
172
194
|
end
|
195
|
+
app.opts[:verbatim_string_matcher] = false
|
173
196
|
|
174
197
|
body("/u/:/jdoe/posts/:123").must_equal 'jdoe123'
|
175
198
|
status("/u/a/jdoe/post/b123").must_equal 404
|
@@ -286,6 +309,49 @@ describe "r.on" do
|
|
286
309
|
body.must_equal '+1'
|
287
310
|
end
|
288
311
|
|
312
|
+
it "does not execute on false" do
|
313
|
+
app do |r|
|
314
|
+
r.on false do
|
315
|
+
"+1"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
status.must_equal 404
|
320
|
+
end
|
321
|
+
|
322
|
+
it "does not execute on nil" do
|
323
|
+
app do |r|
|
324
|
+
r.on nil do
|
325
|
+
"+1"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
status.must_equal 404
|
330
|
+
end
|
331
|
+
|
332
|
+
it "executes on arbitrary object" do
|
333
|
+
app do |r|
|
334
|
+
r.on Object.new do
|
335
|
+
"+1"
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
body.must_equal '+1'
|
340
|
+
end
|
341
|
+
|
342
|
+
it "raises on arbitrary object if :unsupported_matcher => :raise" do
|
343
|
+
app(:bare) do
|
344
|
+
opts[:unsupported_matcher] = :raise
|
345
|
+
route do |r|
|
346
|
+
r.on Object.new do
|
347
|
+
"+1"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
proc{body}.must_raise Roda::RodaError
|
353
|
+
end
|
354
|
+
|
289
355
|
it "executes on non-false" do
|
290
356
|
app do |r|
|
291
357
|
r.on "123" do
|
@@ -700,3 +766,45 @@ describe "route block that returns string" do
|
|
700
766
|
body.must_equal '+1'
|
701
767
|
end
|
702
768
|
end
|
769
|
+
|
770
|
+
describe "app with :unsupported_block_result => :raise option" do
|
771
|
+
def app_value(v)
|
772
|
+
app(:bare) do
|
773
|
+
opts[:unsupported_block_result] = :raise
|
774
|
+
route do |r|
|
775
|
+
r.is 'a' do v end
|
776
|
+
v
|
777
|
+
end
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
it "should handle String as body" do
|
782
|
+
app_value '1'
|
783
|
+
status.must_equal 200
|
784
|
+
body.must_equal '1'
|
785
|
+
status('/a').must_equal 200
|
786
|
+
body('/a').must_equal '1'
|
787
|
+
end
|
788
|
+
|
789
|
+
it "should handle nil and false as not found" do
|
790
|
+
app_value nil
|
791
|
+
status.must_equal 404
|
792
|
+
body.must_equal ''
|
793
|
+
status('/a').must_equal 404
|
794
|
+
body('/a').must_equal ''
|
795
|
+
end
|
796
|
+
|
797
|
+
it "should handle false as not found" do
|
798
|
+
app_value false
|
799
|
+
status.must_equal 404
|
800
|
+
body.must_equal ''
|
801
|
+
status('/a').must_equal 404
|
802
|
+
body('/a').must_equal ''
|
803
|
+
end
|
804
|
+
|
805
|
+
it "should raise RodaError for other types" do
|
806
|
+
app_value Object.new
|
807
|
+
proc{body}.must_raise Roda::RodaError
|
808
|
+
proc{body('/a')}.must_raise Roda::RodaError
|
809
|
+
end
|
810
|
+
end
|
data/spec/plugin/caching_spec.rb
CHANGED
@@ -38,6 +38,7 @@ describe 'response.finish' do
|
|
38
38
|
it 'removes Content-Type and Content-Length for 304 responses' do
|
39
39
|
app(:caching) do |r|
|
40
40
|
response.status = 304
|
41
|
+
nil
|
41
42
|
end
|
42
43
|
header('Content-Type').must_be_nil
|
43
44
|
header('Content-Length').must_be_nil
|
@@ -46,6 +47,7 @@ describe 'response.finish' do
|
|
46
47
|
it 'does not change non-304 responses' do
|
47
48
|
app(:caching) do |r|
|
48
49
|
response.status = 200
|
50
|
+
nil
|
49
51
|
end
|
50
52
|
header('Content-Type').must_equal 'text/html'
|
51
53
|
header('Content-Length').must_equal '0'
|
@@ -18,7 +18,7 @@ describe "class_level_routing plugin" do
|
|
18
18
|
"foo"
|
19
19
|
end
|
20
20
|
|
21
|
-
is "d:d
|
21
|
+
is "d", :d do |x|
|
22
22
|
request.get do
|
23
23
|
"bazget#{x}"
|
24
24
|
end
|
@@ -42,8 +42,8 @@ describe "class_level_routing plugin" do
|
|
42
42
|
body.must_equal 'root'
|
43
43
|
body('/foo').must_equal 'foo'
|
44
44
|
body('/foo/bar').must_equal 'foobar'
|
45
|
-
body('/
|
46
|
-
body('/
|
45
|
+
body('/d/go').must_equal 'bazgetgo'
|
46
|
+
body('/d/go', 'REQUEST_METHOD'=>'POST').must_equal 'bazpostgo'
|
47
47
|
body('/bar').must_equal "x-get-bar"
|
48
48
|
body('/bar', 'REQUEST_METHOD'=>'POST').must_equal "x-post-bar"
|
49
49
|
body('/bar', 'REQUEST_METHOD'=>'DELETE').must_equal "x-delete-bar"
|
@@ -64,8 +64,8 @@ describe "class_level_routing plugin" do
|
|
64
64
|
body.must_equal 'root'
|
65
65
|
body('/foo').must_equal 'foo'
|
66
66
|
body('/foo/bar').must_equal 'foobar'
|
67
|
-
body('/
|
68
|
-
body('/
|
67
|
+
body('/d/go').must_equal 'bazgetgo'
|
68
|
+
body('/d/go', 'REQUEST_METHOD'=>'POST').must_equal 'bazpostgo'
|
69
69
|
body('/bar').must_equal "x-get-bar"
|
70
70
|
body('/bar', 'REQUEST_METHOD'=>'POST').must_equal "x-post-bar"
|
71
71
|
body('/bar', 'REQUEST_METHOD'=>'DELETE').must_equal "x-delete-bar"
|
@@ -100,8 +100,8 @@ describe "class_level_routing plugin" do
|
|
100
100
|
body.must_equal 'iroot'
|
101
101
|
body('/foo').must_equal 'ifoo'
|
102
102
|
body('/foo/bar').must_equal 'foobar'
|
103
|
-
body('/
|
104
|
-
body('/
|
103
|
+
body('/d/go').must_equal 'bazgetgo'
|
104
|
+
body('/d/go', 'REQUEST_METHOD'=>'POST').must_equal 'bazpostgo'
|
105
105
|
body('/bar').must_equal ""
|
106
106
|
body('/bar', 'REQUEST_METHOD'=>'POST').must_equal "ibar"
|
107
107
|
body('/bar', 'REQUEST_METHOD'=>'DELETE').must_equal "x-delete-bar"
|
@@ -120,8 +120,8 @@ describe "class_level_routing plugin" do
|
|
120
120
|
body.must_equal 'root'
|
121
121
|
body('/foo').must_equal 'foo'
|
122
122
|
body('/foo/bar').must_equal 'foobar'
|
123
|
-
body('/
|
124
|
-
body('/
|
123
|
+
body('/d/go').must_equal 'bazgetgo'
|
124
|
+
body('/d/go', 'REQUEST_METHOD'=>'POST').must_equal 'bazpostgo'
|
125
125
|
body('/bar').must_equal "x-get-bar"
|
126
126
|
body('/bar', 'REQUEST_METHOD'=>'POST').must_equal "x-post-bar"
|
127
127
|
body('/bar', 'REQUEST_METHOD'=>'DELETE').must_equal "x-delete-bar"
|
@@ -141,8 +141,8 @@ describe "class_level_routing plugin" do
|
|
141
141
|
body.must_equal 'root'
|
142
142
|
body('/foo').must_equal 'foo'
|
143
143
|
body('/foo/bar').must_equal 'foobar'
|
144
|
-
body('/
|
145
|
-
body('/
|
144
|
+
body('/d/go').must_equal 'bazgetgo'
|
145
|
+
body('/d/go', 'REQUEST_METHOD'=>'POST').must_equal 'bazpostgo'
|
146
146
|
body('/bar').must_equal "x-get-bar"
|
147
147
|
body('/bar', 'REQUEST_METHOD'=>'POST').must_equal "x-post-bar"
|
148
148
|
body('/bar', 'REQUEST_METHOD'=>'DELETE').must_equal "x-delete-bar"
|
data/spec/plugin/mailer_spec.rb
CHANGED
@@ -11,25 +11,28 @@ describe "optimized_string_matchers plugin" do
|
|
11
11
|
"ee"
|
12
12
|
end
|
13
13
|
|
14
|
-
r.on_branch "a
|
15
|
-
r.
|
16
|
-
"c
|
14
|
+
r.on_branch "a" do
|
15
|
+
r.on_branch "b" do
|
16
|
+
r.is_exactly "c" do
|
17
|
+
"c"
|
18
|
+
end
|
19
|
+
|
20
|
+
"b"
|
17
21
|
end
|
18
22
|
|
19
|
-
"a
|
23
|
+
"a"
|
20
24
|
end
|
21
25
|
|
22
26
|
"cc"
|
23
27
|
end
|
24
28
|
|
25
29
|
body.must_equal 'cc'
|
26
|
-
body('/a').must_equal '
|
27
|
-
body('/a/').must_equal '
|
28
|
-
body('/a/b/').must_equal '
|
29
|
-
body('/a/b/').must_equal '
|
30
|
-
body('/a/b/c').must_equal '
|
31
|
-
body('/a/b/c/').must_equal '
|
32
|
-
body('/a/b/c/d').must_equal 'a-0'
|
30
|
+
body('/a').must_equal 'a'
|
31
|
+
body('/a/').must_equal 'a'
|
32
|
+
body('/a/b/').must_equal 'b'
|
33
|
+
body('/a/b/c').must_equal 'c'
|
34
|
+
body('/a/b/c/').must_equal 'b'
|
35
|
+
body('/a/b/c/d').must_equal 'b'
|
33
36
|
body('/e').must_equal 'ee'
|
34
37
|
body('/eb').must_equal 'cc'
|
35
38
|
body('/e/').must_equal 'ee'
|
@@ -1,16 +1,12 @@
|
|
1
1
|
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
2
|
|
3
3
|
describe "params_capturing plugin" do
|
4
|
-
it "should add captures to r.params" do
|
4
|
+
it "should add captures to r.params for symbol matchers" do
|
5
5
|
app(:params_capturing) do |r|
|
6
|
-
r.on('foo
|
6
|
+
r.on('foo', :y, :z, :w) do |y, z, w|
|
7
7
|
(r.params.values_at('y', 'z', 'w') + [y, z, w, r[:captures].length]).join('-')
|
8
8
|
end
|
9
9
|
|
10
|
-
r.on("bar/:foo") do |foo|
|
11
|
-
"b-#{foo}-#{r[:foo]}-#{r[:captures].length}"
|
12
|
-
end
|
13
|
-
|
14
10
|
r.on(/(quux)/, /(foo)(bar)/) do |q, foo, bar|
|
15
11
|
"y-#{r[:captures].join}-#{q}-#{foo}-#{bar}"
|
16
12
|
end
|
@@ -30,9 +26,24 @@ describe "params_capturing plugin" do
|
|
30
26
|
|
31
27
|
body('/blarg', 'rack.input'=>StringIO.new).must_equal 'x-blarg-blarg-1'
|
32
28
|
body('/foo/1/2/3', 'rack.input'=>StringIO.new).must_equal '1-2-3-1-2-3-3'
|
33
|
-
body('/bar/banana', 'rack.input'=>StringIO.new).must_equal 'b-banana-banana-1'
|
34
29
|
body('/quux/foobar', 'rack.input'=>StringIO.new).must_equal 'y-quuxfoobar-quux-foo-bar'
|
35
30
|
body('/quux/asdf', 'rack.input'=>StringIO.new).must_equal 'y--quux-asdf-2'
|
36
31
|
body('/quux/asdf/890', 'rack.input'=>StringIO.new).must_equal 'y--890-quux-asdf-890-3'
|
37
32
|
end
|
33
|
+
|
34
|
+
it "should add captures to r.params for string matchers" do
|
35
|
+
app(:params_capturing) do |r|
|
36
|
+
r.on("bar/:foo") do |foo|
|
37
|
+
"b-#{foo}-#{r[:foo]}-#{r[:captures].length}"
|
38
|
+
end
|
39
|
+
|
40
|
+
r.on("baz/:bar", :foo) do |bar, foo|
|
41
|
+
"b-#{bar}-#{foo}-#{r[:bar]}-#{r[:foo]}-#{r[:captures].length}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
app.opts[:verbatim_string_matcher] = false
|
45
|
+
|
46
|
+
body('/bar/banana', 'rack.input'=>StringIO.new).must_equal 'b-banana-banana-1'
|
47
|
+
body('/baz/ban/ana', 'rack.input'=>StringIO.new).must_equal 'b-ban-ana-ban-ana-2'
|
48
|
+
end
|
38
49
|
end
|
data/spec/plugin/pass_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
2
|
|
3
3
|
describe "symbol_matchers plugin" do
|
4
|
-
it "allows symbol specific regexps" do
|
4
|
+
it "allows symbol specific regexps for symbol matchers" do
|
5
5
|
app(:bare) do
|
6
6
|
plugin :symbol_matchers
|
7
7
|
symbol_matcher(:f, /(f+)/)
|
@@ -11,6 +11,55 @@ describe "symbol_matchers plugin" do
|
|
11
11
|
"d#{d}"
|
12
12
|
end
|
13
13
|
|
14
|
+
r.is "thing2", :thing do |d|
|
15
|
+
"thing2#{d}"
|
16
|
+
end
|
17
|
+
|
18
|
+
r.is :f do |f|
|
19
|
+
"f#{f}"
|
20
|
+
end
|
21
|
+
|
22
|
+
r.is 'q', :rest do |rest|
|
23
|
+
"rest#{rest}"
|
24
|
+
end
|
25
|
+
|
26
|
+
r.is :w do |w|
|
27
|
+
"w#{w}"
|
28
|
+
end
|
29
|
+
|
30
|
+
r.is :d, :w, :f do |d, w, f|
|
31
|
+
"dwf#{d}#{w}#{f}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
status.must_equal 404
|
37
|
+
body("/1").must_equal 'd1'
|
38
|
+
body("/11232135").must_equal 'd11232135'
|
39
|
+
body("/a").must_equal 'wa'
|
40
|
+
body("/1az0").must_equal 'w1az0'
|
41
|
+
body("/f").must_equal 'ff'
|
42
|
+
body("/ffffffffffffffff").must_equal 'fffffffffffffffff'
|
43
|
+
status("/-").must_equal 404
|
44
|
+
body("/1/1a/f").must_equal 'dwf11af'
|
45
|
+
body("/12/1azy/fffff").must_equal 'dwf121azyfffff'
|
46
|
+
status("/1/f/a").must_equal 404
|
47
|
+
body("/q/a/b/c/d//f/g").must_equal 'resta/b/c/d//f/g'
|
48
|
+
body('/q/').must_equal 'rest'
|
49
|
+
body('/thing2/q').must_equal 'thing2q'
|
50
|
+
end
|
51
|
+
|
52
|
+
it "works with placeholder string matchers" do
|
53
|
+
app(:bare) do
|
54
|
+
opts[:verbatim_string_matcher] = false
|
55
|
+
plugin :symbol_matchers
|
56
|
+
symbol_matcher(:f, /(f+)/)
|
57
|
+
|
58
|
+
route do |r|
|
59
|
+
r.is ":d" do |d|
|
60
|
+
"d#{d}"
|
61
|
+
end
|
62
|
+
|
14
63
|
r.is "foo:optd" do |o|
|
15
64
|
"foo#{o.inspect}"
|
16
65
|
end
|
@@ -27,11 +76,11 @@ describe "symbol_matchers plugin" do
|
|
27
76
|
"thing#{d}"
|
28
77
|
end
|
29
78
|
|
30
|
-
r.is "thing2", :thing do |d|
|
79
|
+
r.is "thing2", ":thing" do |d|
|
31
80
|
"thing2#{d}"
|
32
81
|
end
|
33
82
|
|
34
|
-
r.is :f do |f|
|
83
|
+
r.is ":f" do |f|
|
35
84
|
"f#{f}"
|
36
85
|
end
|
37
86
|
|
@@ -39,7 +88,7 @@ describe "symbol_matchers plugin" do
|
|
39
88
|
"rest#{rest}"
|
40
89
|
end
|
41
90
|
|
42
|
-
r.is :w do |w|
|
91
|
+
r.is ":w" do |w|
|
43
92
|
"w#{w}"
|
44
93
|
end
|
45
94
|
|
@@ -5,6 +5,7 @@ describe "symbol_status plugin" do
|
|
5
5
|
app(:symbol_status) do |r|
|
6
6
|
r.on do
|
7
7
|
response.status = :unauthorized
|
8
|
+
nil
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
@@ -15,6 +16,7 @@ describe "symbol_status plugin" do
|
|
15
16
|
app(:symbol_status) do |r|
|
16
17
|
r.on do
|
17
18
|
response.status = 204
|
19
|
+
nil
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -201,6 +201,7 @@ extra_rdoc_files:
|
|
201
201
|
- doc/release_notes/2.19.0.txt
|
202
202
|
- doc/release_notes/2.20.0.txt
|
203
203
|
- doc/release_notes/2.21.0.txt
|
204
|
+
- doc/release_notes/2.22.0.txt
|
204
205
|
files:
|
205
206
|
- CHANGELOG
|
206
207
|
- MIT-LICENSE
|
@@ -226,6 +227,7 @@ files:
|
|
226
227
|
- doc/release_notes/2.2.0.txt
|
227
228
|
- doc/release_notes/2.20.0.txt
|
228
229
|
- doc/release_notes/2.21.0.txt
|
230
|
+
- doc/release_notes/2.22.0.txt
|
229
231
|
- doc/release_notes/2.3.0.txt
|
230
232
|
- doc/release_notes/2.4.0.txt
|
231
233
|
- doc/release_notes/2.5.0.txt
|
@@ -440,7 +442,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
440
442
|
version: '0'
|
441
443
|
requirements: []
|
442
444
|
rubyforge_project:
|
443
|
-
rubygems_version: 2.
|
445
|
+
rubygems_version: 2.6.8
|
444
446
|
signing_key:
|
445
447
|
specification_version: 4
|
446
448
|
summary: Routing tree web toolkit
|