roda 3.61.0 → 3.62.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: 337e7cc074ffe0c300a4dbf9a14f72552caa5b9446fdf0ca04b9f22d9117c9c7
4
- data.tar.gz: 53a8b4ec124df8ef0efdc7efcc7bec3e0e7ea7d13fa60fb5d6adf06f50ed081e
3
+ metadata.gz: 912f354ffcb4440bf7955d4db8546f9eccbee3f3a3f91b77604a91228a2433c4
4
+ data.tar.gz: 8df8254014b7e8e9b6db623fa2b8269ac5485b5087ee53978080a81ba344a60c
5
5
  SHA512:
6
- metadata.gz: 78671d90145cf431a7deff333e6b75ad681252cae159b5ff09dfaba70d4b3fc17ec1f7362c52454c3f8e94e2343bd4b4d419a5c23738a6b525e9fdd79dc8846c
7
- data.tar.gz: 55b5bc0be878d58d66e0834110b47313f522e1f109b95c23348b2cb09430b01383595197db8f8db6587f10d3d9e4a7a353a2d70fb1ed4a099573309a5aabcae6
6
+ metadata.gz: bb43bcbad6e5421935d62a11abc3da27c40559ef1f55b9791d71ab22bd8cc4a73933f85bb68d77feb26efd6bf84f840bd5a202c80938ede330ca67cf9b51ca8b
7
+ data.tar.gz: 6f3234b37c506e3037864e7a7b79280f5b190758e8dada8f213b141cd1e4826a03a98017ddccb806082fa551580af9683088c94080157811867c0a3a1ec92b04
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ = 3.62.0 (2022-11-14)
2
+
3
+ * Add typecast_params_sized_integers plugin for converting parameters to sized integers (jeremyevans)
4
+
5
+ * Add Integer_matcher_max plugin for setting maximum integer value matched by the Integer matcher (jeremyevans)
6
+
7
+ * Allow class matchers in the class_matchers plugin to skip matching based on regexp match values (jeremyevans)
8
+
9
+ * Fix RodaRequest#matched_path when using unescape_path plugin (jeremyevans) (#286)
10
+
1
11
  = 3.61.0 (2022-10-12)
2
12
 
3
13
  * Make Integer matcher limit integer segments to 100 characters by default (jeremyevans)
@@ -0,0 +1,41 @@
1
+ = New Features
2
+
3
+ * An Integer_matcher_max plugin has been added for setting the
4
+ maximum value matched by the Integer matcher (the minimum is
5
+ always 0, since the Integer matcher does not match negative
6
+ integers). The default maximum value when using the plugin
7
+ is 2**63-1, the maximum value for a signed 64-bit integer.
8
+ You can specify a different maximum value by passing an argument
9
+ when loading the plugin.
10
+
11
+ * A typecast_params_sized_integers plugin has been added for
12
+ converting parameters to integers only if the integer is within a
13
+ specific size. By default, the plugin supports 8-bit, 16-bit,
14
+ 32-bit, and 64-bit signed and unsigned integer types, with the
15
+ following typecast_params methods added by the plugin:
16
+
17
+ * int8, uint8, pos_int8, pos_uint8, Integer8, Integeru8
18
+ * int16, uint16, pos_int16, pos_uint16, Integer16, Integeru16
19
+ * int32, uint32, pos_int32, pos_uint32, Integer32, Integeru32
20
+ * int64, uint64, pos_int64, pos_uint64, Integer64, Integeru64
21
+
22
+ You can override what sizes are added by default by using the
23
+ :sizes option. You can also specify a :default_size option,
24
+ in which case the default int, pos_int, and Integer conversions
25
+ will use the given size. So if you want to change the default
26
+ typecast_params integer conversion behavior to only support
27
+ integer values that can fit in 64-bit signed integers, you can
28
+ use:
29
+
30
+ plugin :typecast_params_sized_integers, sizes: [64],
31
+ default_size: 64
32
+
33
+ = Other Improvements
34
+
35
+ * The block passed to the class_matcher method in the class_matchers
36
+ plugin can now return nil/false to signal that it should not match.
37
+ This is useful when the regexp argument provided matches segments
38
+ not valid for the class.
39
+
40
+ * RodaRequest#matched_path now works correctly when using the
41
+ unescape_path plugin.
@@ -0,0 +1,54 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The Integer_matcher_max plugin sets the maximum integer value
7
+ # value that the Integer class matcher will match by default.
8
+ # By default, loading this plugin sets the maximum value to
9
+ # 2**63-1, the largest signed 64-bit integer value:
10
+ #
11
+ # plugin :Integer_matcher_max
12
+ # route do |r|
13
+ # r.is Integer do
14
+ # # Matches /9223372036854775807
15
+ # # Does not match /9223372036854775808
16
+ # end
17
+ # end
18
+ #
19
+ # To specify a different maximum value, you can pass a different
20
+ # maximum value when loading the plugin:
21
+ #
22
+ # plugin :Integer_matcher_max, 2**64-1
23
+ module IntegerMatcherMax
24
+ def self.configure(app, max=nil)
25
+ if max
26
+ app::RodaRequest.class_eval do
27
+ define_method(:_match_class_max_Integer){max}
28
+ alias_method :_match_class_max_Integer, :_match_class_max_Integer
29
+ private :_match_class_max_Integer
30
+ end
31
+ end
32
+ end
33
+
34
+ module RequestMethods
35
+ private
36
+
37
+ # Do not have the Integer matcher max when over the maximum
38
+ # configured Integer value.
39
+ def _match_class_convert_Integer(value)
40
+ value = super
41
+ value if value <= _match_class_max_Integer
42
+ end
43
+
44
+ # Use 2**63-1 as the default maximum value for the Integer
45
+ # matcher.
46
+ def _match_class_max_Integer
47
+ 9223372036854775807
48
+ end
49
+ end
50
+ end
51
+
52
+ register_plugin(:Integer_matcher_max, IntegerMatcherMax)
53
+ end
54
+ end
@@ -52,9 +52,9 @@ class Roda
52
52
  end
