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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ac08181ea0c35fe056de0d73654c46acd01a541
4
- data.tar.gz: bafe66a0feb7e5b3e4d461f8eb05fd3762ccf730
3
+ metadata.gz: e2d73336ee24aee0fa3afe57096ef4d2f47c14f5
4
+ data.tar.gz: 9c0dbc60da0f5d73a24ad48a807f413c0f84e300
5
5
  SHA512:
6
- metadata.gz: '01974a864d4ba36f2173c6a166405f77359a8329e8ae8a7eba65ad1b28549377c52ca50fc4e61f61a828c2323fb5bb1d2dc80bcdb555449117c227144f8d3160'
7
- data.tar.gz: bfec42253904890e2ba6104f9d07b47eae8a3ade1cb45e43f36f5def8280219bce70baec7322545e4040195d5d946ec8038002e6e73edfe15e18c48151d261bb
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", :y, :m, :d, :slug do |y, m, d, slug|
231
- "#{y}-#{m}-#{d} #{slug}" #=> "2011-02-16 hello"
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", :username, :method=>:get do |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", :id # instead of "foo/:id"
304
- :x, :y # instead of ":x/:y"
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=>[:a, :b] do
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 :a, :b do
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', :id]}] do
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", :id do |item_id|
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 :opt_data do |optional_data|
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", :id do |item_id|
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", :id, [:opt_data, true] do |item_id, optional_data|
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
- @captures.concat(matchdata.captures)
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 Symbol
847
- _match_symbol(matcher)
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
- if roda_class.opts[:unsupported_matcher] == :raise
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
@@ -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', :d do |album_id|
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', :d do |album_id|
19
+ # r.get 'albums', Integer do |album_id|
20
20
  # @album = Album[album_id]
21
21
  # r.etag @album.sha1
22
22
  # view('album')
@@ -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', :d do |album_id|
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
@@ -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", :d do |album_id|
64
- # @album = Album[album_id.to_i]
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', :d do |id|
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/(\w+)/){|match| "/a/#{match[1].capitalize}"}
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/(\w+)/, :path_info => true){|match| "/a/#{match[1].capitalize}"}
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 :user_id do |user_id|
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 :user_id do |user_id|
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 :user_id do |user_id|
43
+ # r.on Integer do |user_id|
44
44
  # shared(:user => User[user_id]) do
45
45
  # r.run API
46
46
  # end
@@ -39,7 +39,7 @@ class Roda
39
39
  # r.on "users" do
40
40
  # set_view_subdir 'users'
41
41
  #
42
- # r.get :id do
42
+ # r.get Integer do |id|
43
43
  # append_view_subdir 'profile'
44
44
  # view 'index' # uses ./views/users/profile/index.erb
45
45
  # end
@@ -27,7 +27,7 @@ class Roda
27
27
  # end
28
28
  #
29
29
  # route do |r|
30
- # r.get "room", :d do |room_id|
30
+ # r.get "room", Integer do |room_id|
31
31
  # room = sync{ROOMS[room_id] ||= []}
32
32
  #
33
33
  # r.websocket do |ws|
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 2
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 26
7
+ RodaMinorVersion = 27
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -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 number" do
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 "regex captures in regex format" do
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
@@ -23,7 +23,7 @@ describe "error_mail plugin" do
23
23
  end
24
24
  end
25
25
 
26
- before do
26
+ after do
27
27
  Mail::TestMailer.deliveries.clear
28
28
  end
29
29
 
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.26.0
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-05-16 00:00:00.000000000 Z
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