roda 2.26.0 → 2.27.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 +39 -13
- data/doc/release_notes/2.27.0.txt +56 -0
- data/lib/roda.rb +41 -9
- data/lib/roda/plugins/caching.rb +2 -2
- data/lib/roda/plugins/chunked.rb +1 -1
- data/lib/roda/plugins/class_matchers.rb +51 -0
- data/lib/roda/plugins/mailer.rb +3 -3
- data/lib/roda/plugins/path_rewriter.rb +2 -2
- data/lib/roda/plugins/shared_vars.rb +3 -3
- data/lib/roda/plugins/view_options.rb +1 -1
- data/lib/roda/plugins/websockets.rb +1 -1
- data/lib/roda/version.rb +1 -1
- data/spec/matchers_spec.rb +38 -6
- data/spec/plugin/class_matchers_spec.rb +40 -0
- data/spec/plugin/error_mail_spec.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2d73336ee24aee0fa3afe57096ef4d2f47c14f5
|
4
|
+
data.tar.gz: 9c0dbc60da0f5d73a24ad48a807f413c0f84e300
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0e19332b1d9c17b18d4d9e3514a90389e65373508874b41521c3fb4fcfa4f0dc97da1fdfbb09273c9b84ac08addf4c286d85c4467538a1481e459a5307c6ae2
|
7
|
+
data.tar.gz: 4d7de5e3b2331e0e9a6c1983a2b79ba2a5f6d5e105b6d7fd8f5acf5d52128f34762cabf68627e5f2866415667fbfcd41b23f7fc315a0ea14ab52cb88b19217ba
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= 2.27.0 (2017-06-14)
|
2
|
+
|
3
|
+
* Add class_matchers plugin for matching other classes (in addition to String/Integer), with user specified regexps and type conversion (jeremyevans)
|
4
|
+
|
5
|
+
* Support String class matcher for non-empty segments, same behavior as symbol matchers but more intuitive and DRY (jeremyevans)
|
6
|
+
|
7
|
+
* Support Integer class matcher for \d+ segments, yielding matched values as integers (jeremyevans)
|
8
|
+
|
1
9
|
= 2.26.0 (2017-05-16)
|
2
10
|
|
3
11
|
* Support :skip_middleware option to csrf plugin to add only the methods and not add the middleware (luciusgone) (#118)
|
data/README.rdoc
CHANGED
@@ -227,12 +227,12 @@ Here's an example showcasing how different matchers work:
|
|
227
227
|
end
|
228
228
|
|
229
229
|
# GET /post/2011/02/16/hello
|
230
|
-
r.get "post",
|
231
|
-
"#{
|
230
|
+
r.get "post", Integer, Integer, Integer, String do |year, month, day, slug|
|
231
|
+
"#{year}-#{month}-#{day} #{slug}" #=> "2011-02-16 hello"
|
232
232
|
end
|
233
233
|
|
234
234
|
# GET /username/foobar branch
|
235
|
-
r.on "username",
|
235
|
+
r.on "username", String, :method=>:get do |username|
|
236
236
|
user = User.find_by_username(username)
|
237
237
|
|
238
238
|
# GET /username/foobar/posts
|
@@ -300,8 +300,8 @@ You can use multiple colons in a string:
|
|
300
300
|
Note that instead of using colons in strings, it is recommended to use separate
|
301
301
|
symbol arguments, as it is faster and simpler:
|
302
302
|
|
303
|
-
"foo",
|
304
|
-
|
303
|
+
"foo", String # instead of "foo/:id"
|
304
|
+
String, String # instead of ":x/:y"
|
305
305
|
|
306
306
|
It is possible in future versions of Roda, colons will not be treated specially
|
307
307
|
in strings, and will just match a literal colon character.
|
@@ -325,6 +325,25 @@ If any patterns are captured by the Regexp, they are yielded:
|
|
325
325
|
/foo\w+/ # matches "/foobar", yields nothing
|
326
326
|
/foo(\w+)/ # matches "/foobar", yields "bar"
|
327
327
|
|
328
|
+
=== Class
|
329
|
+
|
330
|
+
There are two classes that are supported as matchers, String
|
331
|
+
and Integer.
|
332
|
+
|
333
|
+
String :: matches any non-empty segment
|
334
|
+
Integer :: matches any segment of 0-9, returns matched values as integers
|
335
|
+
|
336
|
+
Using String and Integer is the recommended way to handle
|
337
|
+
arbitrary segments
|
338
|
+
|
339
|
+
String # matches "/foo", yields "foo"
|
340
|
+
String # matches "/1", yields "1"
|
341
|
+
String # does not match "/"
|
342
|
+
|
343
|
+
Integer # does not match "/foo"
|
344
|
+
Integer # matches "/1", yields 1
|
345
|
+
Integer # does not match "/"
|
346
|
+
|
328
347
|
=== Symbol
|
329
348
|
|
330
349
|
Symbols match any nonempty segment,
|
@@ -333,6 +352,11 @@ yielding the segment except for the preceding slash:
|
|
333
352
|
:id # matches "/foo" yields "foo"
|
334
353
|
:id # does not match "/"
|
335
354
|
|
355
|
+
Symbol matchers operate the same as the class String matcher,
|
356
|
+
and is the historical way to do arbitrary segment matching.
|
357
|
+
It is recommended to use the class String matcher in new code
|
358
|
+
as it is a bit more intuitive.
|
359
|
+
|
336
360
|
=== Proc
|
337
361
|
|
338
362
|
Procs match unless they return false or nil:
|
@@ -381,20 +405,20 @@ allows for easily defining your own:
|
|
381
405
|
|
382
406
|
The +:all+ matcher matches if all of the entries in the given array match, so
|
383
407
|
|
384
|
-
r.on :all=>[
|
408
|
+
r.on :all=>[String, String] do
|
385
409
|
# ...
|
386
410
|
end
|
387
411
|
|
388
412
|
is the same as:
|
389
413
|
|
390
|
-
r.on
|
414
|
+
r.on String, String do
|
391
415
|
# ...
|
392
416
|
end
|
393
417
|
|
394
418
|
The reason it also exists as a separate hash matcher
|
395
419
|
is so you can use it inside an array matcher, so:
|
396
420
|
|
397
|
-
r.on ['foo', {:all=>['foos',
|
421
|
+
r.on ['foo', {:all=>['foos', Integer]}] do
|
398
422
|
end
|
399
423
|
|
400
424
|
would match +/foo+ and +/foos/10+, but not +/foos+.
|
@@ -413,7 +437,9 @@ If +false+ or +nil+ is given directly as a matcher, it doesn't match anything.
|
|
413
437
|
|
414
438
|
=== Everything else
|
415
439
|
|
416
|
-
Everything else matches anything.
|
440
|
+
Everything else matches anything. Note that future versions of Roda will probably
|
441
|
+
raise exceptions for unsupported matchers, so it is not recommended to rely on this
|
442
|
+
behavior.
|
417
443
|
|
418
444
|
== Optional segments
|
419
445
|
|
@@ -424,11 +450,11 @@ the item's id, and 456 being some optional data.
|
|
424
450
|
The simplest way to handle this is by treating this as two separate routes with a
|
425
451
|
shared branch:
|
426
452
|
|
427
|
-
r.on "items",
|
453
|
+
r.on "items", String do |item_id|
|
428
454
|
# Shared code for branch here
|
429
455
|
|
430
456
|
# /items/123/456
|
431
|
-
r.is
|
457
|
+
r.is String do |optional_data|
|
432
458
|
end
|
433
459
|
|
434
460
|
# /items/123
|
@@ -440,7 +466,7 @@ This works well for many cases, but there are also cases where you really want t
|
|
440
466
|
treat it as one route with an optional segment. One simple way to do that is to
|
441
467
|
use a parameter instead of an optional segment (e.g. +/items/123?opt=456+).
|
442
468
|
|
443
|
-
r.is "items",
|
469
|
+
r.is "items", Integer do |item_id|
|
444
470
|
optional_data = r['opt']
|
445
471
|
end
|
446
472
|
|
@@ -448,7 +474,7 @@ However, if you really do want to use a optional segment, there are a couple dif
|
|
448
474
|
ways to use matchers to do so. One is using an array matcher where the last element
|
449
475
|
is true:
|
450
476
|
|
451
|
-
r.is "items",
|
477
|
+
r.is "items", Integer, [String, true] do |item_id, optional_data|
|
452
478
|
end
|
453
479
|
|
454
480
|
Note that this technically yields only one argument instead of two arguments if the
|
@@ -0,0 +1,56 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* String and Integer class matchers have been added. The
|
4
|
+
String class matches any non-empty segment and yields it as a
|
5
|
+
string. This is the same as the behavior of the symbol matchers,
|
6
|
+
but without the duplication. So instead of:
|
7
|
+
|
8
|
+
r.is "album", :album_name do |album_name|
|
9
|
+
end
|
10
|
+
|
11
|
+
you can now do:
|
12
|
+
|
13
|
+
r.is "album", String do |album_name|
|
14
|
+
end
|
15
|
+
|
16
|
+
This makes it a bit more intuitive that you want to match
|
17
|
+
any string, and avoids the redundancy between the symbol
|
18
|
+
name and block argument name.
|
19
|
+
|
20
|
+
The Integer class matches any integer segment (\d+) and yields it
|
21
|
+
as an integer:
|
22
|
+
|
23
|
+
r.is "album", Integer do |album_id|
|
24
|
+
# does not match "/albums/foo"
|
25
|
+
# matches "/albums/1", yielding 1 (not "1")
|
26
|
+
end
|
27
|
+
|
28
|
+
Previously, the :d matcher in the symbol_matchers plugin could
|
29
|
+
be used to only match integer segments, but it yielded results
|
30
|
+
as strings and not integers, so you still needed to convert the
|
31
|
+
type manually. Using Integer is a bit more intuitive than
|
32
|
+
using :d, and it handles the type conversion for you.
|
33
|
+
|
34
|
+
* A class_matchers plugin has been added for matching additional
|
35
|
+
classes, with user-specified regexps and type conversion. For
|
36
|
+
example, if you want to match YYYY-MM-DD segments and yield
|
37
|
+
them to the match blocks as ruby Date objects, you can do:
|
38
|
+
|
39
|
+
plugin :class_matchers
|
40
|
+
|
41
|
+
class_matcher(Date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d|
|
42
|
+
[Date.new(y.to_i, m.to_i, d.to_i)]
|
43
|
+
end
|
44
|
+
|
45
|
+
and then in your routing tree, you can do:
|
46
|
+
|
47
|
+
r.on "posts", Date do |date|
|
48
|
+
# does not match "/posts/foo" or "/posts/2017-01"
|
49
|
+
# matches "/posts/2017-01-13", yielding Date.new(2017, 1, 13)
|
50
|
+
end
|
51
|
+
|
52
|
+
= Backwards Compatibility
|
53
|
+
|
54
|
+
* If you were using the Integer and String classes as matchers
|
55
|
+
before and expected them to always match, you'll need to
|
56
|
+
change your code to use true instead.
|
data/lib/roda.rb
CHANGED
@@ -687,11 +687,31 @@ class Roda
|
|
687
687
|
end
|
688
688
|
end
|
689
689
|
|
690
|
+
# Match the given class. Currently, the following classes
|
691
|
+
# are supported by default:
|
692
|
+
# Integer :: Match an integer segment, yielding result to block as an integer
|
693
|
+
# String :: Match any non-empty segment, yielding result to block as a string
|
694
|
+
def _match_class(klass)
|
695
|
+
meth = :"_match_class_#{klass}"
|
696
|
+
if respond_to?(meth, true)
|
697
|
+
send(meth)
|
698
|
+
else
|
699
|
+
unsupported_matcher(klass)
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
690
703
|
# Match the given hash if all hash matchers match.
|
691
704
|
def _match_hash(hash)
|
692
705
|
hash.all?{|k,v| send("match_#{k}", v)}
|
693
706
|
end
|
694
707
|
|
708
|
+
# Match integer segment, and yield resulting value as an
|
709
|
+
# integer.
|
710
|
+
def _match_class_Integer
|
711
|
+
consume(/\A\/(\d+)(?=\/|\z)/){|i| [i.to_i]}
|
712
|
+
end
|
713
|
+
|
714
|
+
# Match only if all of the arguments in the given array match.
|
695
715
|
# Match the given regexp exactly if it matches a full segment.
|
696
716
|
def _match_regexp(re)
|
697
717
|
consume(self.class.cached_matcher(re){re})
|
@@ -725,7 +745,7 @@ class Roda
|
|
725
745
|
end
|
726
746
|
|
727
747
|
# Match the given symbol if any segment matches.
|
728
|
-
def _match_symbol(sym)
|
748
|
+
def _match_symbol(sym=nil)
|
729
749
|
rp = @remaining_path
|
730
750
|
if rp[0, 1] == SLASH
|
731
751
|
if last = rp.index('/', 1)
|
@@ -740,6 +760,9 @@ class Roda
|
|
740
760
|
end
|
741
761
|
end
|
742
762
|
|
763
|
+
# Match any nonempty segment. This should be called without an argument.
|
764
|
+
alias _match_class_String _match_symbol
|
765
|
+
|
743
766
|
# The regular expression to use for matching symbols. By default, any non-empty
|
744
767
|
# segment matches.
|
745
768
|
def _match_symbol_regexp(s)
|
@@ -791,7 +814,9 @@ class Roda
|
|
791
814
|
def consume(pattern)
|
792
815
|
if matchdata = remaining_path.match(pattern)
|
793
816
|
@remaining_path = matchdata.post_match
|
794
|
-
|
817
|
+
captures = matchdata.captures
|
818
|
+
captures = yield(*captures) if block_given?
|
819
|
+
@captures.concat(captures)
|
795
820
|
end
|
796
821
|
end
|
797
822
|
|
@@ -836,17 +861,19 @@ class Roda
|
|
836
861
|
false
|
837
862
|
end
|
838
863
|
end
|
839
|
-
|
864
|
+
|
840
865
|
# Attempt to match the argument to the given request, handling
|
841
866
|
# common ruby types.
|
842
867
|
def match(matcher)
|
843
868
|
case matcher
|
844
869
|
when String
|
845
870
|
_match_string(matcher)
|
846
|
-
when
|
847
|
-
|
871
|
+
when Class
|
872
|
+
_match_class(matcher)
|
848
873
|
when TERM
|
849
874
|
empty_path?
|
875
|
+
when Symbol
|
876
|
+
_match_symbol(matcher)
|
850
877
|
when Regexp
|
851
878
|
_match_regexp(matcher)
|
852
879
|
when Hash
|
@@ -858,10 +885,7 @@ class Roda
|
|
858
885
|
when true, false, nil
|
859
886
|
matcher
|
860
887
|
else
|
861
|
-
|
862
|
-
raise RodaError, "unsupported matcher: #{matcher.inspect}"
|
863
|
-
end
|
864
|
-
matcher
|
888
|
+
unsupported_matcher(matcher)
|
865
889
|
end
|
866
890
|
end
|
867
891
|
|
@@ -885,6 +909,14 @@ class Roda
|
|
885
909
|
def placeholder_string_matcher?
|
886
910
|
!roda_class.opts[:verbatim_string_matcher]
|
887
911
|
end
|
912
|
+
|
913
|
+
# Handle an unsupported matcher.
|
914
|
+
def unsupported_matcher(matcher)
|
915
|
+
if roda_class.opts[:unsupported_matcher] == :raise
|
916
|
+
raise RodaError, "unsupported matcher: #{matcher.inspect}"
|
917
|
+
end
|
918
|
+
matcher
|
919
|
+
end
|
888
920
|
end
|
889
921
|
|
890
922
|
# Class methods for RodaResponse
|
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', Integer 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', Integer 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', Integer do |album_id|
|
48
48
|
# delay do
|
49
49
|
# @album = Album[album_id]
|
50
50
|
# end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The class_matchers plugin allows you do define custom regexps and
|
7
|
+
# conversion procs to use for specific classes. For example, if you
|
8
|
+
# have multiple routes similar to:
|
9
|
+
#
|
10
|
+
# r.on /(\d\d\d\d)-(\d\d)-(\d\d)/ do |y, m, d|
|
11
|
+
# date = Date.new(y.to_i, m.to_i, d.to_i)
|
12
|
+
# # ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# You can register a Date class matcher for that regexp (note that
|
16
|
+
# the block must return an array):
|
17
|
+
#
|
18
|
+
# class_matcher(Date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d|
|
19
|
+
# [Date.new(y.to_i, m.to_i, d.to_i)]
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# And then use the Date class as a matcher, and it will yield a Date object:
|
23
|
+
#
|
24
|
+
# r.on Date do |date|
|
25
|
+
# # ...
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# This is useful to DRY up code if you are using the same type of pattern and
|
29
|
+
# type conversion in multiple places in your application.
|
30
|
+
#
|
31
|
+
# This plugin does not work with the params capturing plugin, as it does not
|
32
|
+
# offer the ability to associate block arguments with named keys.
|
33
|
+
module ClassMatchers
|
34
|
+
module ClassMethods
|
35
|
+
# Set the regexp to use for the given class. The block given will be
|
36
|
+
# called with all matched values from the regexp, and should return an
|
37
|
+
# array with the captures to yield to the match block.
|
38
|
+
def class_matcher(klass, re, &block)
|
39
|
+
meth = :"_match_class_#{klass}"
|
40
|
+
self::RodaRequest.class_eval do
|
41
|
+
consume_re = consume_pattern(re)
|
42
|
+
define_method(meth){consume(consume_re, &block)}
|
43
|
+
private meth
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
register_plugin(:class_matchers, ClassMatchers)
|
50
|
+
end
|
51
|
+
end
|
data/lib/roda/plugins/mailer.rb
CHANGED
@@ -60,8 +60,8 @@ 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",
|
64
|
-
# @album = Album[album_id
|
63
|
+
# r.on "albums", Integer do |album_id|
|
64
|
+
# @album = Album[album_id]
|
65
65
|
# from 'from@example.com'
|
66
66
|
# to 'to@example.com'
|
67
67
|
#
|
@@ -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 'welcome',
|
95
|
+
# r.mail 'welcome', Integer do |id|
|
96
96
|
# no_mail! unless user = User[id]
|
97
97
|
# # ...
|
98
98
|
# end
|
@@ -36,9 +36,9 @@ class Roda
|
|
36
36
|
# Patterns can be rewritten dynamically by providing a block accepting a MatchData
|
37
37
|
# object and evaluating to the replacement.
|
38
38
|
#
|
39
|
-
# rewrite_path(/\A\/a
|
39
|
+
# rewrite_path(/\A\/a\/(\w+)/){|match| "/a/#{match[1].capitalize}"}
|
40
40
|
# # PATH_INFO '/a/moo' => remaining_path '/a/Moo'
|
41
|
-
# rewrite_path(/\A\/a
|
41
|
+
# rewrite_path(/\A\/a\/(\w+)/, :path_info => true){|match| "/a/#{match[1].capitalize}"}
|
42
42
|
# # PATH_INFO '/a/moo' => PATH_INFO '/a/Moo'
|
43
43
|
#
|
44
44
|
# All path rewrites are applied in order, so if a path is rewritten by one rewrite,
|
@@ -18,7 +18,7 @@ class Roda
|
|
18
18
|
# plugin :shared_vars
|
19
19
|
#
|
20
20
|
# route do |r|
|
21
|
-
# r.on
|
21
|
+
# r.on Integer do |user_id|
|
22
22
|
# shared[:user] = User[user_id]
|
23
23
|
# r.run API
|
24
24
|
# end
|
@@ -29,7 +29,7 @@ class Roda
|
|
29
29
|
# vars with the content of the hash:
|
30
30
|
#
|
31
31
|
# route do |r|
|
32
|
-
# r.on
|
32
|
+
# r.on Integer do |user_id|
|
33
33
|
# shared(:user => User[user_id])
|
34
34
|
# r.run API
|
35
35
|
# end
|
@@ -40,7 +40,7 @@ class Roda
|
|
40
40
|
# previous shared variables afterward:
|
41
41
|
#
|
42
42
|
# route do |r|
|
43
|
-
# r.on
|
43
|
+
# r.on Integer do |user_id|
|
44
44
|
# shared(:user => User[user_id]) do
|
45
45
|
# r.run API
|
46
46
|
# end
|
data/lib/roda/version.rb
CHANGED
data/spec/matchers_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
2
|
|
3
3
|
describe "capturing" do
|
4
|
-
it "doesn't yield the verb" do
|
4
|
+
it "doesn't yield the verb for verb matcher" do
|
5
5
|
app do |r|
|
6
6
|
r.get do |*args|
|
7
7
|
args.size.to_s
|
@@ -11,7 +11,7 @@ describe "capturing" do
|
|
11
11
|
body.must_equal '0'
|
12
12
|
end
|
13
13
|
|
14
|
-
it "doesn't yield the path" do
|
14
|
+
it "doesn't yield the path for string matcher" do
|
15
15
|
app do |r|
|
16
16
|
r.get "home" do |*args|
|
17
17
|
args.size.to_s
|
@@ -21,7 +21,7 @@ describe "capturing" do
|
|
21
21
|
body('/home').must_equal '0'
|
22
22
|
end
|
23
23
|
|
24
|
-
it "yields the segment" do
|
24
|
+
it "yields the segment for symbol matcher" do
|
25
25
|
app do |r|
|
26
26
|
r.get "user", :id do |id|
|
27
27
|
id
|
@@ -31,7 +31,7 @@ describe "capturing" do
|
|
31
31
|
body("/user/johndoe").must_equal 'johndoe'
|
32
32
|
end
|
33
33
|
|
34
|
-
it "yields a
|
34
|
+
it "yields an integer segment as a string when using symbol matcher" do
|
35
35
|
app do |r|
|
36
36
|
r.get "user", :id do |id|
|
37
37
|
id
|
@@ -41,7 +41,7 @@ describe "capturing" do
|
|
41
41
|
body("/user/101").must_equal '101'
|
42
42
|
end
|
43
43
|
|
44
|
-
it "yields a segment per nested block" do
|
44
|
+
it "yields a segment per nested block for symbol matcher" do
|
45
45
|
app do |r|
|
46
46
|
r.on :one do |one|
|
47
47
|
r.on :two do |two|
|
@@ -55,7 +55,17 @@ describe "capturing" do
|
|
55
55
|
body("/one/two/three").must_equal "onetwothree"
|
56
56
|
end
|
57
57
|
|
58
|
-
it "
|
58
|
+
it "yields a segment per argument for symbol matcher" do
|
59
|
+
app do |r|
|
60
|
+
r.on :one, :two, :three do |one, two, three|
|
61
|
+
one + two + three
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
body("/one/two/three").must_equal "onetwothree"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "yields regex captures as separate arguments" do
|
59
69
|
app do |r|
|
60
70
|
r.get %r{posts/(\d+)-(.*)} do |id, slug|
|
61
71
|
id + slug
|
@@ -64,6 +74,28 @@ describe "capturing" do
|
|
64
74
|
|
65
75
|
body("/posts/123-postal-service").must_equal "123postal-service"
|
66
76
|
end
|
77
|
+
|
78
|
+
it "yields an integer segment as an integer when using Integer matcher " do
|
79
|
+
app do |r|
|
80
|
+
r.get "user", Integer do |id|
|
81
|
+
"#{id}-#{id.is_a?(Integer)}"
|
82
|
+
end
|
83
|
+
"b"
|
84
|
+
end
|
85
|
+
|
86
|
+
body("/user/101").must_equal '101-true'
|
87
|
+
body("/user/a").must_equal 'b'
|
88
|
+
end
|
89
|
+
|
90
|
+
it "yields the segment for symbol matcher" do
|
91
|
+
app do |r|
|
92
|
+
r.get "user", String do |id|
|
93
|
+
id
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
body("/user/johndoe").must_equal 'johndoe'
|
98
|
+
end
|
67
99
|
end
|
68
100
|
|
69
101
|
describe "r.is" do
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
describe "class_matchers plugin" do
|
5
|
+
it "allows class specific regexps with type conversion for class matchers" do
|
6
|
+
app(:bare) do
|
7
|
+
plugin :class_matchers
|
8
|
+
class_matcher(Date, /(\d\d\d\d)-(\d\d)-(\d\d)/){|y,m,d| [Date.new(y.to_i, m.to_i, d.to_i)]}
|
9
|
+
class_matcher(Array, /(\w+)\/(\w+)/){|a, b| [[a, 1], [b, 2]]}
|
10
|
+
class_matcher(Hash, /(\d+)\/(\d+)/){|a, b| [{a.to_i=>b.to_i}]}
|
11
|
+
|
12
|
+
route do |r|
|
13
|
+
r.on Array do |(a,b), (c,d)|
|
14
|
+
r.get Date do |date|
|
15
|
+
[date.year, date.month, date.day, a, b, c, d].join('-')
|
16
|
+
end
|
17
|
+
r.get Hash do |h|
|
18
|
+
[h.inspect, a, b, c, d].join('-')
|
19
|
+
end
|
20
|
+
r.get Array do |(a1,b1), (c1,d1)|
|
21
|
+
[a1, b1, c1, d1, a, b, c, d].join('-')
|
22
|
+
end
|
23
|
+
r.is do
|
24
|
+
[a, b, c, d].join('-')
|
25
|
+
end
|
26
|
+
"array"
|
27
|
+
end
|
28
|
+
""
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
body("/c").must_equal ''
|
33
|
+
body("/c/d").must_equal 'c-1-d-2'
|
34
|
+
body("/c/d/e").must_equal 'array'
|
35
|
+
body("/c/d/2009-10-a").must_equal 'array'
|
36
|
+
body("/c/d/2009-10-01").must_equal '2009-10-1-c-1-d-2'
|
37
|
+
body("/c/d/1/2").must_equal '{1=>2}-c-1-d-2'
|
38
|
+
body("/c/d/e/f").must_equal 'e-1-f-2-c-1-d-2'
|
39
|
+
end
|
40
|
+
end
|
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.27.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: 2017-
|
11
|
+
date: 2017-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -206,6 +206,7 @@ extra_rdoc_files:
|
|
206
206
|
- doc/release_notes/2.24.0.txt
|
207
207
|
- doc/release_notes/2.25.0.txt
|
208
208
|
- doc/release_notes/2.26.0.txt
|
209
|
+
- doc/release_notes/2.27.0.txt
|
209
210
|
files:
|
210
211
|
- CHANGELOG
|
211
212
|
- MIT-LICENSE
|
@@ -236,6 +237,7 @@ files:
|
|
236
237
|
- doc/release_notes/2.24.0.txt
|
237
238
|
- doc/release_notes/2.25.0.txt
|
238
239
|
- doc/release_notes/2.26.0.txt
|
240
|
+
- doc/release_notes/2.27.0.txt
|
239
241
|
- doc/release_notes/2.3.0.txt
|
240
242
|
- doc/release_notes/2.4.0.txt
|
241
243
|
- doc/release_notes/2.5.0.txt
|
@@ -253,6 +255,7 @@ files:
|
|
253
255
|
- lib/roda/plugins/caching.rb
|
254
256
|
- lib/roda/plugins/chunked.rb
|
255
257
|
- lib/roda/plugins/class_level_routing.rb
|
258
|
+
- lib/roda/plugins/class_matchers.rb
|
256
259
|
- lib/roda/plugins/content_for.rb
|
257
260
|
- lib/roda/plugins/cookies.rb
|
258
261
|
- lib/roda/plugins/csrf.rb
|
@@ -344,6 +347,7 @@ files:
|
|
344
347
|
- spec/plugin/caching_spec.rb
|
345
348
|
- spec/plugin/chunked_spec.rb
|
346
349
|
- spec/plugin/class_level_routing_spec.rb
|
350
|
+
- spec/plugin/class_matchers_spec.rb
|
347
351
|
- spec/plugin/content_for_spec.rb
|
348
352
|
- spec/plugin/cookies_spec.rb
|
349
353
|
- spec/plugin/csrf_spec.rb
|