roda 3.45.0 → 3.46.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
  SHA256:
3
- metadata.gz: 5dbdf55425a681a0cb32fa0b4a44cd50bf5be6f85c78c1ea1adbdd19e28c2d61
4
- data.tar.gz: e91ee695e5f8648f4269a2c229b794ef2ee921f4291102519d4ca2cadd9bf13a
3
+ metadata.gz: 5fd9ea4a9be8072ffc96e5bab91a845e99cc9a9d6641571167badd4146c93318
4
+ data.tar.gz: ed062be1d4b922ba39456f3868e599e3c6134fecfedaa3bfc780667bfec5c5f2
5
5
  SHA512:
6
- metadata.gz: aa46429e09091c3cf0112c251a9863845c69d4245674dcaddaec030244213d8b9ac1aae96cfa6093680c33f87c732ee4464e4cbe2572d192eb0a9f7e09acd43b
7
- data.tar.gz: 57cf84f7db6e6334918f0c01f7308c4c48db7e0ba9f2a14fe87420c3b2bd17471a229ec02a2ad9242ec6fd4aa67848f6dff04b319fea361a985dd264b52252ab
6
+ metadata.gz: 59e5677db771776107744f2438505b467363b410d6b2c8d19b1b11e802bfd15b22974997b20185e9ba3a1c9a7b8721bb5e3dbc1481a575f0812300c9c3241cac
7
+ data.tar.gz: 6233ec5251cfbeeaabdb44a7cba75d4831910215ae8239ec19873f58a62f14722bb6b57059f0814ae07e7ccc50977e3a2fbf74bb3b423a253b7dace5773c6887
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ = 3.46.0 (2021-07-12)
2
+
3
+ * Automatically optimize r.on/r.is/r.get/r.post methods with a single string, String, Integer, or regexp argument (jeremyevans)
4
+
1
5
  = 3.45.0 (2021-06-14)
2
6
 
3
7
  * Make typecast_params plugin check for null bytes in strings by default, with :allow_null_bytes option for previous behavior (jeremyevans)
data/README.rdoc CHANGED
@@ -13,7 +13,6 @@ Website :: http://roda.jeremyevans.net
13
13
  Source :: http://github.com/jeremyevans/roda
14
14
  Bugs :: http://github.com/jeremyevans/roda/issues
15
15
  Google Group :: http://groups.google.com/group/ruby-roda
16
- IRC :: irc://chat.freenode.net/#roda
17
16
 
18
17
  == Goals
19
18
 
@@ -0,0 +1,19 @@
1
+ = Improvements
2
+
3
+ * The r.on, r.is, r.get and r.post methods (and other verb methods
4
+ if using the all_verbs plugin) have now been optimized when using
5
+ a single string or regexp matcher, or the String or Integer class
6
+ matcher. Since those four matchers are the most common types of
7
+ matchers passed to the methods, this can significantly improve
8
+ routing performance (about 50% in the r10k benchmark).
9
+
10
+ This optimization is automatically applied when freezing
11
+ applications, if the related methods have not been modified by
12
+ plugins.
13
+
14
+ This optimization does come at the expense of a small decrease
15
+ in routing performance (3-4%) for unoptimized cases, but the
16
+ majority of applications will see a overall performance benefit
17
+ from this change.
18
+
19
+ * Other minor performance improvements have been made.
data/lib/roda.rb CHANGED
@@ -212,6 +212,10 @@ class Roda
212
212
  if @middleware.empty? && use_new_dispatch_api?
213
213
  plugin :direct_call
214
214
  end
215
+
216
+ if ([:on, :is, :_verb, :_match_class_String, :_match_class_Integer, :_match_string, :_match_regexp, :empty_path?]).all?{|m| self::RodaRequest.instance_method(m).owner == RequestMethods}
217
+ plugin :_optimized_matching
218
+ end
215
219
  end
216
220
 
217
221
  build_rack_app
