roda 3.18.0 → 3.19.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f39ffb4b00598e7e113f06c2f6e43e968f96b2a447059de8b351c7f5cc35675a
4
- data.tar.gz: eacc16913dcfd72ad822fd8b6f25e132be80123c25a6a0c29869b39a13f04b50
3
+ metadata.gz: 930b16a28e9acd91a802b03248b36f940ee50f22b387e2e6839b34513b181c19
4
+ data.tar.gz: ff504ecc1d793e7af9180742204e219e26838f2187c0538241e2c240d57d4ad9
5
5
  SHA512:
6
- metadata.gz: 8aec053c2bda39f201cb03043cfe382d5982e5e16abca381ba76220603a25d680425a8aaf4b405f4134caef4336e3586e83d2ae0f43e8c7a40279650927237d3
7
- data.tar.gz: 104867afa6a526d13acd76eab634eaf9688f44131606e1c7c67f73f1464c662d16d76938217c0b878deb0d7e6217ae5586fc2764be13cdd64298b5256bd4b3b8
6
+ metadata.gz: 472f855ac1ea091a973d5396dd5950147cdee2f72fa4692233e0acca033ed989ad3d7c7aa338c053058b070efaa151de4b40ad475a9a14e70a32a21e81fe0f3c
7
+ data.tar.gz: bc8c7417ec1922531a2a535b57ca3528fe0de9731df0aaed520d9e3df22263fa96f23eadf2313284a8f03af3e77addafaf45c1fc0061e3795e01b3dd18024922
data/CHANGELOG CHANGED
@@ -1,3 +1,27 @@
1
+ = 3.19.0 (2019-04-12)
2
+
3
+ * Allow assets plugin :timestamp_paths option to be a string to specify a custom separator (jeremyevans)
4
+
5
+ * Fix handling for blocks with arity > 1 where expected arity is 1 (jeremyevans)
6
+
7
+ * Improve performance for handling blocks with arity 0 where expected arity is 1 by avoiding instance_exec (jeremyevans)
8
+
9
+ * Improve terminal maching by around 4x (jeremyevans)
10
+
11
+ * Improve symbol matching by 10-20% (jeremyevans)
12
+
13
+ * Improve string matching by 10-20% (jeremyevans)
14
+
15
+ * Automatically load the direct_call plugin when freezing if no middleware is used for better performance (jeremyevans)
16
+
17
+ * Delay building rack app until Roda.app is called (jeremyevans)
18
+
19
+ * Add hash_routes plugin for O(1) route dispatching at any level in the routing tree (jeremyevans)
20
+
21
+ * Add support for per-cookie cipher secrets in the sessions plugin, and enable them by default (jeremyevans)
22
+
23
+ * Add match_hook plugin for calling hooks when there is a successful match block (adam12) (#164)
24
+
1
25
  = 3.18.0 (2019-03-15)
2
26
 
3
27
  * Add direct_call plugin for making Roda.call skip middleware, allowing more optimization when dispatching routes (jeremyevans)
data/README.rdoc CHANGED
@@ -633,25 +633,23 @@ If you have a lot of rack applications that you want to dispatch to, and
633
633
  which one to dispatch to is based on the request path prefix, look into the
634
634
  +multi_run+ plugin.
635
635
 
636
- === multi_route plugin
636
+ === hash_routes plugin
637
637
 
638
638
  If you are just looking to split up the main route block up by branches,
639
- you should use the +multi_route+ plugin,
639
+ you should use the +hash_routes+ plugin,
640
640
  which keeps the current scope of the +route+ block:
641
641
 
642
642
  class App < Roda
643
- plugin :multi_route
643
+ plugin :hash_routes
644
644
 
645
- route "api" do |r|
645
+ hash_branch "api" do |r|
646
646
  r.is do
647
647
  # ...
648
648
  end
649
649
  end
650
650
 
651
651
  route do |r|
652
- r.on "api" do
653
- r.route "api"
654
- end
652
+ r.hash_routes
655
653
  end
656
654
  end
657
655
 
@@ -662,8 +660,8 @@ and still have access to them inside the +api+ +route+ block.
662
660
 
663
661
  == Testing
664
662
 
665
- It is very easy to test Roda with {Rack::Test}[https://github.com/brynary/rack-test]
666
- or {Capybara}[https://github.com/jnicklas/capybara].
663
+ It is very easy to test Roda with {Rack::Test}[https://github.com/rack-test/rack-test]
664
+ or {Capybara}[https://github.com/teamcapybara/capybara].
667
665
  Roda's own tests use {minitest/spec}[https://github.com/seattlerb/minitest].
668
666
  The default Rake task will run the specs for Roda.
669
667
 
data/doc/conventions.rdoc CHANGED
@@ -78,14 +78,14 @@ Large applications generally need more structure:
78
78
 
79
79
  For larger apps, the +Rakefile+, +assets/+, +migrate+, +models.rb+, +models/+, +public/+, remain the same.
80
80
 
81
- +app_name.rb+ should use the +multi_route+ and +view_options+ plugin, or the +multi_run+ plugin.
82
- The routes used by the +multi_route+ or +multi_run+ should be stored in routing files in the +routes/+
81
+ +app_name.rb+ should use the +hash_routes+ and +view_options+ plugin, or the +multi_run+ plugin.
82
+ The routes used by the +hash_routes+ or +multi_run+ should be stored in routing files in the +routes/+
83
83
  directory, with one file per prefix.
84
84
 
85
85
  For specs/tests, you should have +spec/models/+ and +spec/web/+, with one file per model in +spec/models/+
86
86
  and one file per prefix in +spec/web/+.
87
87
 
88
- You should have a separate view subdirectory per prefix. If you are using +multi_route+ and +view_options+ plugins,
88
+ You should have a separate view subdirectory per prefix. If you are using +hash_routes+ and +view_options+ plugins,
89
89
  use +set_view_subdir+ in your routing files to specify the subdirectory to use, so it doesn't need to be
90
90
  specified on every call to view.
91
91
 
@@ -95,7 +95,7 @@ and views. In a small application, these methods should just be specified in +a
95
95
  === Really Large Applications
96
96
 
97
97
  For very large applications, it's expected that there will be deviations from these conventions. However,
98
- it is recommended to use the +multi_route+ or +multi_run+ plugins to organize your application, and have
98
+ it is recommended to use the +hash_routes+ or +multi_run+ plugins to organize your application, and have
99
99
  subdirectories in the +routes/+ directory, and nested subdirectories in the +views/+ directory.
100
100
 
101
101
  == Roda Application File Layout
@@ -112,7 +112,7 @@ For a small application, the convention in Roda is to layout your Roda applicati
112
112
 
113
113
  use SomeMiddleware
114
114
 
115
- plugin :render
115
+ plugin :render, escape: true
116
116
  plugin :assets
117
117
 
118
118
  route do |r|
@@ -145,21 +145,21 @@ For larger applications, there are some slight changes to the Roda application f
145
145
 
146
146
  use SomeMiddleware
147
147
 
148
- plugin :render
148
+ plugin :render, escape: true
149
149
  plugin :assets
150
150
  plugin :view_options
151
- plugin :multi_route
151
+ plugin :hash_routes
152
152
  Dir['./routes/*.rb'].each{|f| require f}
153
153
 
154
154
  route do |r|
155
- r.multi_route
155
+ r.hash_routes
156
156
  end
157
157
 
158
158
  Dir['./helpers/*.rb'].each{|f| require f}
159
159
  end
160
160
 
161
- After loading the +view_options+ and +multi_route+ plugin, you require all of your
161
+ After loading the +view_options+ and +hash_routes+ plugin, you require all of your
162
162
  routing files. Inside your route block, instead of defining your routes, you just call
163
- +r.multi_route+, which will dispatch to all of your routing files. After your route
163
+ +r.hash_routes+, which will dispatch to all of your routing files. After your route
164
164
  block, you require all of your helper files containing the instance methods for your
165
165
  route block or views, instead of defining the methods directly.
@@ -0,0 +1,229 @@
1
+ = New Features
2
+
3
+ * A hash_routes plugin has been added for O(1) route dispatching at
4
+ any level of the routing tree. By default, Roda uses a linear
5
+ search of possible branches at each level of the routing tree,
6
+ which results in roughly O(log(n)) routing behavior in most
7
+ applications (where n is the total number of routes in the
8
+ application).
9
+
10
+ Assume you have the following routing tree:
11
+
12
+ route do |r|
13
+ r.on "a" do
14
+ # ...
15
+ end
16
+
17
+ r.on "b" do
18
+ # ...
19
+ end
20
+
21
+ r.is "c" do
22
+ # ...
23
+ end
24
+
25
+ # ...
26
+ end
27
+
28
+ With this routing tree, a request for /c will first check /a and
29
+ /b. This is not normally a performance issue, but if you have a
30
+ large number of routes at a particular level, it can be.
31
+
32
+ The hash_routes plugin allows you to convert this routing tree to:
33
+
34
+ plugin :hash_routes
35
+
36
+ hash_routes do
37
+ on "a" do |r|
38
+ # ...
39
+ end
40
+
41
+ on "b" do |r|
42
+ # ...
43
+ end
44
+
45
+ is "c" do |r|
46
+ # ...
47
+ end
48
+
49
+ # ...
50
+ end
51
+
52
+ route do |r|
53
+ r.hash_routes
54
+ end
55
+
56
+ This routing tree looks similar to Roda's standard routing tree, and
57
+ will have the same behavior as the previous example, but dispatching
58
+ to the routes inside the hash_routes block by the r.hash_routes
59
+ method will be an O(1) operation, instead of a linear search. This
60
+ can significantly improve performance in cases where you have a large
61
+ number of branches at any point in the routing tree.
62
+
63
+ In order to support O(1) route dispatching at any level of the
64
+ tree, the hash_routes plugin supports namespaces. You can use this
65
+ namespace support to keep the primary advantage of Roda when using
66
+ the hash_routes plugin, which is the ability to operate on a request
67
+ at any point during routing. Assume you have this routing tree:
68
+
69
+ hash_routes :root do
70
+ on "foo" do |r|
71
+ r.on Integer do |foo_id|
72
+ next unless @foo = Foo[foo_id]
73
+ r.hash_routes(:foo)
74
+ end
75
+ end
76
+
77
+ on "bar" do |r|
78
+ r.on Integer do |bar_id|
79
+ next unless @bar = Bar[bar_id]
80
+ r.hash_routes(:bar)
81
+ end
82
+ end
83
+
84
+ # ...
85
+ end
86
+
87
+ hash_routes :foo do
88
+ get "show" do
89
+ @page_title = @foo.name
90
+ view('foo/show')
91
+ end
92
+
93
+ # ...
94
+ end
95
+
96
+ hash_routes :bar do
97
+ post "edit" do
98
+ @bar.update(:name=>request.params['name'])
99
+ r.redirect "/"
100
+ end
101
+
102
+ # ...
103
+ end
104
+
105
+ route do |r|
106
+ r.hash_routes(:root)
107
+ end
108
+
109
+ With this routing tree, a GET /foo/123/show request will first get
110
+ dispatched to the on "foo" block in the :root namespace. That will
111
+ extract the 123 segment from the path, and use it to find the Foo
112
+ object with id 123 and set that to the instance variable @foo.
113
+ If there is no matching foo, the rest of the block will be skipped,
114
+ which will result in a 404 response. If there is a matching foo,
115
+ after setting the instance variable, it will dispatch to routes in
116
+ the :foo namespace, one of which is show, which will be able to use
117
+ the @foo variable, both in the route and in the view.
118
+
119
+ Similarly, a POST /bar/321/edit request would dispatch to the on
120
+ "bar" block in the :root namespace, will look up the matching bar,
121
+ then will dispatch to the edit route in the :bar namespace.
122
+
123
+ The hash_routes plugin can be used as a faster version of the
124
+ multi_route plugin's r.multi_route method. It can also be used as
125
+ a faster replacement for the multi_view plugin.
126
+
127
+ Please see the hash_routes plugin documentation for additional
128
+ methods and configuration styles supported by the plugin.
129
+
130
+ * A match_hook plugin has been added, which is called for each
131
+ successful match, before yielding to the match block. For example,
132
+ with the following routing tree:
133
+
134
+ plugin :match_hook
135
+
136
+ match_hook do
137
+ puts "#{r.matched_path}|#{r.remaining_path}"
138
+ end
139
+
140
+ route do |r|
141
+ r.on "a" do
142
+ r.is "b" do
143
+ r.get do
144
+ end
145
+
146
+ r.post do
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ A GET request for /a/b would call the match hook three times, and
153
+ output the following:
154
+
155
+ /a|/b # When the r.on block matches
156
+ /a/b| # When the r.is block matches
157
+ /a/b| # When the r.get block matches
158
+
159
+ A GET request for /a/c would call the match hook once, and output
160
+ the following:
161
+
162
+ /a|/b # When the r.on block matches
163
+
164
+ This plugin can be used to make debugging easier, as well as for
165
+ metrics.
166
+
167
+ = Other Improvements
168
+
169
+ * Per-cookie cipher secrets are now supported and used automatically
170
+ by default in the sessions plugin. This can prevent issues where
171
+ the cipher secret can be leaked if the random initialization vector
172
+ turns out not to be so random and ends up being reused. This
173
+ makes the session cookies slightly larger and about 10-20% slower.
174
+
175
+ Note that because of the way the sessions plugin is designed,
176
+ even if the cipher secret was leaked and you are not using
177
+ per-cookie cipher secrets, it would not allow an attacker to
178
+ forge a session, it would only allow them to read the contents of
179
+ an existing session.
180
+
181
+ If you are currently using the sessions plugin, and performing
182
+ rolling restarts, you should temporarily disable per-cookie session
183
+ secrets until all processes have been restarted and are able to
184
+ support per-cookie session secrets. You can do so by setting the
185
+ :per_cookie_cipher_secret sessions plugin option to false
186
+ temporarily until all processes have restarted and are running Roda
187
+ 3.19.0+.
188
+
189
+ * When passing route blocks to Roda that have 0 arity instead of the
190
+ expected arity of 1, emulate an arity of 1 using an approach that is
191
+ about 2.75-8x faster. This emulation is still about 20% slower than
192
+ using the expected arity.
193
+
194
+ * Fix emulation of route blocks that have >1 arity but where the
195
+ expected arity is 1. Such blocks were not handled correctly in
196
+ Roda 3.18.0.
197
+
198
+ * String matching performance has been improved by 10-20%.
199
+
200
+ * Symbol and String class matching performance has improved by 10-20%.
201
+
202
+ * Terminal matching performance has improved by about 4x.
203
+
204
+ * Roda will now automatically load the direct_call plugin when
205
+ freezing the application if there is no middleware used and the
206
+ application has not been subclassed, for improved performance.
207
+
208
+ * Roda no longer builds the rack application until the app class
209
+ method is called. This can fix O(n^2) issues when building
210
+ applications with a lot of middleware. One consequence of
211
+ this is that a Roda.route block is no longer required. If
212
+ Roda.route is not called, then the default routing tree will
213
+ return a 404 response for all requests.
214
+
215
+ The delay_build plugin used to support delaying building the rack
216
+ application until a build! method is called. Now that Roda delays
217
+ building the rack application until the app method is called, there
218
+ is no reason to use this plugin, and it is now a no-op.
219
+
220
+ * The assets plugin :timestamp_paths option now supports a string
221
+ value to use a custom separator. A slash separator is still used
222
+ by default.
223
+
224
+ = Backwards Compatibility
225
+
226
+ * The static_routing plugin internals have changed, as the
227
+ static_routing is now implemented via the hash_routes plugin. If
228
+ you were depending on the internals, you will need to update your
229
+ code.
data/lib/roda.rb CHANGED
@@ -118,7 +118,9 @@ class Roda
118
118
  # Class methods for the Roda class.
119
119
  module ClassMethods
120
120
  # The rack application that this class uses.
121
- attr_reader :app
121
+ def app
122
+ @app || build_rack_app
123
+ end
122
124
 
123
125
  # Whether middleware from the current class should be inherited by subclasses.
124
126
  # True by default, should be set to false when using a design where the parent
@@ -142,7 +144,7 @@ class Roda
142
144
  # Clear the middleware stack
143
145
  def clear_middleware!
144
146
  @middleware.clear
145
- build_rack_app
147
+ @app = nil
146
148
  end
147
149
 
148
150
  # Define an instance method using the block with the provided name and
@@ -172,6 +174,7 @@ class Roda
172
174
  if meth.is_a?(String)
173
175
  meth = roda_method_name(meth)
174
176
  end
177
+ call_meth = meth
175
178
 
176
179
  if (check_arity = opts.fetch(:check_arity, true)) && !block.lambda?
177
180
  required_args, optional_args, rest, keyword = _define_roda_method_arg_numbers(block)
@@ -194,8 +197,15 @@ class Roda
194
197
  if check_arity == :warn
195
198
  RodaPlugins.warn "Arity mismatch in block passed to define_roda_method. Expected Arity 1, but no arguments accepted for #{block.inspect}"
196
199
  end
200
+ temp_method = roda_method_name("temp")
201
+ class_eval("def #{temp_method}(_) #{meth =~ /\A\w+\z/ ? "#{meth}_arity" : "send(:\"#{meth}_arity\")"} end", __FILE__, __LINE__)
202
+ alias_method meth, temp_method
203
+ undef_method temp_method
204
+ private meth
205
+ meth = :"#{meth}_arity"
206
+ elsif required_args > 1
197
207
  b = block
198
- block = lambda{|_| instance_exec(&b)} # Fallback
208
+ block = lambda{|r| instance_exec(r, &b)} # Fallback
199
209
  end
200
210
  when :any
201
211
  if check_dynamic_arity = opts.fetch(:check_dynamic_arity, check_arity)
@@ -239,10 +249,9 @@ class Roda
239
249
  send(meth, *a)
240
250
  end
241
251
  private arity_meth
242
- arity_meth
243
- else
244
- meth
245
252
  end
253
+
254
+ call_meth
246
255
  end
247
256
 
248
257
  # Expand the given path, using the root argument as the base directory.
@@ -258,8 +267,7 @@ class Roda
258
267
  # Note that freezing the class prevents you from subclassing it, mostly because
259
268
  # it would cause some plugins to break.
260
269
  def freeze
261
- @opts.freeze
262
- @middleware.freeze
270
+ return self if frozen?
263
271
 
264
272
  unless opts[:subclassed]
265
273
  # If the _roda_run_main_route instance method has not been overridden,
@@ -276,8 +284,16 @@ class Roda
276
284
  end
277
285
  end
278
286
  end
287
+
288
+ if @middleware.empty? && use_new_dispatch_api?
289
+ plugin :direct_call
290
+ end
279
291
  end
280
292
 
293
+ build_rack_app
294
+ @opts.freeze
295
+ @middleware.freeze
296
+
281
297
  super
282
298
  end
283
299
 
@@ -340,7 +356,7 @@ class Roda
340
356
  self::RodaResponse.send(:include, plugin::ResponseMethods) if defined?(plugin::ResponseMethods)
341
357
  self::RodaResponse.extend(plugin::ResponseClassMethods) if defined?(plugin::ResponseClassMethods)
342
358
  plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
343
- nil
359
+ @app = nil
344
360
  end
345
361
 
346
362
  # Setup routing tree for the current Roda application, and build the
@@ -366,7 +382,7 @@ class Roda
366
382
  @route_block = block = convert_route_block(block)
367
383
  @rack_app_route_block = block = rack_app_route_block(block)
368
384
  public define_roda_method(:_roda_main_route, 1, &block)
369
- build_rack_app
385
+ @app = nil
370
386
  end
371
387
 
372
388
  # Add a middleware to use for the rack application. Must be
@@ -375,7 +391,7 @@ class Roda
375
391
  # Roda.use Rack::ShowExceptions
376
392
  def use(*args, &block)
377
393
  @middleware << [args, block].freeze
378
- build_rack_app
394
+ @app = nil
379
395
  end
380
396
 
381
397
  private
@@ -426,29 +442,15 @@ class Roda
426
442
 
427
443
  # Build the rack app to use
428
444
  def build_rack_app
429
- if @rack_app_route_block
430
- # RODA4: Assume optimize is true
431
- optimize = ancestors.each do |mod|
432
- break true if mod == InstanceMethods
433
- meths = mod.instance_methods(false)
434
- if meths.include?(:call) && !(meths.include?(:_roda_handle_main_route) || meths.include?(:_roda_run_main_route))
435
- RodaPlugins.warn <<WARNING
436
- Falling back to using #call for dispatching for #{self}, due to #call override in #{mod}.
437
- #{mod} should be fixed to adjust to Roda's new dispatch API, and override _roda_handle_main_route or _roda_run_main_route
438
- WARNING
439
- break false
440
- end
441
- end
442
-
443
- app = base_rack_app_callable(optimize)
445
+ app = base_rack_app_callable(use_new_dispatch_api?)
444
446
 
445
- @middleware.reverse_each do |args, bl|
446
- mid, *args = args
447
- app = mid.new(app, *args, &bl)
448
- app.freeze if opts[:freeze_middleware]
449
- end
450
- @app = app
447
+ @middleware.reverse_each do |args, bl|
448
+ mid, *args = args
449
+ app = mid.new(app, *args, &bl)
450
+ app.freeze if opts[:freeze_middleware]
451
451
  end
452
+
453
+ @app = app
452
454
  end
453
455
 
454
456
  # Modify the route block to use for any route block provided as input,
@@ -499,6 +501,24 @@ WARNING
499
501
  block
500
502
  end
501
503
 
504
+ # Whether the new dispatch API should be used.
505
+ def use_new_dispatch_api?
506
+ # RODA4: remove this method
507
+ ancestors.each do |mod|
508
+ break if mod == InstanceMethods
509
+ meths = mod.instance_methods(false)
510
+ if meths.include?(:call) && !(meths.include?(:_roda_handle_main_route) || meths.include?(:_roda_run_main_route))
511
+ RodaPlugins.warn <<WARNING
512
+ Falling back to using #call for dispatching for #{self}, due to #call override in #{mod}.
513
+ #{mod} should be fixed to adjust to Roda's new dispatch API, and override _roda_handle_main_route or _roda_run_main_route
514
+ WARNING
515
+ return false
516
+ end
517
+ end
518
+
519
+ true
520
+ end
521
+
502
522
  method_num = 0
503
523
  method_num_mutex = Mutex.new
504
524
  # Return a unique method name symbol for the given suffix.
@@ -1017,13 +1037,34 @@ WARNING
1017
1037
  # request path is a slash (indicating a new segment).
1018
1038
  def _match_string(str)
1019
1039
  rp = @remaining_path
1020
- if rp.start_with?("/#{str}")
1021
- last = str.length + 1
1022
- case rp[last]
1023
- when "/"
1024
- @remaining_path = rp[last, rp.length]
1040
+ length = str.length
1041
+
1042
+ match = case rp.rindex(str, length)
1043
+ when nil
1044
+ # segment does not match, most common case
1045
+ return
1046
+ when 1
1047
+ # segment matches, check first character is /
1048
+ rp.getbyte(0) == 47
1049
+ else # must be 0
1050
+ # segment matches at first character, only a match if
1051
+ # empty string given and first character is /
1052
+ length == 0 && rp.getbyte(0) == 47
1053
+ end
1054
+
1055
+ if match
1056
+ length += 1
1057
+ case rp.getbyte(length)
1058
+ when 47
1059
+ # next character is /, update remaining path to rest of string
1060
+ @remaining_path = rp[length, 100000000]
1025
1061
  when nil
1062
+ # end of string, so remaining path is empty
1026
1063
  @remaining_path = ""
1064
+ # else
1065
+ # Any other value means this was partial segment match,
1066
+ # so we return nil in that case without updating the
1067
+ # remaining_path. No need for explicit else clause.
1027
1068
  end
1028
1069
  end
1029
1070
  end
@@ -1031,7 +1072,7 @@ WARNING
1031
1072
  # Match the given symbol if any segment matches.
1032
1073
  def _match_symbol(sym=nil)
1033
1074
  rp = @remaining_path
1034
- if rp[0, 1] == "/"
1075
+ if rp.getbyte(0) == 47
1035
1076
  if last = rp.index('/', 1)
1036
1077
  if last > 1
1037
1078
  @captures << rp[1, last-1]
@@ -1117,7 +1158,7 @@ WARNING
1117
1158
 
1118
1159
  # Whether the current path is considered empty.
1119
1160
  def empty_path?
1120
- remaining_path == ""
1161
+ remaining_path.empty?
1121
1162
  end
1122
1163
 
1123
1164
  # If all of the arguments match, yields to the match block and
@@ -1148,18 +1189,20 @@ WARNING
1148
1189
  _match_class(matcher)
1149
1190
  when TERM
1150
1191
  empty_path?
1151
- when Symbol
1152
- _match_symbol(matcher)
1153
1192
  when Regexp
1154
1193
  _match_regexp(matcher)
1155
- when Hash
1156
- _match_hash(matcher)
1194
+ when true
1195
+ matcher
1157
1196
  when Array
1158
1197
  _match_array(matcher)
1198
+ when Hash
1199
+ _match_hash(matcher)
1200
+ when Symbol
1201
+ _match_symbol(matcher)
1202
+ when false, nil
1203
+ matcher
1159
1204
  when Proc
1160
1205
  matcher.call
1161
- when true, false, nil
1162
- matcher
1163
1206
  else
1164
1207
  unsupported_matcher(matcher)
1165
1208
  end