53
53
  end
54
54
  elsif matcher == Integer
55
- if matchdata = /\A\/(\d{1,100})(?=\/|\z)/.match(@remaining_path)
55
+ if (matchdata = /\A\/(\d{1,100})(?=\/|\z)/.match(@remaining_path)) && (value = _match_class_convert_Integer(matchdata[1]))
56
56
  @remaining_path = matchdata.post_match
57
- always{yield(matchdata[1].to_i)}
57
+ always{yield(value)}
58
58
  end
59
59
  else
60
60
  path = @remaining_path
@@ -151,9 +151,9 @@ class Roda
151
151
  always{yield rp[1, len]}
152
152
  end
153
153
  elsif matcher == Integer
154
- if matchdata = /\A\/(\d{1,100})\z/.match(@remaining_path)
154
+ if (matchdata = /\A\/(\d{1,100})\z/.match(@remaining_path)) && (value = _match_class_convert_Integer(matchdata[1]))
155
155
  @remaining_path = ''
156
- always{yield(matchdata[1].to_i)}
156
+ always{yield(value)}
157
157
  end
158
158
  else
159
159
  path = @remaining_path
@@ -28,6 +28,18 @@ class Roda
28
28
  # This is useful to DRY up code if you are using the same type of pattern and
29
29
  # type conversion in multiple places in your application.
30
30
  #
31
+ # If you have a segment match the passed regexp, but decide during block
32
+ # processing that you do not want to treat it as a match, you can have the
33
+ # block return nil or false. This is useful if you want to make sure you
34
+ # are using valid data:
35
+ #
36
+ # class_matcher(Date, /(\dd\d)-(\d\d)-(\d\d)/) do |y, m, d|
37
+ # y = y.to_i
38
+ # m = m.to_i
39
+ # d = d.to_i
40
+ # [Date.new(y, m, d)] if Date.valid_date?(y, m, d)
41
+ # end
42
+ #
31
43
  # This plugin does not work with the params_capturing plugin, as it does not
32
44
  # offer the ability to associate block arguments with named keys.
33
45
  module ClassMatchers
@@ -56,7 +56,7 @@ class Roda
56
56
  # r.halt(:template) if r.params['a']
57
57
  #
58
58
  # # symbol_views plugin, specifying status code, headers, and template file to render as body
59
- # r.halt(500, 'header=>'value', :other_template) if r.params['c']
59
+ # r.halt(500, {'header'=>'value'}, :other_template) if r.params['c']
60
60
  #
61
61
  # # json plugin, specifying status code and JSON body
62
62
  # r.halt(500, [{'error'=>'foo'}]) if r.params['b']
@@ -526,9 +526,10 @@ class Roda
526
526
  handle_type(:int, :max_input_bytesize=>100) do |v|
527
527
  string_or_numeric!(v) && v.to_i
528
528
  end
529
+ alias base_convert_int convert_int
529
530
 