@@ -0,0 +1,137 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The _optimized_matching plugin is automatically used internally to speed
7
+ # up matching when a single argument String instance, String class, Integer
8
+ # class, or Regexp matcher is passed to +r.on+, +r.is_+, or a verb method
9
+ # such as +r.get+ or +r.post+.
10
+ #
11
+ # The optimization works by avoiding the +if_match+ method if possible.
12
+ # Instead of clearing the captures array on every call, and having the
13
+ # matching append to the captures, it checks directly for the match,
14
+ # and on succesful match, it yields directly to the block without using
15
+ # the captures array.
16
+ module OptimizedMatching
17
+ TERM = Base::RequestMethods::TERM
18
+
19
+ module RequestMethods
20
+ # Optimize the r.is method handling of a single string, String, Integer,
21
+ # regexp, or true, argument.
22
+ def is(*args, &block)
23
+ case args.length
24
+ when 1
25
+ _is1(args, &block)
26
+ when 0
27
+ always(&block) if @remaining_path.empty?
28
+ else
29
+ if_match(args << TERM, &block)
30
+ end
31
+ end
32
+
33
+ # Optimize the r.on method handling of a single string, String, Integer,
34
+ # or regexp argument. Inline the related matching code to avoid the
35
+ # need to modify @captures.
36
+ def on(*args, &block)
37
+ case args.length
38
+ when 1
39
+ case matcher = args[0]
40
+ when String
41
+ always{yield} if _match_string(matcher)
42
+ when Class
43
+ if matcher == String
44
+ rp = @remaining_path
45
+ if rp.getbyte(0) == 47
46
+ if last = rp.index('/', 1)
47
+ @remaining_path = rp[last, rp.length]
48
+ always{yield rp[1, last-1]}
49
+ elsif (len = rp.length) > 1
50
+ @remaining_path = ""
51
+ always{yield rp[1, len]}
52
+ end
53
+ end
54
+ elsif matcher == Integer
55
+ if matchdata = @remaining_path.match(/\A\/(\d+)(?=\/|\z)/)
56
+ @remaining_path = matchdata.post_match
57
+ always{yield(matchdata[1].to_i)}
58
+ end
59
+ else
60
+ if_match(args, &block)
61
+ end
62
+ when Regexp
63
+ if matchdata = @remaining_path.match(self.class.cached_matcher(matcher){matcher})
64
+ @remaining_path = matchdata.post_match
65
+ always{yield(*matchdata.captures)}
66
+ end
67
+ else
68
+ if_match(args, &block)
69
+ end
70
+ when 0
71
+ always(&block)
72
+ else
73
+ if_match(args, &block)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ # Optimize the r.get/r.post method handling of a single string, String, Integer,
80
+ # regexp, or true, argument.
81
+ def _verb(args, &block)
82
+ case args.length
83
+ when 0
84
+ always(&block)
85
+ when 1
86
+ _is1(args, &block)
87
+ else
88
+ if_match(args << TERM, &block)
89
+ end
90
+ end
91
+
92
+ # Internals of r.is/r.get/r.post optimization. Inline the related matching
93
+ # code to avoid the need to modify @captures.
94
+ def _is1(args, &block)
95
+ case matcher = args[0]
96
+ when String
97
+ rp = @remaining_path
98
+ if _match_string(matcher)
99
+ if @remaining_path.empty?
100
+ always{yield}
101
+ else
102
+ @remaining_path = rp
103
+ nil
104
+ end
105
+ end
106
+ when Class
107
+ if matcher == String
108
+ rp = @remaining_path
109
+ if rp.getbyte(0) == 47 && !rp.index('/', 1) && (len = rp.length) > 1
110
+ @remaining_path = ''
111
+ always{yield rp[1, len]}
112
+ end
113
+ elsif matcher == Integer
114
+ if matchdata = @remaining_path.match(/\A\/(\d+)\z/)
115
+ @remaining_path = ''
116
+ always{yield(matchdata[1].to_i)}
117
+ end
118
+ else
119
+ if_match(args << TERM, &block)
120
+ end
121
+ when Regexp
122
+ if (matchdata = @remaining_path.match(self.class.cached_matcher(matcher){matcher})) && (post_match = matchdata.post_match).empty?
123
+ @remaining_path = ''
124
+ always{yield(*matchdata.captures)}
125
+ end
126
+ when true
127
+ always(&block) if @remaining_path.empty?
128
+ else
129
+ if_match(args << TERM, &block)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ register_plugin(:_optimized_matching, OptimizedMatching)
136
+ end
137
+ end
@@ -62,8 +62,7 @@ class Roda
62
62
  end
63
63
 
64
64
  # Configure whether to append or overwrite if content_for
