roda 3.104.0 → 3.105.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 +4 -4
- data/lib/roda/plugins/_optimized_matching.rb +19 -7
- data/lib/roda/plugins/_symbol_class_matchers.rb +20 -19
- data/lib/roda/plugins/class_matchers.rb +18 -3
- data/lib/roda/plugins/sec_fetch_site_csrf.rb +4 -4
- data/lib/roda/plugins/symbol_matchers.rb +15 -6
- data/lib/roda/request.rb +34 -1
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f6f16557b167cdca71f0005ed7b4e40c4465b6400874e05a8ece95e7dad7b68
|
|
4
|
+
data.tar.gz: decd44332e836e46bc4bb2a6f93e7d40730fbf9f016c83b8a6ae99169c74efd3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 768134a23ee25d9f9d41641b1bf3dd5d499624b05adf9261f90300407a5dcd55d1535f080547d573b11e19d161c4563eb8538d998ee627d11329e6b59223db88
|
|
7
|
+
data.tar.gz: 00aa1dfbbc81b5754a824fa668324886338f4a4827d27a2de6cfff5b6318c8e4aba1c8c93afb8e96f80b214b4ba6b96917b3df77108f1eaa60153fa5fc5add5e
|
|
@@ -52,9 +52,20 @@ class Roda
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
elsif matcher == Integer
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
rp = @remaining_path
|
|
56
|
+
if /\A\/(\d{1,100})(?=\/|\z)/.match?(rp)
|
|
57
|
+
if last = rp.index('/', 1)
|
|
58
|
+
value = rp[1, last-1]
|
|
59
|
+
rp = rp[last, rp.length]
|
|
60
|
+
else
|
|
61
|
+
value = rp[1, rp.length]
|
|
62
|
+
rp = ""
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if value = scope.send(:_convert_class_Integer, value)
|
|
66
|
+
@remaining_path = rp
|
|
67
|
+
always{yield(value)}
|
|
68
|
+
end
|
|
58
69
|
end
|
|
59
70
|
else
|
|
60
71
|
path = @remaining_path
|
|
@@ -151,10 +162,11 @@ class Roda
|
|
|
151
162
|
always{yield rp[1, len]}
|
|
152
163
|
end
|
|
153
164
|
elsif matcher == Integer
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
rp = @remaining_path
|
|
166
|
+
if /\A\/(\d{1,100})\z/.match?(rp) && (value = scope.send(:_convert_class_Integer, rp[1, 101]))
|
|
167
|
+
@remaining_path = ''
|
|
168
|
+
always{yield(value)}
|
|
169
|
+
end
|
|
158
170
|
else
|
|
159
171
|
path = @remaining_path
|
|
160
172
|
captures = @captures.clear
|
|
@@ -8,7 +8,7 @@ class Roda
|
|
|
8
8
|
private
|
|
9
9
|
|
|
10
10
|
# Backend of symbol_matcher and class_matcher.
|
|
11
|
-
def _symbol_class_matcher(expected_class, obj, matcher, block, &request_class_block)
|
|
11
|
+
def _symbol_class_matcher(expected_class, obj, matcher, block, options=OPTS, &request_class_block)
|
|
12
12
|
unless obj.is_a?(expected_class)
|
|
13
13
|
raise RodaError, "Invalid type passed to class_matcher or symbol_matcher: #{matcher.inspect}"
|
|
14
14
|
end
|
|
@@ -30,23 +30,23 @@ class Roda
|
|
|
30
30
|
raise RodaError, "cannot provide Symbol matcher to class_matcher unless using symbol_matchers plugin: #{matcher.inspect}"
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
regexp, consume_regexp,
|
|
33
|
+
regexp, consume_regexp, convert_meth, consume_meth = opts[:symbol_matchers][matcher]
|
|
34
34
|
|
|
35
35
|
unless regexp
|
|
36
36
|
raise RodaError, "unregistered symbol matcher given to #{type}_matcher: #{matcher.inspect}"
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
block = _merge_matcher_blocks(type, obj, block,
|
|
39
|
+
block = _merge_matcher_blocks(type, obj, block, convert_meth)
|
|
40
40
|
when Class
|
|
41
41
|
unless opts[:class_matchers]
|
|
42
42
|
raise RodaError, "cannot provide Class matcher to symbol_matcher unless using class_matchers plugin: #{matcher.inspect}"
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
regexp, consume_regexp,
|
|
45
|
+
regexp, consume_regexp, convert_meth, consume_meth = opts[:class_matchers][matcher]
|
|
46
46
|
unless regexp
|
|
47
47
|
raise RodaError, "unregistered class matcher given to #{type}_matcher: #{matcher.inspect}"
|
|
48
48
|
end
|
|
49
|
-
block = _merge_matcher_blocks(type, obj, block,
|
|
49
|
+
block = _merge_matcher_blocks(type, obj, block, convert_meth)
|
|
50
50
|
else
|
|
51
51
|
raise RodaError, "unsupported matcher given to #{type}_matcher: #{matcher.inspect}"
|
|
52
52
|
end
|
|
@@ -59,7 +59,8 @@ class Roda
|
|
|
59
59
|
private convert_meth
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
consume_meth ||= options[:segment] ? :_consume_single_segment : :consume
|
|
63
|
+
array = opts[:"#{type}_matchers"][obj] = [regexp, consume_regexp, convert_meth, consume_meth].freeze
|
|
63
64
|
|
|
64
65
|
self::RodaRequest.class_eval do
|
|
65
66
|
class_exec(meth, array, &request_class_block)
|
|
@@ -69,30 +70,30 @@ class Roda
|
|
|
69
70
|
nil
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
# If both block and
|
|
73
|
+
# If both block and convert_meth are given,
|
|
73
74
|
# define a method for block, and then return a
|
|
74
|
-
# proc that calls
|
|
75
|
-
# the newly defined method with the return values of
|
|
75
|
+
# proc that calls convert_meth first, and only calls
|
|
76
|
+
# the newly defined method with the return values of convert_meth
|
|
76
77
|
# if matcher_method returns a truthy value.
|
|
77
|
-
# Otherwise, return
|
|
78
|
-
def _merge_matcher_blocks(type, obj, block,
|
|
79
|
-
if
|
|
78
|
+
# Otherwise, return convert_meth or block.
|
|
79
|
+
def _merge_matcher_blocks(type, obj, block, convert_meth)
|
|
80
|
+
if convert_meth
|
|
80
81
|
if block
|
|
81
|
-
|
|
82
|
-
define_method(
|
|
83
|
-
private
|
|
82
|
+
convert_merge_meth = :"_convert_merge_#{type}_#{obj}"
|
|
83
|
+
define_method(convert_merge_meth, &block)
|
|
84
|
+
private convert_merge_meth
|
|
84
85
|
|
|
85
86
|
proc do |*a|
|
|
86
|
-
if captures = send(
|
|
87
|
+
if captures = send(convert_meth, *a)
|
|
87
88
|
if captures.is_a?(Array)
|
|
88
|
-
send(
|
|
89
|
+
send(convert_merge_meth, *captures)
|
|
89
90
|
else
|
|
90
|
-
send(
|
|
91
|
+
send(convert_merge_meth, captures)
|
|
91
92
|
end
|
|
92
93
|
end
|
|
93
94
|
end
|
|
94
95
|
else
|
|
95
|
-
|
|
96
|
+
convert_meth
|
|
96
97
|
end
|
|
97
98
|
else
|
|
98
99
|
block
|
|
@@ -67,6 +67,17 @@ class Roda
|
|
|
67
67
|
# Blocks passed to the class_matchers plugin are evaluated in route
|
|
68
68
|
# block context.
|
|
69
69
|
#
|
|
70
|
+
# When passing a regexp as a matcher, to class_matcher, you can provide a
|
|
71
|
+
# <tt>segment: true</tt> option to speed up the matching on Ruby 2.4+:
|
|
72
|
+
#
|
|
73
|
+
# symbol_matcher(:employee_id, /E-(\d{6})/, segment: true) do |employee_id|
|
|
74
|
+
# employee_id.to_i
|
|
75
|
+
# end
|
|
76
|
+
#
|
|
77
|
+
# Use of <tt>segment: true</tt> requires that the regexp not match more than
|
|
78
|
+
# one segment (i.e. it cannot match +/+). Additionally, the entire segment will
|
|
79
|
+
# be captured, so any capture groups in the regexp will be ignored.
|
|
80
|
+
#
|
|
70
81
|
# This plugin does not work with the params_capturing plugin, as it does not
|
|
71
82
|
# offer the ability to associate block arguments with named keys.
|
|
72
83
|
module ClassMatchers
|
|
@@ -92,10 +103,14 @@ class Roda
|
|
|
92
103
|
# captures if no block was registered with the class or symbol. In either case,
|
|
93
104
|
# if a block is given, it should return an array with the captures to yield to
|
|
94
105
|
# the match block.
|
|
95
|
-
def class_matcher(klass, matcher, &block)
|
|
96
|
-
_symbol_class_matcher(Class, klass, matcher, block) do |meth, (_, regexp, convert_meth)|
|
|
106
|
+
def class_matcher(klass, matcher, opts=OPTS, &block)
|
|
107
|
+
_symbol_class_matcher(Class, klass, matcher, block, opts) do |meth, (_, regexp, convert_meth, consume_meth)|
|
|
97
108
|
if regexp
|
|
98
|
-
|
|
109
|
+
if consume_meth == :_consume_single_segment
|
|
110
|
+
define_method(meth){_consume_single_segment(regexp, convert_meth)}
|
|
111
|
+
else
|
|
112
|
+
define_method(meth){consume(regexp, convert_meth)}
|
|
113
|
+
end
|
|
99
114
|
else
|
|
100
115
|
define_method(meth){_consume_segment(convert_meth)}
|
|
101
116
|
end
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
class Roda
|
|
4
4
|
module RodaPlugins
|
|
5
|
-
# The
|
|
5
|
+
# The sec_fetch_site_csrf plugin allows for CSRF protection using the
|
|
6
6
|
# Sec-Fetch-Site header added in modern browsers. It allows for CSRF
|
|
7
7
|
# protection without the use of CSRF tokens, which can simplify
|
|
8
8
|
# form creation.
|
|
9
9
|
#
|
|
10
|
-
# The protection offered by the
|
|
10
|
+
# The protection offered by the sec_fetch_site_csrf plugin is weaker than
|
|
11
11
|
# the protection offered by the route_csrf plugin with default settings,
|
|
12
12
|
# since it doesn't support per-request tokens. Be aware you are trading
|
|
13
|
-
# security for simplicity when using the
|
|
14
|
-
# of the route_csrf plugin. Other caveats in using the
|
|
13
|
+
# security for simplicity when using the sec_fetch_site_csrf plugin instead
|
|
14
|
+
# of the route_csrf plugin. Other caveats in using the sec_fetch_site_csrf
|
|
15
15
|
# plugin:
|
|
16
16
|
#
|
|
17
17
|
# * Not all browsers set the Sec-Fetch-Site header. Some browsers
|
|
@@ -19,6 +19,15 @@ class Roda
|
|
|
19
19
|
# Then the route will only if the path is +/foobar123+, but not if it is
|
|
20
20
|
# +/foo+, +/FooBar123+, or +/foobar_123+.
|
|
21
21
|
#
|
|
22
|
+
# You can provide a <tt>segment: true</tt> option to symbol_matcher to speed up
|
|
23
|
+
# the matching on Ruby 2.4+:
|
|
24
|
+
#
|
|
25
|
+
# symbol_matcher :username, /([a-z0-9]{6,20})/, segment: true
|
|
26
|
+
#
|
|
27
|
+
# Use of <tt>segment: true</tt> requires that the regexp not match more than
|
|
28
|
+
# one segment (i.e. it cannot match +/+). Additionally, the entire segment will
|
|
29
|
+
# be captured, so any capture groups in the regexp will be ignored.
|
|
30
|
+
#
|
|
22
31
|
# By default, this plugin sets up the following symbol matchers:
|
|
23
32
|
#
|
|
24
33
|
# :d :: <tt>/(\d+)/</tt>, a decimal segment
|
|
@@ -106,8 +115,8 @@ class Roda
|
|
|
106
115
|
|
|
107
116
|
def self.configure(app)
|
|
108
117
|
app.opts[:symbol_matchers] ||= {}
|
|
109
|
-
app.symbol_matcher(:d, /(\d+)
|
|
110
|
-
app.symbol_matcher(:w, /(\w+)
|
|
118
|
+
app.symbol_matcher(:d, /(\d+)/, segment: true)
|
|
119
|
+
app.symbol_matcher(:w, /(\w+)/, segment: true)
|
|
111
120
|
app.symbol_matcher(:rest, /(.*)/)
|
|
112
121
|
end
|
|
113
122
|
|
|
@@ -122,8 +131,8 @@ class Roda
|
|
|
122
131
|
# captures if no block was registered with the symbol or class. In either case,
|
|
123
132
|
# if a block is given, it should return an array with the captures to yield to
|
|
124
133
|
# the match block.
|
|
125
|
-
def symbol_matcher(s, matcher, &block)
|
|
126
|
-
_symbol_class_matcher(Symbol, s, matcher, block) do |meth, array|
|
|
134
|
+
def symbol_matcher(s, matcher, opts=OPTS, &block)
|
|
135
|
+
_symbol_class_matcher(Symbol, s, matcher, block, opts) do |meth, array|
|
|
127
136
|
define_method(meth){array}
|
|
128
137
|
end
|
|
129
138
|
|
|
@@ -147,9 +156,9 @@ class Roda
|
|
|
147
156
|
meth = :"match_symbol_#{s}"
|
|
148
157
|
if respond_to?(meth, true)
|
|
149
158
|
# Allow calling private match methods
|
|
150
|
-
_, re, convert_meth = send(meth)
|
|
159
|
+
_, re, convert_meth, consume_meth = send(meth)
|
|
151
160
|
if re
|
|
152
|
-
|
|
161
|
+
send(consume_meth, re, convert_meth)
|
|
153
162
|
else
|
|
154
163
|
# defined in class_matchers plugin
|
|
155
164
|
_consume_segment(convert_meth)
|
data/lib/roda/request.rb
CHANGED
|
@@ -444,7 +444,7 @@ class Roda
|
|
|
444
444
|
# Match integer segment of up to 100 decimal characters, and yield resulting value as an
|
|
445
445
|
# integer.
|
|
446
446
|
def _match_class_Integer
|
|
447
|
-
|
|
447
|
+
_consume_single_segment(/\A\/(\d{1,100})(?=\/|\z)/, :_convert_class_Integer)
|
|
448
448
|
end
|
|
449
449
|
|
|
450
450
|
# Match only if all of the arguments in the given array match.
|
|
@@ -585,6 +585,39 @@ class Roda
|
|
|
585
585
|
end
|
|
586
586
|
end
|
|
587
587
|
|
|
588
|
+
if RUBY_VERSION >= "2.4"
|
|
589
|
+
# A faster version of consume that can be used if you are sure the regexp
|
|
590
|
+
# will match a single segment if it matches, capturing the entire segment.
|
|
591
|
+
def _consume_single_segment(regexp, meth=nil)
|
|
592
|
+
rp = @remaining_path
|
|
593
|
+
if regexp.match?(rp)
|
|
594
|
+
if last = rp.index('/', 1)
|
|
595
|
+
val = rp[1, last-1]
|
|
596
|
+
@remaining_path = rp[last, rp.length]
|
|
597
|
+
else
|
|
598
|
+
val = rp[1, rp.length]
|
|
599
|
+
@remaining_path = ""
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
if meth
|
|
603
|
+
if captures = scope.send(meth, val)
|
|
604
|
+
if captures.is_a?(Array)
|
|
605
|
+
@captures.concat(captures)
|
|
606
|
+
else
|
|
607
|
+
@captures << captures
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
else
|
|
611
|
+
@captures << val
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
# :nocov:
|
|
616
|
+
else
|
|
617
|
+
alias _consume_single_segment consume
|
|
618
|
+
# :nocov:
|
|
619
|
+
end
|
|
620
|
+
|
|
588
621
|
# The default path to use for redirects when a path is not given.
|
|
589
622
|
# For non-GET requests, redirects to the current path, which will
|
|
590
623
|
# trigger a GET request. This is to make the common case where
|
data/lib/roda/version.rb
CHANGED
data/lib/roda.rb
CHANGED
|
@@ -217,7 +217,7 @@ class Roda
|
|
|
217
217
|
plugin :direct_call
|
|
218
218
|
end
|
|
219
219
|
|
|
220
|
-
if ([:on, :is, :_verb, :_match_class_String, :_match_class_Integer, :_match_string, :_match_regexp, :empty_path?, :if_match, :match, :_match_class]).all?{|m| self::RodaRequest.instance_method(m).owner == RequestMethods}
|
|
220
|
+
if RUBY_VERSION > "2.4" && ([:on, :is, :_verb, :_match_class_String, :_match_class_Integer, :_match_string, :_match_regexp, :empty_path?, :if_match, :match, :_match_class]).all?{|m| self::RodaRequest.instance_method(m).owner == RequestMethods}
|
|
221
221
|
plugin :_optimized_matching
|
|
222
222
|
end
|
|
223
223
|
end
|