roda 3.83.0 → 3.85.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/roda/plugins/Integer_matcher_max.rb +9 -8
- data/lib/roda/plugins/_optimized_matching.rb +2 -2
- data/lib/roda/plugins/_symbol_class_matchers.rb +107 -0
- data/lib/roda/plugins/capture_erb.rb +6 -5
- data/lib/roda/plugins/class_matchers.rb +91 -14
- data/lib/roda/plugins/hsts.rb +35 -0
- data/lib/roda/plugins/placeholder_string_matchers.rb +4 -0
- data/lib/roda/plugins/public.rb +1 -1
- data/lib/roda/plugins/render.rb +1 -1
- data/lib/roda/plugins/symbol_matchers.rb +70 -15
- data/lib/roda/request.rb +16 -13
- data/lib/roda/response.rb +1 -1
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +7 -0
- metadata +5 -179
- data/CHANGELOG +0 -691
- data/README.rdoc +0 -1136
- data/doc/conventions.rdoc +0 -177
- data/doc/release_notes/3.0.0.txt +0 -84
- data/doc/release_notes/3.1.0.txt +0 -24
- data/doc/release_notes/3.10.0.txt +0 -132
- data/doc/release_notes/3.11.0.txt +0 -54
- data/doc/release_notes/3.12.0.txt +0 -19
- data/doc/release_notes/3.13.0.txt +0 -38
- data/doc/release_notes/3.14.0.txt +0 -36
- data/doc/release_notes/3.14.1.txt +0 -43
- data/doc/release_notes/3.15.0.txt +0 -21
- data/doc/release_notes/3.16.0.txt +0 -52
- data/doc/release_notes/3.17.0.txt +0 -62
- data/doc/release_notes/3.18.0.txt +0 -170
- data/doc/release_notes/3.19.0.txt +0 -229
- data/doc/release_notes/3.2.0.txt +0 -22
- data/doc/release_notes/3.20.0.txt +0 -7
- data/doc/release_notes/3.21.0.txt +0 -5
- data/doc/release_notes/3.22.0.txt +0 -24
- data/doc/release_notes/3.23.0.txt +0 -28
- data/doc/release_notes/3.24.0.txt +0 -14
- data/doc/release_notes/3.25.0.txt +0 -12
- data/doc/release_notes/3.26.0.txt +0 -15
- data/doc/release_notes/3.27.0.txt +0 -15
- data/doc/release_notes/3.28.0.txt +0 -13
- data/doc/release_notes/3.29.0.txt +0 -15
- data/doc/release_notes/3.3.0.txt +0 -291
- data/doc/release_notes/3.30.0.txt +0 -14
- data/doc/release_notes/3.31.0.txt +0 -11
- data/doc/release_notes/3.32.0.txt +0 -42
- data/doc/release_notes/3.33.0.txt +0 -8
- data/doc/release_notes/3.34.0.txt +0 -17
- data/doc/release_notes/3.35.0.txt +0 -12
- data/doc/release_notes/3.36.0.txt +0 -17
- data/doc/release_notes/3.37.0.txt +0 -42
- data/doc/release_notes/3.38.0.txt +0 -5
- data/doc/release_notes/3.39.0.txt +0 -16
- data/doc/release_notes/3.4.0.txt +0 -24
- data/doc/release_notes/3.40.0.txt +0 -24
- data/doc/release_notes/3.41.0.txt +0 -9
- data/doc/release_notes/3.42.0.txt +0 -21
- data/doc/release_notes/3.43.0.txt +0 -34
- data/doc/release_notes/3.44.0.txt +0 -23
- data/doc/release_notes/3.45.0.txt +0 -22
- data/doc/release_notes/3.46.0.txt +0 -19
- data/doc/release_notes/3.47.0.txt +0 -13
- data/doc/release_notes/3.48.0.txt +0 -10
- data/doc/release_notes/3.49.0.txt +0 -18
- data/doc/release_notes/3.5.0.txt +0 -31
- data/doc/release_notes/3.50.0.txt +0 -21
- data/doc/release_notes/3.51.0.txt +0 -20
- data/doc/release_notes/3.52.0.txt +0 -20
- data/doc/release_notes/3.53.0.txt +0 -14
- data/doc/release_notes/3.54.0.txt +0 -48
- data/doc/release_notes/3.55.0.txt +0 -12
- data/doc/release_notes/3.56.0.txt +0 -33
- data/doc/release_notes/3.57.0.txt +0 -34
- data/doc/release_notes/3.58.0.txt +0 -16
- data/doc/release_notes/3.59.0.txt +0 -17
- data/doc/release_notes/3.6.0.txt +0 -21
- data/doc/release_notes/3.60.0.txt +0 -56
- data/doc/release_notes/3.61.0.txt +0 -24
- data/doc/release_notes/3.62.0.txt +0 -41
- data/doc/release_notes/3.63.0.txt +0 -36
- data/doc/release_notes/3.64.0.txt +0 -26
- data/doc/release_notes/3.65.0.txt +0 -12
- data/doc/release_notes/3.66.0.txt +0 -23
- data/doc/release_notes/3.67.0.txt +0 -25
- data/doc/release_notes/3.68.0.txt +0 -21
- data/doc/release_notes/3.69.0.txt +0 -33
- data/doc/release_notes/3.7.0.txt +0 -123
- data/doc/release_notes/3.70.0.txt +0 -19
- data/doc/release_notes/3.71.0.txt +0 -33
- data/doc/release_notes/3.72.0.txt +0 -48
- data/doc/release_notes/3.73.0.txt +0 -33
- data/doc/release_notes/3.74.0.txt +0 -28
- data/doc/release_notes/3.75.0.txt +0 -19
- data/doc/release_notes/3.76.0.txt +0 -18
- data/doc/release_notes/3.77.0.txt +0 -8
- data/doc/release_notes/3.78.0.txt +0 -99
- data/doc/release_notes/3.79.0.txt +0 -148
- data/doc/release_notes/3.8.0.txt +0 -27
- data/doc/release_notes/3.80.0.txt +0 -31
- data/doc/release_notes/3.81.0.txt +0 -24
- data/doc/release_notes/3.82.0.txt +0 -43
- data/doc/release_notes/3.83.0.txt +0 -6
- data/doc/release_notes/3.9.0.txt +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a862ed0414bf90081e744ed7c50ac19806f141ebeae8c699d5f96b9a4e718e10
|
4
|
+
data.tar.gz: b1fcd622255d24044ba7f26c2c3f35e5fc8ea7f30b2ba577bec5fe306ca75c76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '086d21f564d66b14fafabbb99c6d670559c12fed91105c1acb9e5e9147623bf15de0730d35f2b5bd098a6e26aba0d6a73497a914ee23d97d8cb58dbbf0f6dd5b'
|
7
|
+
data.tar.gz: 713dee3419ce7bf52742d7da6be4243c393b393aeb5762dcd0dc61a58d691e586226636b5df687e56e0dd280a89930a18b1e32f3c9b499516afc461cf92fcf4c
|
@@ -23,27 +23,28 @@ class Roda
|
|
23
23
|
module IntegerMatcherMax
|
24
24
|
def self.configure(app, max=nil)
|
25
25
|
if max
|
26
|
-
app
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
app.class_eval do
|
27
|
+
meth = :_max_value_convert_class_Integer
|
28
|
+
define_method(meth){max}
|
29
|
+
alias_method meth, meth
|
30
|
+
private meth
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
|
-
module
|
35
|
+
module InstanceMethods
|
35
36
|
private
|
36
37
|
|
37
38
|
# Do not have the Integer matcher max when over the maximum
|
38
39
|
# configured Integer value.
|
39
|
-
def
|
40
|
+
def _convert_class_Integer(value)
|
40
41
|
value = super
|
41
|
-
value if value <=
|
42
|
+
value if value <= _max_value_convert_class_Integer
|
42
43
|
end
|
43
44
|
|
44
45
|
# Use 2**63-1 as the default maximum value for the Integer
|
45
46
|
# matcher.
|
46
|
-
def
|
47
|
+
def _max_value_convert_class_Integer
|
47
48
|
9223372036854775807
|
48
49
|
end
|
49
50
|
end
|
@@ -52,7 +52,7 @@ class Roda
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
elsif matcher == Integer
|
55
|
-
if (matchdata = /\A\/(\d{1,100})(?=\/|\z)/.match(@remaining_path)) && (value =
|
55
|
+
if (matchdata = /\A\/(\d{1,100})(?=\/|\z)/.match(@remaining_path)) && (value = scope.send(:_convert_class_Integer, matchdata[1]))
|
56
56
|
@remaining_path = matchdata.post_match
|
57
57
|
always{yield(value)}
|
58
58
|
end
|
@@ -151,7 +151,7 @@ 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)) && (value =
|
154
|
+
if (matchdata = /\A\/(\d{1,100})\z/.match(@remaining_path)) && (value = scope.send(:_convert_class_Integer, matchdata[1]))
|
155
155
|
@remaining_path = ''
|
156
156
|
always{yield(value)}
|
157
157
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
module SymbolClassMatchers_
|
7
|
+
module ClassMethods
|
8
|
+
private
|
9
|
+
|
10
|
+
# Backend of symbol_matcher and class_matcher.
|
11
|
+
def _symbol_class_matcher(expected_class, obj, matcher, block, &request_class_block)
|
12
|
+
unless obj.is_a?(expected_class)
|
13
|
+
raise RodaError, "Invalid type passed to class_matcher or symbol_matcher: #{matcher.inspect}"
|
14
|
+
end
|
15
|
+
|
16
|
+
if obj.is_a?(Symbol)
|
17
|
+
type = "symbol"
|
18
|
+
meth = :"match_symbol_#{obj}"
|
19
|
+
else
|
20
|
+
type = "class"
|
21
|
+
meth = :"_match_class_#{obj}"
|
22
|
+
end
|
23
|
+
|
24
|
+
case matcher
|
25
|
+
when Regexp
|
26
|
+
regexp = matcher
|
27
|
+
consume_regexp = self::RodaRequest.send(:consume_pattern, regexp)
|
28
|
+
when Symbol
|
29
|
+
unless opts[:symbol_matchers]
|
30
|
+
raise RodaError, "cannot provide Symbol matcher to class_matcher unless using symbol_matchers plugin: #{matcher.inspect}"
|
31
|
+
end
|
32
|
+
|
33
|
+
regexp, consume_regexp, matcher_block = opts[:symbol_matchers][matcher]
|
34
|
+
|
35
|
+
unless regexp
|
36
|
+
raise RodaError, "unregistered symbol matcher given to #{type}_matcher: #{matcher.inspect}"
|
37
|
+
end
|
38
|
+
|
39
|
+
block = _merge_matcher_blocks(type, obj, block, matcher_block)
|
40
|
+
when Class
|
41
|
+
unless opts[:class_matchers]
|
42
|
+
raise RodaError, "cannot provide Class matcher to symbol_matcher unless using class_matchers plugin: #{matcher.inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
regexp, consume_regexp, matcher_block = opts[:class_matchers][matcher]
|
46
|
+
unless regexp
|
47
|
+
raise RodaError, "unregistered class matcher given to #{type}_matcher: #{matcher.inspect}"
|
48
|
+
end
|
49
|
+
block = _merge_matcher_blocks(type, obj, block, matcher_block)
|
50
|
+
else
|
51
|
+
raise RodaError, "unsupported matcher given to #{type}_matcher: #{matcher.inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
if block.is_a?(Symbol)
|
55
|
+
convert_meth = block
|
56
|
+
elsif block
|
57
|
+
convert_meth = :"_convert_#{type}_#{obj}"
|
58
|
+
define_method(convert_meth, &block)
|
59
|
+
private convert_meth
|
60
|
+
end
|
61
|
+
|
62
|
+
array = opts[:"#{type}_matchers"][obj] = [regexp, consume_regexp, convert_meth].freeze
|
63
|
+
|
64
|
+
self::RodaRequest.class_eval do
|
65
|
+
class_exec(meth, array, &request_class_block)
|
66
|
+
private meth
|
67
|
+
end
|
68
|
+
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
# If both block and matche_meth are given,
|
73
|
+
# define a method for block, and then return a
|
74
|
+
# proc that calls matcher_meth first, and only calls
|
75
|
+
# the newly defined method with the return values of matcher_meth
|
76
|
+
# if matcher_method returns a truthy value.
|
77
|
+
# Otherwise, return matcher_meth or block.
|
78
|
+
def _merge_matcher_blocks(type, obj, block, matcher_meth)
|
79
|
+
if matcher_meth
|
80
|
+
if block
|
81
|
+
convert_meth = :"_convert_merge_#{type}_#{obj}"
|
82
|
+
define_method(convert_meth, &block)
|
83
|
+
private convert_meth
|
84
|
+
|
85
|
+
proc do |*a|
|
86
|
+
if captures = send(matcher_meth, *a)
|
87
|
+
if captures.is_a?(Array)
|
88
|
+
send(convert_meth, *captures)
|
89
|
+
else
|
90
|
+
send(convert_meth, captures)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
else
|
95
|
+
matcher_meth
|
96
|
+
end
|
97
|
+
else
|
98
|
+
block
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
register_plugin(:_symbol_class_matchers, SymbolClassMatchers_)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
@@ -16,10 +16,11 @@ class Roda
|
|
16
16
|
# to wrap template blocks with arbitrary output and then inject the
|
17
17
|
# wrapped output into the template.
|
18
18
|
#
|
19
|
-
# If the output buffer object responds to +capture+
|
20
|
-
# +erubi/capture_block+ is being
|
21
|
-
# this will call +capture+ on the
|
22
|
-
# of setting the output buffer object
|
19
|
+
# If the output buffer object responds to +capture+ and is not
|
20
|
+
# an instance of String (e.g. when +erubi/capture_block+ is being
|
21
|
+
# used as the template engine), this will call +capture+ on the
|
22
|
+
# output buffer object, instead of setting the output buffer object
|
23
|
+
# temporarily to a new object.
|
23
24
|
module CaptureERB
|
24
25
|
def self.load_dependencies(app)
|
25
26
|
app.plugin :render
|
@@ -34,7 +35,7 @@ class Roda
|
|
34
35
|
outvar = render_opts[:template_opts][:outvar]
|
35
36
|
buf_was = instance_variable_get(outvar)
|
36
37
|
|
37
|
-
if buf_was.respond_to?(:capture)
|
38
|
+
if buf_was.respond_to?(:capture) && !buf_was.instance_of?(String)
|
38
39
|
buf_was.capture(&block)
|
39
40
|
else
|
40
41
|
begin
|
@@ -12,11 +12,10 @@ class Roda
|
|
12
12
|
# # ...
|
13
13
|
# end
|
14
14
|
#
|
15
|
-
# You can register a Date class matcher for that regexp
|
16
|
-
# the block must return an array):
|
15
|
+
# You can register a Date class matcher for that regexp:
|
17
16
|
#
|
18
17
|
# class_matcher(Date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d|
|
19
|
-
#
|
18
|
+
# Date.new(y.to_i, m.to_i, d.to_i)
|
20
19
|
# end
|
21
20
|
#
|
22
21
|
# And then use the Date class as a matcher, and it will yield a Date object:
|
@@ -26,7 +25,8 @@ class Roda
|
|
26
25
|
# end
|
27
26
|
#
|
28
27
|
# 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.
|
28
|
+
# type conversion in multiple places in your application. You can have the
|
29
|
+
# block return an array to yield multiple captures.
|
30
30
|
#
|
31
31
|
# If you have a segment match the passed regexp, but decide during block
|
32
32
|
# processing that you do not want to treat it as a match, you can have the
|
@@ -37,22 +37,99 @@ class Roda
|
|
37
37
|
# y = y.to_i
|
38
38
|
# m = m.to_i
|
39
39
|
# d = d.to_i
|
40
|
-
#
|
40
|
+
# Date.new(y, m, d) if Date.valid_date?(y, m, d)
|
41
41
|
# end
|
42
42
|
#
|
43
|
+
# The second argument to class_matcher can be a class already registered
|
44
|
+
# as a class matcher. This can DRY up code that wants a conversion
|
45
|
+
# performed by an existing class matcher:
|
46
|
+
#
|
47
|
+
# class_matcher Employee, Integer do |id|
|
48
|
+
# Employee[id]
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# With the above example, the Integer matcher performs the conversion to
|
52
|
+
# integer, so +id+ is yielded as an integer. The block then looks up the
|
53
|
+
# employee with that id. If there is no employee with that id, then
|
54
|
+
# the Employee matcher will not match.
|
55
|
+
#
|
56
|
+
# If using the symbol_matchers plugin, you can provide a recognized symbol
|
57
|
+
# matcher as the second argument to class_matcher, and it will work in
|
58
|
+
# a similar manner:
|
59
|
+
#
|
60
|
+
# symbol_matcher(:employee_id, /E-(\d{6})/) do |employee_id|
|
61
|
+
# employee_id.to_i
|
62
|
+
# end
|
63
|
+
# class_matcher Employee, :employee_id do |id|
|
64
|
+
# Employee[id]
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Blocks passed to the class_matchers plugin are evaluated in route
|
68
|
+
# block context.
|
69
|
+
#
|
43
70
|
# This plugin does not work with the params_capturing plugin, as it does not
|
44
71
|
# offer the ability to associate block arguments with named keys.
|
45
72
|
module ClassMatchers
|
73
|
+
def self.load_dependencies(app)
|
74
|
+
app.plugin :_symbol_class_matchers
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.configure(app)
|
78
|
+
app.opts[:class_matchers] ||= {
|
79
|
+
Integer=>[/(\d{1,100})/, /\A\/(\d{1,100})(?=\/|\z)/, :_convert_class_Integer].freeze,
|
80
|
+
String=>[/([^\/]+)/, nil, nil].freeze
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
46
84
|
module ClassMethods
|
47
|
-
# Set the
|
48
|
-
#
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
85
|
+
# Set the matcher and block to use for the given class.
|
86
|
+
# The matcher can be a regexp, registered class matcher, or registered symbol
|
87
|
+
# matcher (if using the symbol_matchers plugin).
|
88
|
+
#
|
89
|
+
# If providing a regexp, the block given will be called with all regexp captures.
|
90
|
+
# If providing a registered class or symbol, the block will be called with the
|
91
|
+
# captures returned by the block for the registered class or symbol, or the regexp
|
92
|
+
# captures if no block was registered with the class or symbol. In either case,
|
93
|
+
# if a block is given, it should return an array with the captures to yield to
|
94
|
+
# the match block.
|
95
|
+
def class_matcher(klass, matcher, &block)
|
96
|
+
_symbol_class_matcher(Class, klass, matcher, block) do |meth, (_, regexp, convert_meth)|
|
97
|
+
if regexp
|
98
|
+
define_method(meth){consume(regexp, convert_meth)}
|
99
|
+
else
|
100
|
+
define_method(meth){_consume_segment(convert_meth)}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Freeze the class_matchers hash when freezing the app.
|
106
|
+
def freeze
|
107
|
+
opts[:class_matchers].freeze
|
108
|
+
super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module RequestMethods
|
113
|
+
# Use faster approach for segment matching. This is used for
|
114
|
+
# matchers based on the String class matcher, and avoids the
|
115
|
+
# use of regular expressions for scanning.
|
116
|
+
def _consume_segment(convert_meth)
|
117
|
+
rp = @remaining_path
|
118
|
+
if _match_class_String
|
119
|
+
if convert_meth
|
120
|
+
if captures = scope.send(convert_meth, @captures.pop)
|
121
|
+
if captures.is_a?(Array)
|
122
|
+
@captures.concat(captures)
|
123
|
+
else
|
124
|
+
@captures << captures
|
125
|
+
end
|
126
|
+
else
|
127
|
+
@remaining_path = rp
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
else
|
131
|
+
true
|
132
|
+
end
|
56
133
|
end
|
57
134
|
end
|
58
135
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The hsts plugin allows for easily configuring an appropriate
|
7
|
+
# Strict-Transport-Security response header for the application:
|
8
|
+
#
|
9
|
+
# plugin :hsts
|
10
|
+
# # Strict-Transport-Security: max-age=63072000; includeSubDomains
|
11
|
+
#
|
12
|
+
# plugin :hsts, preload: true
|
13
|
+
# # Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
|
14
|
+
#
|
15
|
+
# plugin :hsts, max_age: 31536000, subdomains: false
|
16
|
+
# # Strict-Transport-Security: max-age=31536000
|
17
|
+
module Hsts
|
18
|
+
# Ensure default_headers plugin is loaded first
|
19
|
+
def self.load_dependencies(app, opts=OPTS)
|
20
|
+
app.plugin :default_headers
|
21
|
+
end
|
22
|
+
|
23
|
+
# Configure the Strict-Transport-Security header. Options:
|
24
|
+
# :max_age :: Set max-age in seconds (default is 63072000, two years)
|
25
|
+
# :preload :: Set preload, so the domain can be included in HSTS preload lists
|
26
|
+
# :subdomains :: Set to false to not set includeSubDomains. By default,
|
27
|
+
# includeSubDomains is set to enforce HTTPS for subdomains.
|
28
|
+
def self.configure(app, opts=OPTS)
|
29
|
+
app.plugin :default_headers, RodaResponseHeaders::STRICT_TRANSPORT_SECURITY => "max-age=#{opts[:max_age]||63072000}#{'; includeSubDomains' unless opts[:subdomains] == false}#{'; preload' if opts[:preload]}".freeze
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
register_plugin(:hsts, Hsts)
|
34
|
+
end
|
35
|
+
end
|
@@ -21,6 +21,10 @@ class Roda
|
|
21
21
|
#
|
22
22
|
# r.is "foo", String
|
23
23
|
# r.is "foo", :bar
|
24
|
+
#
|
25
|
+
# If used with the symbol_matchers plugin, this plugin respects the regexps
|
26
|
+
# for the registered symbols, but it does not perform the conversions, the
|
27
|
+
# captures for the regexp are used directly as the captures for the match method.
|
24
28
|
module PlaceholderStringMatchers
|
25
29
|
def self.load_dependencies(app)
|
26
30
|
app.plugin :_symbol_regexp_matchers
|
data/lib/roda/plugins/public.rb
CHANGED
@@ -42,12 +42,12 @@ class Roda
|
|
42
42
|
# end
|
43
43
|
module Public
|
44
44
|
SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact)
|
45
|
-
PARSER = URI::DEFAULT_PARSER
|
46
45
|
RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
|
47
46
|
ENCODING_MAP = {:zstd=>'zstd', :brotli=>'br', :gzip=>'gzip'}.freeze
|
48
47
|
ENCODING_EXTENSIONS = {'br'=>'.br', 'gzip'=>'.gz', 'zstd'=>'.zst'}.freeze
|
49
48
|
|
50
49
|
# :nocov:
|
50
|
+
PARSER = defined?(::URI::RFC2396_PARSER) ? ::URI::RFC2396_PARSER : ::URI::DEFAULT_PARSER
|
51
51
|
MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
|
52
52
|
# :nocov:
|
53
53
|
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -48,7 +48,7 @@ class Roda
|
|
48
48
|
#
|
49
49
|
# :allowed_paths :: Set the template paths to allow. Attempts to render paths outside
|
50
50
|
# of these paths will raise an error. Defaults to the +:views+ directory.
|
51
|
-
# :cache :: nil/false to explicitly disable
|
51
|
+
# :cache :: nil/false to explicitly disable permanent template caching. By default, permanent
|
52
52
|
# template caching is disabled by default if RACK_ENV is development. When permanent
|
53
53
|
# template caching is disabled, for templates with paths in the file system, the
|
54
54
|
# modification time of the file will be checked on every render, and if it has changed,
|
@@ -23,7 +23,7 @@ class Roda
|
|
23
23
|
#
|
24
24
|
# :d :: <tt>/(\d+)/</tt>, a decimal segment
|
25
25
|
# :rest :: <tt>/(.*)/</tt>, all remaining characters, if any
|
26
|
-
# :w :: <tt>/(\w+)/</tt>,
|
26
|
+
# :w :: <tt>/(\w+)/</tt>, an alphanumeric segment
|
27
27
|
#
|
28
28
|
# If the placeholder_string_matchers plugin is loaded, this feature also applies to
|
29
29
|
# placeholders in strings, so the following:
|
@@ -39,11 +39,10 @@ class Roda
|
|
39
39
|
# be loaded first.
|
40
40
|
#
|
41
41
|
# You can provide a block when calling +symbol_matcher+, and it will be called
|
42
|
-
# for all matches to allow for type conversion
|
43
|
-
# array:
|
42
|
+
# for all matches to allow for type conversion:
|
44
43
|
#
|
45
44
|
# symbol_matcher(:date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d|
|
46
|
-
#
|
45
|
+
# Date.new(y.to_i, m.to_i, d.to_i)
|
47
46
|
# end
|
48
47
|
#
|
49
48
|
# route do |r|
|
@@ -61,29 +60,80 @@ class Roda
|
|
61
60
|
# y = y.to_i
|
62
61
|
# m = m.to_i
|
63
62
|
# d = d.to_i
|
64
|
-
#
|
63
|
+
# Date.new(y, m, d) if Date.valid_date?(y, m, d)
|
65
64
|
# end
|
66
65
|
#
|
67
|
-
#
|
68
|
-
#
|
66
|
+
# You can have the block return an array to yield multiple captures.
|
67
|
+
#
|
68
|
+
# The second argument to symbol_matcher can be a symbol already registered
|
69
|
+
# as a symbol matcher. This can DRY up code that wants a conversion
|
70
|
+
# performed by an existing class matcher or to use the same regexp:
|
71
|
+
#
|
72
|
+
# symbol_matcher :employee_id, :d do |id|
|
73
|
+
# id.to_i
|
74
|
+
# end
|
75
|
+
# symbol_matcher :employee, :employee_id do |id|
|
76
|
+
# Employee[id]
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# With the above example, the :d matcher matches only decimal strings, but
|
80
|
+
# yields them as string. The registered :employee_id matcher converts the
|
81
|
+
# decimal string to an integer. The registered :employee matcher builds
|
82
|
+
# on that and uses the integer to lookup the related employee. If there is
|
83
|
+
# no employee with that id, then the :employee matcher will not match.
|
84
|
+
#
|
85
|
+
# If using the class_matchers plugin, you can provide a recognized class
|
86
|
+
# matcher as the second argument to symbol_matcher, and it will work in
|
87
|
+
# a similar manner:
|
88
|
+
#
|
89
|
+
# symbol_matcher :employee, Integer do |id|
|
90
|
+
# Employee[id]
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# Blocks passed to the symbol matchers plugin are evaluated in route
|
94
|
+
# block context.
|
95
|
+
#
|
96
|
+
# If providing a block to the symbol_matchers plugin, the symbol may
|
97
|
+
# not work with the params_capturing plugin. Note that the use of
|
98
|
+
# symbol matchers inside strings when using the placeholder_string_matchers
|
99
|
+
# plugin only uses the regexp, it does not respect the conversion blocks
|
100
|
+
# registered with the symbols.
|
69
101
|
module SymbolMatchers
|
70
102
|
def self.load_dependencies(app)
|
71
103
|
app.plugin :_symbol_regexp_matchers
|
104
|
+
app.plugin :_symbol_class_matchers
|
72
105
|
end
|
73
106
|
|
74
107
|
def self.configure(app)
|
108
|
+
app.opts[:symbol_matchers] ||= {}
|
75
109
|
app.symbol_matcher(:d, /(\d+)/)
|
76
110
|
app.symbol_matcher(:w, /(\w+)/)
|
77
111
|
app.symbol_matcher(:rest, /(.*)/)
|
78
112
|
end
|
79
113
|
|
80
114
|
module ClassMethods
|
81
|
-
# Set the
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
115
|
+
# Set the matcher and block to use for the given class.
|
116
|
+
# The matcher can be a regexp, registered symbol matcher, or registered class
|
117
|
+
# matcher (if using the class_matchers plugin).
|
118
|
+
#
|
119
|
+
# If providing a regexp, the block given will be called with all regexp captures.
|
120
|
+
# If providing a registered symbol or class, the block will be called with the
|
121
|
+
# captures returned by the block for the registered symbol or class, or the regexp
|
122
|
+
# captures if no block was registered with the symbol or class. In either case,
|
123
|
+
# if a block is given, it should return an array with the captures to yield to
|
124
|
+
# the match block.
|
125
|
+
def symbol_matcher(s, matcher, &block)
|
126
|
+
_symbol_class_matcher(Symbol, s, matcher, block) do |meth, array|
|
127
|
+
define_method(meth){array}
|
128
|
+
end
|
129
|
+
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# Freeze the class_matchers hash when freezing the app.
|
134
|
+
def freeze
|
135
|
+
opts[:symbol_matchers].freeze
|
136
|
+
super
|
87
137
|
end
|
88
138
|
end
|
89
139
|
|
@@ -97,8 +147,13 @@ class Roda
|
|
97
147
|
meth = :"match_symbol_#{s}"
|
98
148
|
if respond_to?(meth, true)
|
99
149
|
# Allow calling private match methods
|
100
|
-
re,
|
101
|
-
|
150
|
+
_, re, convert_meth = send(meth)
|
151
|
+
if re
|
152
|
+
consume(re, convert_meth)
|
153
|
+
else
|
154
|
+
# defined in class_matchers plugin
|
155
|
+
_consume_segment(convert_meth)
|
156
|
+
end
|
102
157
|
else
|
103
158
|
super
|
104
159
|
end
|
data/lib/roda/request.rb
CHANGED
@@ -443,16 +443,7 @@ 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)
|
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
|
446
|
+
consume(/\A\/(\d{1,100})(?=\/|\z)/, :_convert_class_Integer)
|
456
447
|
end
|
457
448
|
|
458
449
|
# Match only if all of the arguments in the given array match.
|
@@ -555,14 +546,26 @@ class Roda
|
|
555
546
|
# match, returns false without changes. Otherwise, modifies
|
556
547
|
# SCRIPT_NAME to include the matched path, removes the matched
|
557
548
|
# path from PATH_INFO, and updates captures with any regex captures.
|
558
|
-
def consume(pattern)
|
549
|
+
def consume(pattern, meth=nil)
|
559
550
|
if matchdata = pattern.match(@remaining_path)
|
560
551
|
captures = matchdata.captures
|
561
|
-
|
552
|
+
|
553
|
+
if meth
|
554
|
+
return unless captures = scope.send(meth, *captures)
|
555
|
+
# :nocov:
|
556
|
+
elsif defined?(yield)
|
557
|
+
# RODA4: Remove
|
562
558
|
return unless captures = yield(*captures)
|
559
|
+
# :nocov:
|
563
560
|
end
|
561
|
+
|
564
562
|
@remaining_path = matchdata.post_match
|
565
|
-
|
563
|
+
|
564
|
+
if captures.is_a?(Array)
|
565
|
+
@captures.concat(captures)
|
566
|
+
else
|
567
|
+
@captures << captures
|
568
|
+
end
|
566
569
|
end
|
567
570
|
end
|
568
571
|
|
data/lib/roda/response.rb
CHANGED
@@ -15,7 +15,7 @@ class Roda
|
|
15
15
|
%w'Allow Cache-Control Content-Disposition Content-Encoding Content-Length
|
16
16
|
Content-Security-Policy Content-Security-Policy-Report-Only Content-Type
|
17
17
|
ETag Expires Last-Modified Link Location Set-Cookie Transfer-Encoding Vary
|
18
|
-
Permissions-Policy Permissions-Policy-Report-Only'.
|
18
|
+
Permissions-Policy Permissions-Policy-Report-Only Strict-Transport-Security'.
|
19
19
|
each do |value|
|
20
20
|
value = value.downcase if downcase
|
21
21
|
const_set(value.gsub('-', '_').upcase!.to_sym, value.freeze)
|
data/lib/roda/version.rb
CHANGED
data/lib/roda.rb
CHANGED