roda 2.21.0 → 2.22.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 +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
|