roda 2.26.0 → 2.27.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 +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
|