530
531
  handle_type(:pos_int, :max_input_bytesize=>100) do |v|
531
- if (v = convert_int(v)) && v > 0
532
+ if (v = base_convert_int(v)) && v > 0
532
533
  v
533
534
  end
534
535
  end
@@ -547,6 +548,7 @@ class Roda
547
548
  end
548
549
  end
549
550
  end
551
+ alias base_convert_Integer convert_Integer
550
552
 
551
553
  handle_type(:float, :max_input_bytesize=>1000) do |v|
552
554
  string_or_numeric!(v) && v.to_f
@@ -0,0 +1,107 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The typecast_params_sized_integers plugin adds sized integer conversion
7
+ # methods to typecast_params:
8
+ #
9
+ # * int8, uint8, pos_int8, pos_uint8, Integer8, Integeru8
10
+ # * int16, uint16, pos_int16, pos_uint16, Integer16, Integeru16
11
+ # * int32, uint32, pos_int32, pos_uint32, Integer32, Integeru32
12
+ # * int64, uint64, pos_int64, pos_uint64, Integer64, Integeru64
13
+ #
14
+ # The int*, pos_int*, and Integer* methods operate the same as the
15
+ # standard int, pos_int, and Integer methods in typecast_params,
16
+ # except that they will only handle parameter values in the given
17
+ # range for the signed integer type. The uint*, pos_int*, and
18
+ # Integeru* methods are similar to the int*, pos_int*, and
19
+ # Integer* methods, except they use the range of the unsigned
20
+ # integer type instead of the range of the signed integer type.
21
+ #
22
+ # Here are the signed and unsigned integer type ranges:
23
+ # 8 :: [-128, 127], [0, 255]
24
+ # 16 :: [-32768, 32767], [0, 65535]
25
+ # 32 :: [-2147483648, 2147483647], [0, 4294967295]
26
+ # 64 :: [-9223372036854775808, 9223372036854775807], [0, 18446744073709551615]
27
+ #
28
+ # To only create methods for certain integer sizes, you can pass a
29
+ # :sizes option when loading the plugin, and it will only create
30
+ # methods for the sizes you specify.
31
+ #
32
+ # You can provide a :default_size option when loading the plugin,
33
+ # in which case the int, uint, pos_int, pos_uint, Integer, and Integeru,
34
+ # typecast_params conversion methods will be aliases to the conversion
35
+ # methods for the given sized type:
36
+ #
37
+ # plugin :typecast_params_sized_integers, default_size: 64
38
+ #
39
+ # route do |r|
40
+ # # Returns nil unless param.to_i > 0 && param.to_i <= 9223372036854775807
41
+ # typecast_params.pos_int('param_name')
42
+ # end
43
+ module TypecastParamsSizedIntegers
44
+ def self.load_dependencies(app, opts=OPTS)
45
+ app.plugin :typecast_params do
46
+ (opts[:sizes] || [8, 16, 32, 64]).each do |i|
47
+ # Avoid defining the same methods more than once
48
+ next if method_defined?(:"pos_int#{i}")
49
+
50
+ min_signed = -(2**(i-1))
51
+ max_signed = 2**(i-1)-1
52
+ max_unsigned = 2**i-1
53
+
54
+ handle_type(:"int#{i}", :max_input_bytesize=>100) do |v|
55
+ if (v = base_convert_int(v)) && v >= min_signed && v <= max_signed
56
+ v
57
+ end
58
+ end
59
+
60
+ handle_type(:"uint#{i}", :max_input_bytesize=>100) do |v|
61
+ if (v = base_convert_int(v)) && v >= 0 && v <= max_unsigned
62
+ v
63
+ end
64
+ end
65
+
66
+ handle_type(:"pos_int#{i}", :max_input_bytesize=>100) do |v|
67
+ if (v = base_convert_int(v)) && v > 0 && v <= max_signed
68
+ v
69
+ end
70
+ end
71
+
72
+ handle_type(:"pos_uint#{i}", :max_input_bytesize=>100) do |v|
73
+ if (v = base_convert_int(v)) && v > 0 && v <= max_unsigned
74
+ v
75
+ end
76
+ end
77
+
78
+ handle_type(:"Integer#{i}", :max_input_bytesize=>100) do |v|
79
+ if (v = base_convert_Integer(v)) && v >= min_signed && v <= max_signed
80
+ v
81
+ end
82
+ end
83
+
84
+ handle_type(:"Integeru#{i}", :max_input_bytesize=>100) do |v|
85
+ if (v = base_convert_Integer(v)) && v >= 0 && v <= max_unsigned
86
+ v
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ if default = opts[:default_size]
93
+ app::TypecastParams.class_eval do
94
+ %w[int uint pos_int pos_uint Integer Integeru].each do |type|
95
+ ['', 'convert_', '_convert_array_', '_max_input_bytesize_for_'].each do |prefix|
96
+ alias_method :"#{prefix}#{type}", :"#{prefix}#{type}#{default}"
97
+ end
98
+ alias_method :"#{type}!", :"#{type}#{default}!"
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ register_plugin(:typecast_params_sized_integers, TypecastParamsSizedIntegers)
106
+ end
107
+ end
@@ -20,6 +20,13 @@ class Roda
20
20
  # end
