roda 3.34.0 → 3.39.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/CHANGELOG +30 -0
- data/doc/release_notes/3.3.0.txt +1 -1
- data/doc/release_notes/3.35.0.txt +12 -0
- data/doc/release_notes/3.36.0.txt +17 -0
- data/doc/release_notes/3.37.0.txt +42 -0
- data/doc/release_notes/3.38.0.txt +5 -0
- data/doc/release_notes/3.39.0.txt +16 -0
- data/lib/roda.rb +12 -0
- data/lib/roda/plugins/content_security_policy.rb +3 -3
- data/lib/roda/plugins/custom_matchers.rb +87 -0
- data/lib/roda/plugins/default_headers.rb +1 -0
- data/lib/roda/plugins/error_email.rb +9 -2
- data/lib/roda/plugins/error_mail.rb +9 -2
- data/lib/roda/plugins/mail_processor.rb +2 -0
- data/lib/roda/plugins/multi_public.rb +87 -0
- data/lib/roda/plugins/public.rb +17 -15
- data/lib/roda/plugins/r.rb +35 -0
- data/lib/roda/plugins/relative_path.rb +4 -3
- data/lib/roda/plugins/render.rb +1 -1
- data/lib/roda/plugins/type_routing.rb +7 -1
- data/lib/roda/plugins/typecast_params.rb +4 -5
- data/lib/roda/version.rb +1 -1
- metadata +16 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f01105e24f4eae80a69f5734f93d3dfe54b5f6be2489521f1d886053c569102
|
|
4
|
+
data.tar.gz: 2aebf30460c7d5352ee15baf3f884e59dac972e0aadd63aa7ab81445a8a29f65
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3fd374b3d700106ca814fff8f2dd0c66a3295955f27a453ef5d9fb2ba7fdea583d76ce82f91efb8e2ebcbb97a07e664f3f008689d51eb67ae39aebdd86a5d0e
|
|
7
|
+
data.tar.gz: 59c34c88ab1e5a5bc5017d46707a530c7cd002f4262c30751ba6429983b32477b813d83ada45a19873be711d1d2bd0abd95fe2a5c44c01038ad4a32dbb8f276d
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
= 3.39.0 (2020-12-15)
|
|
2
|
+
|
|
3
|
+
* Speed up relative_path plugin if relative_path or relative_prefix is called more than once (jeremyevans)
|
|
4
|
+
|
|
5
|
+
* Avoid method redefinition warnings in verbose warning mode (jeremyevans)
|
|
6
|
+
|
|
7
|
+
* Make typecast_params.convert! handle explicit nil values the same as missing values (jeremyevans)
|
|
8
|
+
|
|
9
|
+
= 3.38.0 (2020-11-16)
|
|
10
|
+
|
|
11
|
+
* Make error_email and error_mail plugins rescue invalid parameter errors when preparing the email body (jeremyevans)
|
|
12
|
+
|
|
13
|
+
= 3.37.0 (2020-10-16)
|
|
14
|
+
|
|
15
|
+
* Add custom_matchers plugin, for supporting arbitrary objects as matchers (jeremyevans)
|
|
16
|
+
|
|
17
|
+
= 3.36.0 (2020-09-14)
|
|
18
|
+
|
|
19
|
+
* Add multi_public plugin, for serving files from multiple public directories (jeremyevans)
|
|
20
|
+
|
|
21
|
+
* Support report-to directive in the content_security_policy plugin (jeremyevans)
|
|
22
|
+
|
|
23
|
+
* Add Vary response header when using type_routing plugin with Accept request header to prevent caching issues (jeremyevans)
|
|
24
|
+
|
|
25
|
+
= 3.35.0 (2020-08-14)
|
|
26
|
+
|
|
27
|
+
* Add r plugin for r method for accessing request, useful when r local variable is not in scope (jeremyevans)
|
|
28
|
+
|
|
29
|
+
* Warn when loading a plugin with arguments or a block if the plugin does not accept arguments or block (jeremyevans)
|
|
30
|
+
|
|
1
31
|
= 3.34.0 (2020-07-14)
|
|
2
32
|
|
|
3
33
|
* Remove unnecessary conditionals (jeremyevans)
|
data/doc/release_notes/3.3.0.txt
CHANGED
|
@@ -248,7 +248,7 @@
|
|
|
248
248
|
Note that if there are multiple conversion errors raised inside a
|
|
249
249
|
convert! or convert_each! block, they are recorded and a single
|
|
250
250
|
Roda::RodaPlugins::TypecastParams::Error instance is raised after
|
|
251
|
-
processing the block. TypecastParams::Error#
|
|
251
|
+
processing the block. TypecastParams::Error#param_names can be
|
|
252
252
|
called on the exception to get an array of all parameter names
|
|
253
253
|
with conversion issues, and TypecastParams::Error#all_errors
|
|
254
254
|
can be used to get an array of all Error instances.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* An r plugin has been added. This plugin adds an r method for the
|
|
4
|
+
request, useful for allowing the use of r.halt and r.redirect even
|
|
5
|
+
in methods where the r local variable is not in scope.
|
|
6
|
+
|
|
7
|
+
= Other Improvements
|
|
8
|
+
|
|
9
|
+
* Attempting to load a plugin with an argument or block when the plugin
|
|
10
|
+
does not accept arguments or a block now warns. This is because a
|
|
11
|
+
future update to support a block or an optional argument could break
|
|
12
|
+
the call.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A multi_public plugin has been added, which allows serving static
|
|
4
|
+
files from multiple separate directories. This is especially
|
|
5
|
+
useful when there are different access control requirements per
|
|
6
|
+
directory.
|
|
7
|
+
|
|
8
|
+
* The content_security_policy now supports a
|
|
9
|
+
content_security_policy.report_to method to set the
|
|
10
|
+
report-to directive.
|
|
11
|
+
|
|
12
|
+
= Other Improvements
|
|
13
|
+
|
|
14
|
+
* When using the type_routing plugin and performing type routing
|
|
15
|
+
using the Accept request header, the Vary response header will be
|
|
16
|
+
added or updated so that http caches do not cache a response for one
|
|
17
|
+
type and serve it for a different type.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A custom_matchers plugin has been added, which allows using
|
|
4
|
+
arbitrary objects as matchers, as long as the matcher has been
|
|
5
|
+
registered. You can register matchers using the custom_matcher
|
|
6
|
+
class method, which takes the class of the matcher, and a block
|
|
7
|
+
which is yielded the matcher object. The block should return
|
|
8
|
+
nil or false if the matcher doesn't match, and any other value
|
|
9
|
+
if the matcher does match. Example:
|
|
10
|
+
|
|
11
|
+
plugin :custom_matchers
|
|
12
|
+
method_segment = Struct.new(:request_method, :next_segment)
|
|
13
|
+
custom_matcher(method_segment) do |matcher|
|
|
14
|
+
# self is the request instance ("r" yielded in the route block below)
|
|
15
|
+
if matcher.request_method == self.request_method
|
|
16
|
+
match(matcher.next_segment)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
get_foo = method_segment.new('GET', 'foo')
|
|
21
|
+
post_any = method_segment.new('POST', String)
|
|
22
|
+
route do |r|
|
|
23
|
+
r.on('baz') do
|
|
24
|
+
r.on(get_foo) do
|
|
25
|
+
# GET method, /baz/foo prefix
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
r.is(post_any) do |seg|
|
|
29
|
+
# for POST /baz/bar, seg is "bar"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
r.on('quux') do
|
|
34
|
+
r.is(get_foo) do
|
|
35
|
+
# GET method, /quux/foo route
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
r.on(post_any) do |seg|
|
|
39
|
+
# for POST /quux/xyz, seg is "xyz"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
= Improvements
|
|
2
|
+
|
|
3
|
+
* The relative_path plugin is now faster if you are calling
|
|
4
|
+
relative_path or relative_prefix more than once when handling a
|
|
5
|
+
request.
|
|
6
|
+
|
|
7
|
+
* The typecast_params.convert! method in the typecast_params plugin
|
|
8
|
+
now handles explicit nil values the same as missing values.
|
|
9
|
+
Explicit nil values do not generally occur in normal Rack parameter
|
|
10
|
+
parsing, but they can occur when using the json_parser plugin to
|
|
11
|
+
parse JSON requests.
|
|
12
|
+
|
|
13
|
+
* Roda now avoids method redefinition warnings in verbose mode by
|
|
14
|
+
using a self alias. As Ruby 3 is dropping uninitialized instance
|
|
15
|
+
variable warnings, Roda will be verbose warning free if you are
|
|
16
|
+
using Ruby 3.
|
data/lib/roda.rb
CHANGED
|
@@ -114,6 +114,7 @@ class Roda
|
|
|
114
114
|
alias_method meth, temp_method
|
|
115
115
|
undef_method temp_method
|
|
116
116
|
private meth
|
|
117
|
+
alias_method meth, meth
|
|
117
118
|
meth = :"#{meth}_arity"
|
|
118
119
|
elsif required_args > 1
|
|
119
120
|
b = block
|
|
@@ -144,6 +145,7 @@ class Roda
|
|
|
144
145
|
|
|
145
146
|
define_method(meth, &block)
|
|
146
147
|
private meth
|
|
148
|
+
alias_method meth, meth
|
|
147
149
|
|
|
148
150
|
if arity_meth
|
|
149
151
|
required_args, optional_args, rest, keyword = _define_roda_method_arg_numbers(instance_method(meth))
|
|
@@ -167,6 +169,7 @@ class Roda
|
|
|
167
169
|
send(meth, *a)
|
|
168
170
|
end
|
|
169
171
|
private arity_meth
|
|
172
|
+
alias_method arity_meth, arity_meth
|
|
170
173
|
end
|
|
171
174
|
|
|
172
175
|
call_meth
|
|
@@ -199,6 +202,7 @@ class Roda
|
|
|
199
202
|
|
|
200
203
|
private
|
|
201
204
|
|
|
205
|
+
alias set_default_headers set_default_headers
|
|
202
206
|
def set_default_headers
|
|
203
207
|
@headers['Content-Type'] ||= 'text/html'
|
|
204
208
|
end
|
|
@@ -272,6 +276,12 @@ class Roda
|
|
|
272
276
|
raise RodaError, "Cannot add a plugin to a frozen Roda class" if frozen?
|
|
273
277
|
plugin = RodaPlugins.load_plugin(plugin) if plugin.is_a?(Symbol)
|
|
274
278
|
raise RodaError, "Invalid plugin type: #{plugin.class.inspect}" unless plugin.is_a?(Module)
|
|
279
|
+
|
|
280
|
+
if !plugin.respond_to?(:load_dependencies) && !plugin.respond_to?(:configure) && (!args.empty? || block)
|
|
281
|
+
# RODA4: switch from warning to error
|
|
282
|
+
RodaPlugins.warn("Plugin #{plugin} does not accept arguments or a block, but arguments or a block was passed when loading this. This will raise an error in Roda 4.")
|
|
283
|
+
end
|
|
284
|
+
|
|
275
285
|
plugin.load_dependencies(self, *args, &block) if plugin.respond_to?(:load_dependencies)
|
|
276
286
|
include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods)
|
|
277
287
|
extend(plugin::ClassMethods) if defined?(plugin::ClassMethods)
|
|
@@ -397,6 +407,7 @@ class Roda
|
|
|
397
407
|
class_eval("def _roda_before; #{meths.join(';')} end", __FILE__, __LINE__)
|
|
398
408
|
end
|
|
399
409
|
private :_roda_before
|
|
410
|
+
alias_method :_roda_before, :_roda_before
|
|
400
411
|
end
|
|
401
412
|
end
|
|
402
413
|
|
|
@@ -413,6 +424,7 @@ class Roda
|
|
|
413
424
|
class_eval("def _roda_after(res); #{meths.map{|s| "#{s}(res)"}.join(';')} end", __FILE__, __LINE__)
|
|
414
425
|
end
|
|
415
426
|
private :_roda_after
|
|
427
|
+
alias_method :_roda_after, :_roda_after
|
|
416
428
|
end
|
|
417
429
|
end
|
|
418
430
|
|
|
@@ -56,6 +56,7 @@ class Roda
|
|
|
56
56
|
# * media_src
|
|
57
57
|
# * object_src
|
|
58
58
|
# * plugin_types
|
|
59
|
+
# * report_to
|
|
59
60
|
# * report_uri
|
|
60
61
|
# * require_sri_for
|
|
61
62
|
# * sandbox
|
|
@@ -123,6 +124,7 @@ class Roda
|
|
|
123
124
|
media-src
|
|
124
125
|
object-src
|
|
125
126
|
plugin-types
|
|
127
|
+
report-to
|
|
126
128
|
report-uri
|
|
127
129
|
require-sri-for
|
|
128
130
|
sandbox
|
|
@@ -145,9 +147,7 @@ class Roda
|
|
|
145
147
|
# add_* method name adds to the setting value, or clears setting if no values
|
|
146
148
|
# are given.
|
|
147
149
|
define_method("add_#{meth}") do |*args|
|
|
148
|
-
|
|
149
|
-
@opts[setting]
|
|
150
|
-
else
|
|
150
|
+
unless args.empty?
|
|
151
151
|
@opts[setting] ||= EMPTY_ARRAY
|
|
152
152
|
@opts[setting] += args
|
|
153
153
|
@opts[setting].freeze
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The custom_matchers plugin supports using arbitrary objects
|
|
7
|
+
# as matchers, as long as the application has been configured
|
|
8
|
+
# to accept such objects.
|
|
9
|
+
#
|
|
10
|
+
# After loading the plugin, support for custom matchers can be
|
|
11
|
+
# configured using the +custom_matcher+ class method. This
|
|
12
|
+
# method is generally passed the class of the object you want
|
|
13
|
+
# to use as a custom matcher, as well as a block. The block
|
|
14
|
+
# will be called in the context of the request instance
|
|
15
|
+
# with the specific matcher used in the match method.
|
|
16
|
+
#
|
|
17
|
+
# Blocks can append to the captures in order to yield the appropriate
|
|
18
|
+
# values to match blocks, or call request methods that append to the
|
|
19
|
+
# captures.
|
|
20
|
+
#
|
|
21
|
+
# Example:
|
|
22
|
+
#
|
|
23
|
+
# plugin :custom_matchers
|
|
24
|
+
# method_segment = Struct.new(:request_method, :next_segment)
|
|
25
|
+
# custom_matcher(method_segment) do |matcher|
|
|
26
|
+
# # self is the request instance ("r" yielded in the route block below)
|
|
27
|
+
# if matcher.request_method == self.request_method
|
|
28
|
+
# match(matcher.next_segment)
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# get_foo = method_segment.new('GET', 'foo')
|
|
33
|
+
# post_any = method_segment.new('POST', String)
|
|
34
|
+
# route do |r|
|
|
35
|
+
# r.on('baz') do
|
|
36
|
+
# r.on(get_foo) do
|
|
37
|
+
# # GET method, /baz/foo prefix
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# r.is(post_any) do |seg|
|
|
41
|
+
# # for POST /baz/bar, seg is "bar"
|
|
42
|
+
# end
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# r.on('quux') do
|
|
46
|
+
# r.is(get_foo) do
|
|
47
|
+
# # GET method, /quux/foo route
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# r.on(post_any) do |seg|
|
|
51
|
+
# # for POST /quux/xyz, seg is "xyz"
|
|
52
|
+
# end
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
module CustomMatchers
|
|
56
|
+
def self.configure(app)
|
|
57
|
+
app.opts[:custom_matchers] ||= OPTS
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
module ClassMethods
|
|
61
|
+
def custom_matcher(match_class, &block)
|
|
62
|
+
custom_matchers = Hash[opts[:custom_matchers]]
|
|
63
|
+
meth = custom_matchers[match_class] = custom_matchers[match_class] || :"_custom_matcher_#{match_class}"
|
|
64
|
+
opts[:custom_matchers] = custom_matchers.freeze
|
|
65
|
+
self::RodaRequest.send(:define_method, meth, &block)
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
module RequestMethods
|
|
71
|
+
# Try custom matchers before calling super
|
|
72
|
+
def unsupported_matcher(matcher)
|
|
73
|
+
roda_class.opts[:custom_matchers].each do |match_class, meth|
|
|
74
|
+
if match_class === matcher
|
|
75
|
+
return send(meth, matcher)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
super
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
register_plugin(:custom_matchers, CustomMatchers)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
@@ -54,6 +54,13 @@ class Roda
|
|
|
54
54
|
:body=>lambda do |s, e|
|
|
55
55
|
format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
|
|
56
56
|
|
|
57
|
+
begin
|
|
58
|
+
params = s.request.params
|
|
59
|
+
params = (format[params] unless params.empty?)
|
|
60
|
+
rescue
|
|
61
|
+
params = 'Invalid Parameters!'
|
|
62
|
+
end
|
|
63
|
+
|
|
57
64
|
message = String.new
|
|
58
65
|
message << <<END
|
|
59
66
|
Path: #{s.request.path}
|
|
@@ -73,12 +80,12 @@ ENV:
|
|
|
73
80
|
#{format[s.env]}
|
|
74
81
|
END
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
if params
|
|
77
84
|
message << <<END
|
|
78
85
|
|
|
79
86
|
Params:
|
|
80
87
|
|
|
81
|
-
#{
|
|
88
|
+
#{params}
|
|
82
89
|
END
|
|
83
90
|
end
|
|
84
91
|
|
|
@@ -71,6 +71,13 @@ class Roda
|
|
|
71
71
|
|
|
72
72
|
format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
|
|
73
73
|
|
|
74
|
+
begin
|
|
75
|
+
params = request.params
|
|
76
|
+
params = (format[params] unless params.empty?)
|
|
77
|
+
rescue
|
|
78
|
+
params = 'Invalid Parameters!'
|
|
79
|
+
end
|
|
80
|
+
|
|
74
81
|
message = String.new
|
|
75
82
|
message << <<END
|
|
76
83
|
Path: #{request.path}
|
|
@@ -91,12 +98,12 @@ ENV:
|
|
|
91
98
|
#{format[env]}
|
|
92
99
|
END
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
if params
|
|
95
102
|
message << <<END
|
|
96
103
|
|
|
97
104
|
Params:
|
|
98
105
|
|
|
99
|
-
#{
|
|
106
|
+
#{params}
|
|
100
107
|
END
|
|
101
108
|
end
|
|
102
109
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The multi_public plugin adds an +r.multi_public+ method that accepts an argument specifying
|
|
7
|
+
# a directory from which to serve static files. It is similar to the public plugin, but
|
|
8
|
+
# allows for multiple separate directories.
|
|
9
|
+
#
|
|
10
|
+
# Here's an example of using the multi_public plugin to serve 3 different types of files
|
|
11
|
+
# from 3 different directories:
|
|
12
|
+
#
|
|
13
|
+
# plugin :multi_public,
|
|
14
|
+
# img: 'static/images',
|
|
15
|
+
# font: 'assets/fonts',
|
|
16
|
+
# form: 'static/forms/pdfs'
|
|
17
|
+
#
|
|
18
|
+
# r.route do
|
|
19
|
+
# r.on "images" do
|
|
20
|
+
# r.multi_public(:img)
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# r.on "fonts" do
|
|
24
|
+
# r.multi_public(:font)
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# r.on "forms" do
|
|
28
|
+
# r.multi_public(:form)
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# It is possible to simplify the routing tree for this using string keys and an array
|
|
33
|
+
# matcher:
|
|
34
|
+
#
|
|
35
|
+
# plugin :multi_public,
|
|
36
|
+
# 'images' => 'static/images',
|
|
37
|
+
# 'fonts' => 'assets/fonts',
|
|
38
|
+
# 'forms' => 'static/forms/pdfs'
|
|
39
|
+
#
|
|
40
|
+
# r.route do
|
|
41
|
+
# r.on %w"images fonts forms" do |dir|
|
|
42
|
+
# r.multi_public(dir)
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# You can provide custom headers and default mime type for each directory using an array
|
|
47
|
+
# of three elements as the value, with the first element being the path, the second
|
|
48
|
+
# being the custom headers, and the third being the default mime type:
|
|
49
|
+
#
|
|
50
|
+
# plugin :multi_public,
|
|
51
|
+
# 'images' => ['static/images', {'Cache-Control'=>'max-age=86400'}, nil],
|
|
52
|
+
# 'fonts' => ['assets/fonts', {'Cache-Control'=>'max-age=31536000'}, 'font/ttf'],
|
|
53
|
+
# 'forms' => ['static/forms/pdfs', nil, 'application/pdf']
|
|
54
|
+
#
|
|
55
|
+
# r.route do
|
|
56
|
+
# r.on %w"images fonts forms" do |dir|
|
|
57
|
+
# r.multi_public(dir)
|
|
58
|
+
# end
|
|
59
|
+
# end
|
|
60
|
+
module MultiPublic
|
|
61
|
+
def self.load_dependencies(app, _, opts=OPTS)
|
|
62
|
+
app.plugin(:public, opts)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Use the given directories to setup servers. Any opts are passed to the public plugin.
|
|
66
|
+
def self.configure(app, directories, _=OPTS)
|
|
67
|
+
roots = app.opts[:multi_public_servers] = (app.opts[:multi_public_servers] || {}).dup
|
|
68
|
+
directories.each do |key, path|
|
|
69
|
+
path, headers, mime = path
|
|
70
|
+
roots[key] = ::Rack::File.new(app.expand_path(path), headers||{}, mime||'text/plain')
|
|
71
|
+
end
|
|
72
|
+
roots.freeze
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
module RequestMethods
|
|
76
|
+
# Serve files from the directory corresponding to the given key if the file exists and
|
|
77
|
+
# this is a GET request.
|
|
78
|
+
def multi_public(key)
|
|
79
|
+
public_serve_with(roda_class.opts[:multi_public_servers].fetch(key))
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
register_plugin(:multi_public, MultiPublic)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
data/lib/roda/plugins/public.rb
CHANGED
|
@@ -60,21 +60,7 @@ class Roda
|
|
|
60
60
|
module RequestMethods
|
|
61
61
|
# Serve files from the public directory if the file exists and this is a GET request.
|
|
62
62
|
def public
|
|
63
|
-
|
|
64
|
-
path = PARSER.unescape(real_remaining_path)
|
|
65
|
-
return if path.include?("\0")
|
|
66
|
-
|
|
67
|
-
roda_opts = roda_class.opts
|
|
68
|
-
server = roda_opts[:public_server]
|
|
69
|
-
path = ::File.join(server.root, *public_path_segments(path))
|
|
70
|
-
|
|
71
|
-
public_serve_compressed(server, path, '.br', 'br') if roda_opts[:public_brotli]
|
|
72
|
-
public_serve_compressed(server, path, '.gz', 'gzip') if roda_opts[:public_gzip]
|
|
73
|
-
|
|
74
|
-
if public_file_readable?(path)
|
|
75
|
-
halt public_serve(server, path)
|
|
76
|
-
end
|
|
77
|
-
end
|
|
63
|
+
public_serve_with(roda_class.opts[:public_server])
|
|
78
64
|
end
|
|
79
65
|
|
|
80
66
|
private
|
|
@@ -101,6 +87,22 @@ class Roda
|
|
|
101
87
|
# :nocov:
|
|
102
88
|
end
|
|
103
89
|
|
|
90
|
+
def public_serve_with(server)
|
|
91
|
+
return unless is_get?
|
|
92
|
+
path = PARSER.unescape(real_remaining_path)
|
|
93
|
+
return if path.include?("\0")
|
|
94
|
+
|
|
95
|
+
roda_opts = roda_class.opts
|
|
96
|
+
path = ::File.join(server.root, *public_path_segments(path))
|
|
97
|
+
|
|
98
|
+
public_serve_compressed(server, path, '.br', 'br') if roda_opts[:public_brotli]
|
|
99
|
+
public_serve_compressed(server, path, '.gz', 'gzip') if roda_opts[:public_gzip]
|
|
100
|
+
|
|
101
|
+
if public_file_readable?(path)
|
|
102
|
+
halt public_serve(server, path)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
104
106
|
def public_serve_compressed(server, path, suffix, encoding)
|
|
105
107
|
if env['HTTP_ACCEPT_ENCODING'] =~ /\b#{encoding}\b/
|
|
106
108
|
compressed_path = path + suffix
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The r plugin adds an +r+ instance method that will return the request.
|
|
7
|
+
# This allows you to use common Roda idioms such as +r.halt+ and
|
|
8
|
+
# +r.redirect+ even when +r+ isn't a local variable in scope. Example:
|
|
9
|
+
#
|
|
10
|
+
# plugin :r
|
|
11
|
+
#
|
|
12
|
+
# def foo
|
|
13
|
+
# r.redirect "/bar"
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# route do |r|
|
|
17
|
+
# r.get "foo" do
|
|
18
|
+
# foo
|
|
19
|
+
# end
|
|
20
|
+
# r.get "bar" do
|
|
21
|
+
# "bar"
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
module R
|
|
25
|
+
module InstanceMethods
|
|
26
|
+
# The request object.
|
|
27
|
+
def r
|
|
28
|
+
@_request
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
register_plugin(:r, R)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -41,6 +41,7 @@ class Roda
|
|
|
41
41
|
# Return a relative prefix to append to an absolute path to a relative path
|
|
42
42
|
# based on the current path of the request.
|
|
43
43
|
def relative_prefix
|
|
44
|
+
return @_relative_prefix if @_relative_prefix
|
|
44
45
|
env = @_request.env
|
|
45
46
|
script_name = env["SCRIPT_NAME"]
|
|
46
47
|
path_info = env["PATH_INFO"]
|
|
@@ -50,16 +51,16 @@ class Roda
|
|
|
50
51
|
case script_name.getbyte(0)
|
|
51
52
|
when nil # SCRIPT_NAME empty
|
|
52
53
|
unless path_info.getbyte(0) == 47 # PATH_INFO starts with /
|
|
53
|
-
return ''
|
|
54
|
+
return(@_relative_prefix = '')
|
|
54
55
|
end
|
|
55
56
|
when 47 # SCRIPT_NAME starts with /
|
|
56
57
|
# nothing
|
|
57
58
|
else
|
|
58
|
-
return ''
|
|
59
|
+
return(@_relative_prefix = '')
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
slash_count = script_name.count('/') + path_info.count('/')
|
|
62
|
-
if slash_count > 1
|
|
63
|
+
@_relative_prefix = if slash_count > 1
|
|
63
64
|
("../" * (slash_count - 2)) << ".."
|
|
64
65
|
else
|
|
65
66
|
"."
|
data/lib/roda/plugins/render.rb
CHANGED
|
@@ -106,7 +106,7 @@ class Roda
|
|
|
106
106
|
# :template_block :: Pass this block when creating the underlying template,
|
|
107
107
|
# ignored when using :inline. Disables caching of the
|
|
108
108
|
# template by default.
|
|
109
|
-
# :template_class :: Provides the template class to use,
|
|
109
|
+
# :template_class :: Provides the template class to use, instead of using
|
|
110
110
|
# Tilt or <tt>Tilt[:engine]</tt>.
|
|
111
111
|
#
|
|
112
112
|
# Here's an example of using these options:
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
class Roda
|
|
5
5
|
module RodaPlugins
|
|
6
6
|
# This plugin makes it easier to to respond to specific request data types. User agents can request
|
|
7
|
-
# specific data types by either supplying an appropriate +Accept+ header
|
|
7
|
+
# specific data types by either supplying an appropriate +Accept+ request header
|
|
8
8
|
# or by appending it as file extension to the path.
|
|
9
9
|
#
|
|
10
10
|
# Example:
|
|
@@ -50,6 +50,10 @@ class Roda
|
|
|
50
50
|
# Content-Type header will be set to Roda's default (which you can override via
|
|
51
51
|
# the default_headers plugin).
|
|
52
52
|
#
|
|
53
|
+
# If the type routing is based on the +Accept+ request header and not the file extension,
|
|
54
|
+
# then an appropriate +Vary+ header will be set or appended to, so that HTTP caches do
|
|
55
|
+
# not serve the same result for requests with different +Accept+ headers.
|
|
56
|
+
#
|
|
53
57
|
# To match custom extensions, use the :types option:
|
|
54
58
|
#
|
|
55
59
|
# plugin :type_routing, types: {
|
|
@@ -137,6 +141,7 @@ class Roda
|
|
|
137
141
|
app::RodaRequest.send(:define_method, type) do |&block|
|
|
138
142
|
on_type(type, &block)
|
|
139
143
|
end
|
|
144
|
+
app::RodaRequest.send(:alias_method, type, type)
|
|
140
145
|
end
|
|
141
146
|
|
|
142
147
|
app.opts[:type_routing] = config.freeze
|
|
@@ -195,6 +200,7 @@ class Roda
|
|
|
195
200
|
@env['HTTP_ACCEPT'].to_s.split(/\s*,\s*/).map do |part|
|
|
196
201
|
mime, _= part.split(/\s*;\s*/, 2)
|
|
197
202
|
if sym = mimes[mime]
|
|
203
|
+
response['Vary'] = (vary = response['Vary']) ? "#{vary}, Accept" : 'Accept'
|
|
198
204
|
return sym
|
|
199
205
|
end
|
|
200
206
|
end
|
|
@@ -229,7 +229,7 @@ class Roda
|
|
|
229
229
|
#
|
|
230
230
|
# Note that if there are multiple conversion Error raised inside a +convert!+ or +convert_each!+
|
|
231
231
|
# block, they are recorded and a single TypecastParams::Error instance is raised after
|
|
232
|
-
# processing the block. TypecastParams::Error#
|
|
232
|
+
# processing the block. TypecastParams::Error#param_names can be called on the exception to
|
|
233
233
|
# get an array of all parameter names with conversion issues, and TypecastParams::Error#all_errors
|
|
234
234
|
# can be used to get an array of all Error instances.
|
|
235
235
|
#
|
|
@@ -609,10 +609,9 @@ class Roda
|
|
|
609
609
|
when nil
|
|
610
610
|
keys = (0...@obj.length)
|
|
611
611
|
|
|
612
|
-
valid =
|
|
613
|
-
when Array
|
|
612
|
+
valid = if @obj.is_a?(Array)
|
|
614
613
|
true
|
|
615
|
-
|
|
614
|
+
else
|
|
616
615
|
keys = keys.map(&:to_s)
|
|
617
616
|
keys.all?{|k| @obj.has_key?(k)}
|
|
618
617
|
end
|
|
@@ -725,7 +724,7 @@ class Roda
|
|
|
725
724
|
raise Error, "parameter #{param_name(nil)} is not a hash" if do_raise
|
|
726
725
|
return
|
|
727
726
|
end
|
|
728
|
-
present =
|
|
727
|
+
present = !@obj[key].nil?
|
|
729
728
|
when Integer
|
|
730
729
|
unless @obj.is_a?(Array)
|
|
731
730
|
raise Error, "parameter #{param_name(nil)} is not an array" if do_raise
|
data/lib/roda/version.rb
CHANGED
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.
|
|
4
|
+
version: 3.39.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: 2020-
|
|
11
|
+
date: 2020-12-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -210,6 +210,11 @@ extra_rdoc_files:
|
|
|
210
210
|
- doc/release_notes/3.32.0.txt
|
|
211
211
|
- doc/release_notes/3.33.0.txt
|
|
212
212
|
- doc/release_notes/3.34.0.txt
|
|
213
|
+
- doc/release_notes/3.35.0.txt
|
|
214
|
+
- doc/release_notes/3.36.0.txt
|
|
215
|
+
- doc/release_notes/3.37.0.txt
|
|
216
|
+
- doc/release_notes/3.38.0.txt
|
|
217
|
+
- doc/release_notes/3.39.0.txt
|
|
213
218
|
files:
|
|
214
219
|
- CHANGELOG
|
|
215
220
|
- MIT-LICENSE
|
|
@@ -245,6 +250,11 @@ files:
|
|
|
245
250
|
- doc/release_notes/3.32.0.txt
|
|
246
251
|
- doc/release_notes/3.33.0.txt
|
|
247
252
|
- doc/release_notes/3.34.0.txt
|
|
253
|
+
- doc/release_notes/3.35.0.txt
|
|
254
|
+
- doc/release_notes/3.36.0.txt
|
|
255
|
+
- doc/release_notes/3.37.0.txt
|
|
256
|
+
- doc/release_notes/3.38.0.txt
|
|
257
|
+
- doc/release_notes/3.39.0.txt
|
|
248
258
|
- doc/release_notes/3.4.0.txt
|
|
249
259
|
- doc/release_notes/3.5.0.txt
|
|
250
260
|
- doc/release_notes/3.6.0.txt
|
|
@@ -271,6 +281,7 @@ files:
|
|
|
271
281
|
- lib/roda/plugins/content_security_policy.rb
|
|
272
282
|
- lib/roda/plugins/cookies.rb
|
|
273
283
|
- lib/roda/plugins/csrf.rb
|
|
284
|
+
- lib/roda/plugins/custom_matchers.rb
|
|
274
285
|
- lib/roda/plugins/default_headers.rb
|
|
275
286
|
- lib/roda/plugins/default_status.rb
|
|
276
287
|
- lib/roda/plugins/delay_build.rb
|
|
@@ -305,6 +316,7 @@ files:
|
|
|
305
316
|
- lib/roda/plugins/middleware.rb
|
|
306
317
|
- lib/roda/plugins/middleware_stack.rb
|
|
307
318
|
- lib/roda/plugins/module_include.rb
|
|
319
|
+
- lib/roda/plugins/multi_public.rb
|
|
308
320
|
- lib/roda/plugins/multi_route.rb
|
|
309
321
|
- lib/roda/plugins/multi_run.rb
|
|
310
322
|
- lib/roda/plugins/multi_view.rb
|
|
@@ -324,6 +336,7 @@ files:
|
|
|
324
336
|
- lib/roda/plugins/placeholder_string_matchers.rb
|
|
325
337
|
- lib/roda/plugins/precompile_templates.rb
|
|
326
338
|
- lib/roda/plugins/public.rb
|
|
339
|
+
- lib/roda/plugins/r.rb
|
|
327
340
|
- lib/roda/plugins/relative_path.rb
|
|
328
341
|
- lib/roda/plugins/render.rb
|
|
329
342
|
- lib/roda/plugins/render_each.rb
|
|
@@ -381,7 +394,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
381
394
|
- !ruby/object:Gem::Version
|
|
382
395
|
version: '0'
|
|
383
396
|
requirements: []
|
|
384
|
-
rubygems_version: 3.1.
|
|
397
|
+
rubygems_version: 3.1.4
|
|
385
398
|
signing_key:
|
|
386
399
|
specification_version: 4
|
|
387
400
|
summary: Routing tree web toolkit
|