65
- # is called multiple times to set data. Overwrite is default, use
66
- # the :append option to append.
65
+ # is called multiple times with the same key.
67
66
  def self.configure(app, opts = OPTS)
68
67
  app.opts[:append_content_for] = opts.fetch(:append, true)
69
68
  end
data/lib/roda/request.rb CHANGED
@@ -24,7 +24,7 @@ class Roda
24
24
 
25
25
  # Return the cached pattern for the given object. If the object is
26
26
  # not already cached, yield to get the basic pattern, and convert the
27
- # basic pattern to a pattern that does not partial segments.
27
+ # basic pattern to a pattern that does not match partial segments.
28
28
  def cached_matcher(obj)
29
29
  cache = @match_pattern_cache
30
30
 
@@ -234,9 +234,7 @@ class Roda
234
234
 
235
235
  # An alias of remaining_path. If a plugin changes remaining_path then
236
236
  # it should override this method to return the untouched original.
237
- def real_remaining_path
238
- remaining_path
239
- end
237
+ alias real_remaining_path remaining_path
240
238
 
241
239
  # Match POST requests. If no arguments are provided, matches all POST
242
240
  # requests, otherwise, matches only POST requests where the arguments
@@ -336,7 +334,7 @@ class Roda
336
334
  # Use <tt>r.get true</tt> to handle +GET+ requests where the current
337
335
  # path is empty.
338
336
  def root(&block)
339
- if remaining_path == "/" && is_get?
337
+ if @remaining_path == "/" && is_get?
340
338
  always(&block)
341
339
  end
342
340
  end
@@ -519,7 +517,7 @@ class Roda
519
517
  # SCRIPT_NAME to include the matched path, removes the matched
520
518
  # path from PATH_INFO, and updates captures with any regex captures.
521
519
  def consume(pattern)
522
- if matchdata = remaining_path.match(pattern)
520
+ if matchdata = @remaining_path.match(pattern)
523
521
  @remaining_path = matchdata.post_match
524
522
  captures = matchdata.captures
525
523
  captures = yield(*captures) if block_given?
@@ -548,7 +546,7 @@ class Roda
548
546
 
549
547
  # Whether the current path is considered empty.
550
548
  def empty_path?
551
- remaining_path.empty?
549
+ @remaining_path.empty?
552
550
  end
553
551
 
554
552
  # If all of the arguments match, yields to the match block and
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 45
7
+ RodaMinorVersion = 46
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
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: 3.45.0
4
+ version: 3.46.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: 2021-06-14 00:00:00.000000000 Z
11
+ date: 2021-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -216,6 +216,7 @@ extra_rdoc_files:
216
216
  - doc/release_notes/3.43.0.txt
217
217
  - doc/release_notes/3.44.0.txt
218
218
  - doc/release_notes/3.45.0.txt
219
+ - doc/release_notes/3.46.0.txt
219
220
  - doc/release_notes/3.5.0.txt
220
221
  - doc/release_notes/3.6.0.txt
221
222
  - doc/release_notes/3.7.0.txt
@@ -268,6 +269,7 @@ files:
268
269
  - doc/release_notes/3.43.0.txt
269
270
  - doc/release_notes/3.44.0.txt
270
271
  - doc/release_notes/3.45.0.txt
272
+ - doc/release_notes/3.46.0.txt
271
273
  - doc/release_notes/3.5.0.txt
272
274
  - doc/release_notes/3.6.0.txt
273
275
  - doc/release_notes/3.7.0.txt
@@ -278,6 +280,7 @@ files:
278
280
  - lib/roda/plugins.rb
279
281
  - lib/roda/plugins/_after_hook.rb
280
282
  - lib/roda/plugins/_before_hook.rb
283
+ - lib/roda/plugins/_optimized_matching.rb
281
284
  - lib/roda/plugins/_symbol_regexp_matchers.rb
282
285
  - lib/roda/plugins/all_verbs.rb
283
286
  - lib/roda/plugins/assets.rb
@@ -409,7 +412,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
409
412
  - !ruby/object:Gem::Version
410
413
  version: '0'
411
414
  requirements: []
412
- rubygems_version: 3.2.15
415
+ rubygems_version: 3.2.22
413
416
  signing_key:
414
417
  specification_version: 4
415
418
  summary: Routing tree web toolkit