21
21
  module UnescapePath
22
22
  module RequestMethods
23
+ # Make sure the matched path calculation handles the unescaping
24
+ # of the remaining path.
25
+ def matched_path
26
+ e = @env
27
+ Rack::Utils.unescape(e["SCRIPT_NAME"] + e["PATH_INFO"]).chomp(@remaining_path)
28
+ end
29
+
23
30
  private
24
31
 
25
32
  # Unescape the path.
data/lib/roda/request.rb CHANGED
@@ -443,7 +443,16 @@ class Roda
443
443
  # Match integer segment of up to 100 decimal characters, and yield resulting value as an
444
444
  # integer.
445
445
  def _match_class_Integer
446
- consume(/\A\/(\d{1,100})(?=\/|\z)/){|i| [i.to_i]}
446
+ consume(/\A\/(\d{1,100})(?=\/|\z)/) do |i|
447
+ if i = _match_class_convert_Integer(i)
448
+ [i]
449
+ end
450
+ end
451
+ end
452
+
453
+ # Convert the segment matched by the Integer matcher to an integer.
454
+ def _match_class_convert_Integer(value)
455
+ value.to_i
447
456
  end
448
457
 
449
458
  # Match only if all of the arguments in the given array match.
@@ -548,9 +557,11 @@ class Roda
548
557
  # path from PATH_INFO, and updates captures with any regex captures.
549
558
  def consume(pattern)
550
559
  if matchdata = pattern.match(@remaining_path)
551
- @remaining_path = matchdata.post_match
552
560
  captures = matchdata.captures
553
- captures = yield(*captures) if defined?(yield)
561
+ if defined?(yield)
562
+ return unless captures = yield(*captures)
563
+ end
564
+ @remaining_path = matchdata.post_match
554
565
  @captures.concat(captures)
555
566
  end
556
567
  end
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 = 61
7
+ RodaMinorVersion = 62
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.61.0
4
+ version: 3.62.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: 2022-10-12 00:00:00.000000000 Z
11
+ date: 2022-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -234,6 +234,7 @@ extra_rdoc_files:
234
234
  - doc/release_notes/3.6.0.txt
235
235
  - doc/release_notes/3.60.0.txt
236
236
  - doc/release_notes/3.61.0.txt
237
+ - doc/release_notes/3.62.0.txt
237
238
  - doc/release_notes/3.7.0.txt
238
239
  - doc/release_notes/3.8.0.txt
239
240
  - doc/release_notes/3.9.0.txt
@@ -302,12 +303,14 @@ files:
302
303
  - doc/release_notes/3.6.0.txt
303
304
  - doc/release_notes/3.60.0.txt
304
305
  - doc/release_notes/3.61.0.txt
306
+ - doc/release_notes/3.62.0.txt
305
307
  - doc/release_notes/3.7.0.txt
306
308
  - doc/release_notes/3.8.0.txt
307
309
  - doc/release_notes/3.9.0.txt
308
310
  - lib/roda.rb
309
311
  - lib/roda/cache.rb
310
312
  - lib/roda/plugins.rb
313
+ - lib/roda/plugins/Integer_matcher_max.rb
311
314
  - lib/roda/plugins/_after_hook.rb
312
315
  - lib/roda/plugins/_before_hook.rb
313
316
  - lib/roda/plugins/_optimized_matching.rb
@@ -423,6 +426,7 @@ files:
423
426
  - lib/roda/plugins/timestamp_public.rb
424
427
  - lib/roda/plugins/type_routing.rb
425
428
  - lib/roda/plugins/typecast_params.rb
429
+ - lib/roda/plugins/typecast_params_sized_integers.rb
426
430
  - lib/roda/plugins/unescape_path.rb
427
431
  - lib/roda/plugins/view_options.rb
428
432
  - lib/roda/request.rb