roda 3.107.0 → 3.109.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/opts.rb +22 -0
- data/lib/roda/plugins/Integer_matcher_max.rb +1 -1
- data/lib/roda/plugins/_add_script_name.rb +14 -0
- data/lib/roda/plugins/_base64.rb +2 -2
- data/lib/roda/plugins/_json.rb +22 -0
- data/lib/roda/plugins/_optimized_matching.rb +1 -0
- data/lib/roda/plugins/_symbol_class_matchers.rb +6 -6
- data/lib/roda/plugins/additional_render_engines.rb +5 -1
- data/lib/roda/plugins/additional_view_directories.rb +5 -1
- data/lib/roda/plugins/all_verbs.rb +2 -2
- data/lib/roda/plugins/assets.rb +11 -14
- data/lib/roda/plugins/autoload_hash_branches.rb +9 -3
- data/lib/roda/plugins/autoload_named_routes.rb +9 -3
- data/lib/roda/plugins/bearer_token.rb +2 -2
- data/lib/roda/plugins/break.rb +9 -1
- data/lib/roda/plugins/caching.rb +12 -12
- data/lib/roda/plugins/capture_erb.rb +5 -1
- data/lib/roda/plugins/chunked.rb +11 -5
- data/lib/roda/plugins/class_level_routing.rb +5 -3
- data/lib/roda/plugins/class_matchers.rb +3 -1
- data/lib/roda/plugins/common_logger.rb +11 -7
- data/lib/roda/plugins/conditional_sessions.rb +1 -1
- data/lib/roda/plugins/content_for.rb +5 -1
- data/lib/roda/plugins/content_security_policy.rb +6 -2
- data/lib/roda/plugins/cookie_flags.rb +8 -4
- data/lib/roda/plugins/cookies.rb +7 -3
- data/lib/roda/plugins/custom_block_results.rb +5 -3
- data/lib/roda/plugins/custom_matchers.rb +4 -2
- data/lib/roda/plugins/default_headers.rb +2 -5
- data/lib/roda/plugins/default_status.rb +1 -0
- data/lib/roda/plugins/each_part.rb +23 -27
- data/lib/roda/plugins/environments.rb +3 -7
- data/lib/roda/plugins/error_email.rb +11 -7
- data/lib/roda/plugins/error_handler.rb +5 -3
- data/lib/roda/plugins/error_mail.rb +6 -2
- data/lib/roda/plugins/exception_page.rb +2 -2
- data/lib/roda/plugins/h.rb +4 -4
- data/lib/roda/plugins/hash_branch_view_subdir.rb +6 -4
- data/lib/roda/plugins/hash_branches.rb +15 -9
- data/lib/roda/plugins/hash_paths.rb +11 -7
- data/lib/roda/plugins/hash_public.rb +13 -9
- data/lib/roda/plugins/hash_public_cache.rb +9 -7
- data/lib/roda/plugins/hash_routes.rb +5 -3
- data/lib/roda/plugins/heartbeat.rb +6 -2
- data/lib/roda/plugins/hmac_paths.rb +36 -32
- data/lib/roda/plugins/hooks.rb +13 -10
- data/lib/roda/plugins/host_authorization.rb +7 -2
- data/lib/roda/plugins/host_routing.rb +36 -6
- data/lib/roda/plugins/invalid_request_body.rb +22 -4
- data/lib/roda/plugins/ip_from_header.rb +5 -1
- data/lib/roda/plugins/json.rb +10 -8
- data/lib/roda/plugins/json_parser.rb +43 -11
- data/lib/roda/plugins/mail_processor.rb +11 -8
- data/lib/roda/plugins/mailer.rb +9 -7
- data/lib/roda/plugins/match_affix.rb +6 -1
- data/lib/roda/plugins/match_hook_args.rb +7 -5
- data/lib/roda/plugins/middleware.rb +13 -7
- data/lib/roda/plugins/module_include.rb +1 -1
- data/lib/roda/plugins/multi_public.rb +6 -2
- data/lib/roda/plugins/multi_route.rb +1 -1
- data/lib/roda/plugins/multi_run.rb +13 -17
- data/lib/roda/plugins/named_routes.rb +8 -6
- data/lib/roda/plugins/named_templates.rb +5 -3
- data/lib/roda/plugins/optimized_segment_matchers.rb +13 -6
- data/lib/roda/plugins/optimized_string_matchers.rb +6 -3
- data/lib/roda/plugins/params_capturing.rb +16 -7
- data/lib/roda/plugins/params_capturing_restore.rb +72 -0
- data/lib/roda/plugins/part.rb +4 -4
- data/lib/roda/plugins/pass.rb +4 -1
- data/lib/roda/plugins/path.rb +22 -20
- data/lib/roda/plugins/path_matchers.rb +5 -1
- data/lib/roda/plugins/path_rewriter.rb +8 -5
- data/lib/roda/plugins/permissions_policy.rb +7 -3
- data/lib/roda/plugins/public.rb +15 -14
- data/lib/roda/plugins/recheck_precompiled_assets.rb +1 -1
- data/lib/roda/plugins/redirect_http_to_https.rb +6 -3
- data/lib/roda/plugins/render.rb +58 -61
- data/lib/roda/plugins/render_coverage.rb +12 -8
- data/lib/roda/plugins/render_each.rb +2 -2
- data/lib/roda/plugins/render_locals.rb +12 -6
- data/lib/roda/plugins/request_aref.rb +1 -1
- data/lib/roda/plugins/response_content_type.rb +7 -3
- data/lib/roda/plugins/route_csrf.rb +42 -5
- data/lib/roda/plugins/run_append_slash.rb +12 -4
- data/lib/roda/plugins/sec_fetch_site_csrf.rb +11 -4
- data/lib/roda/plugins/sessions.rb +17 -13
- data/lib/roda/plugins/shape_friendly.rb +7 -5
- data/lib/roda/plugins/sinatra_helpers.rb +7 -3
- data/lib/roda/plugins/static.rb +3 -0
- data/lib/roda/plugins/status_handler.rb +5 -3
- data/lib/roda/plugins/strip_path_prefix.rb +4 -2
- data/lib/roda/plugins/symbol_matchers.rb +3 -1
- data/lib/roda/plugins/symbol_views.rb +1 -1
- data/lib/roda/plugins/timestamp_public.rb +8 -4
- data/lib/roda/plugins/type_routing.rb +10 -6
- data/lib/roda/plugins/typecast_params.rb +2 -2
- data/lib/roda/plugins/typecast_params_sized_integers.rb +1 -1
- data/lib/roda/plugins/url_escape.rb +2 -14
- data/lib/roda/plugins/view_options.rb +3 -5
- data/lib/roda/plugins.rb +14 -4
- data/lib/roda/request.rb +25 -9
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +32 -21
- metadata +7 -3
data/lib/roda/plugins/path.rb
CHANGED
|
@@ -80,11 +80,15 @@ class Roda
|
|
|
80
80
|
# Regexp for valid constant names, to prevent code execution.
|
|
81
81
|
VALID_CONSTANT_NAME_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.freeze
|
|
82
82
|
|
|
83
|
+
def self.load_dependencies(app, opts = OPTS)
|
|
84
|
+
app.plugin :_add_script_name
|
|
85
|
+
end
|
|
86
|
+
|
|
83
87
|
# Initialize the path classes when loading the plugin. Options:
|
|
84
88
|
# :by_name :: Register classes by name, which is friendlier when reloading code (defaults to
|
|
85
89
|
# true in development mode)
|
|
86
90
|
def self.configure(app, opts=OPTS)
|
|
87
|
-
app.
|
|
91
|
+
app.instance_exec do
|
|
88
92
|
self.opts[:path_class_by_name] = opts.fetch(:by_name, ENV['RACK_ENV'] == 'development')
|
|
89
93
|
self.opts[:path_classes] ||= {}
|
|
90
94
|
self.opts[:path_class_methods] ||= {}
|
|
@@ -95,15 +99,14 @@ class Roda
|
|
|
95
99
|
end
|
|
96
100
|
|
|
97
101
|
module ClassMethods
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
end
|
|
102
|
+
RodaPlugins.opt_attr_reader(self, :path_classes)
|
|
103
|
+
RodaPlugins.opt_attr_reader(self, :path_class_by_name, name: :path_class_by_name?)
|
|
104
|
+
RodaPlugins.opt_attr_reader(self, :path_class_methods)
|
|
102
105
|
|
|
103
106
|
# Freeze the path classes when freezing the app.
|
|
104
107
|
def freeze
|
|
105
108
|
path_classes.freeze
|
|
106
|
-
|
|
109
|
+
path_class_methods.freeze
|
|
107
110
|
super
|
|
108
111
|
end
|
|
109
112
|
|
|
@@ -113,7 +116,7 @@ class Roda
|
|
|
113
116
|
raise RodaError, "can't provide path when calling path with a class" if path && !class_name
|
|
114
117
|
raise RodaError, "can't provide options when calling path with a class" unless opts.empty?
|
|
115
118
|
raise RodaError, "must provide a block when calling path with a class" unless block
|
|
116
|
-
if
|
|
119
|
+
if path_class_by_name?
|
|
117
120
|
if class_name
|
|
118
121
|
name = name.to_s
|
|
119
122
|
else
|
|
@@ -125,7 +128,7 @@ class Roda
|
|
|
125
128
|
name = Object.class_eval(name, __FILE__, __LINE__)
|
|
126
129
|
end
|
|
127
130
|
path_classes[name] = block
|
|
128
|
-
|
|
131
|
+
path_class_methods[name] = define_roda_method("path_#{name}", :any, &block)
|
|
129
132
|
return
|
|
130
133
|
end
|
|
131
134
|
|
|
@@ -147,7 +150,7 @@ class Roda
|
|
|
147
150
|
url = opts[:url]
|
|
148
151
|
url_only = opts[:url_only]
|
|
149
152
|
relative = opts[:relative]
|
|
150
|
-
add_script_name = opts.fetch(:add_script_name
|
|
153
|
+
add_script_name = opts.fetch(:add_script_name){add_script_name?}
|
|
151
154
|
|
|
152
155
|
if relative
|
|
153
156
|
if (url || url_only)
|
|
@@ -169,17 +172,17 @@ class Roda
|
|
|
169
172
|
# Allow calling private _method to get path
|
|
170
173
|
relative_path(request.script_name.to_s + send(_meth, *a, &blk))
|
|
171
174
|
end
|
|
172
|
-
# :
|
|
175
|
+
# simplecov:disable
|
|
173
176
|
ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
|
|
174
|
-
# :
|
|
177
|
+
# simplecov:enable
|
|
175
178
|
elsif add_script_name
|
|
176
179
|
define_method(meth) do |*a, &blk|
|
|
177
180
|
# Allow calling private _method to get path
|
|
178
181
|
request.script_name.to_s + send(_meth, *a, &blk)
|
|
179
182
|
end
|
|
180
|
-
# :
|
|
183
|
+
# simplecov:disable
|
|
181
184
|
ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
|
|
182
|
-
# :
|
|
185
|
+
# simplecov:enable
|
|
183
186
|
else
|
|
184
187
|
define_method(meth, &block)
|
|
185
188
|
end
|
|
@@ -198,9 +201,9 @@ class Roda
|
|
|
198
201
|
end
|
|
199
202
|
|
|
200
203
|
define_method(url_meth, &url_block)
|
|
201
|
-
# :
|
|
204
|
+
# simplecov:disable
|
|
202
205
|
ruby2_keywords(url_meth) if respond_to?(:ruby2_keywords, true)
|
|
203
|
-
# :
|
|
206
|
+
# simplecov:enable
|
|
204
207
|
end
|
|
205
208
|
|
|
206
209
|
nil
|
|
@@ -209,7 +212,7 @@ class Roda
|
|
|
209
212
|
# Return the block related to the given class, or nil if there is no block.
|
|
210
213
|
def path_block(klass)
|
|
211
214
|
# RODA4: Remove
|
|
212
|
-
if
|
|
215
|
+
if path_class_by_name?
|
|
213
216
|
klass = klass.name
|
|
214
217
|
end
|
|
215
218
|
path_classes[klass]
|
|
@@ -222,14 +225,13 @@ class Roda
|
|
|
222
225
|
# :add_script_name option is true, this prepends the SCRIPT_NAME to the path.
|
|
223
226
|
def path(obj, *args, &block)
|
|
224
227
|
app = self.class
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
unless meth = opts[:path_class_methods][klass]
|
|
228
|
+
klass = app.path_class_by_name? ? obj.class.name : obj.class
|
|
229
|
+
unless meth = app.path_class_methods[klass]
|
|
228
230
|
raise RodaError, "unrecognized object given to Roda#path: #{obj.inspect}"
|
|
229
231
|
end
|
|
230
232
|
|
|
231
233
|
path = send(meth, obj, *args, &block)
|
|
232
|
-
path = request.script_name.to_s + path if
|
|
234
|
+
path = request.script_name.to_s + path if app.add_script_name?
|
|
233
235
|
path
|
|
234
236
|
end
|
|
235
237
|
|
|
@@ -29,12 +29,16 @@ class Roda
|
|
|
29
29
|
# # Matches '/foo.bar', yielding 'foo'
|
|
30
30
|
# # Does not match bar.foo
|
|
31
31
|
# end
|
|
32
|
+
#
|
|
33
|
+
# Note that all of these matchers treat a given string as regexp source,
|
|
34
|
+
# so if you are including regexp metacharacters but want a literal string
|
|
35
|
+
# match, use +Regexp.escape+.
|
|
32
36
|
module PathMatchers
|
|
33
37
|
module RequestMethods
|
|
34
38
|
# Match when the current segment ends with the given extension.
|
|
35
39
|
# request path end with the extension.
|
|
36
40
|
def match_extension(ext)
|
|
37
|
-
match_suffix("
|
|
41
|
+
match_suffix("\\.#{ext}")
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
# Match when the current path segment starts with the given prefix.
|
|
@@ -53,10 +53,13 @@ class Roda
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
module ClassMethods
|
|
56
|
+
RodaPlugins.opt_attr_reader(self, :remaining_path_rewrites)
|
|
57
|
+
RodaPlugins.opt_attr_reader(self, :path_info_rewrites)
|
|
58
|
+
|
|
56
59
|
# Freeze the path rewrite metadata.
|
|
57
60
|
def freeze
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
remaining_path_rewrites.freeze
|
|
62
|
+
path_info_rewrites.freeze
|
|
60
63
|
super
|
|
61
64
|
end
|
|
62
65
|
|
|
@@ -75,7 +78,7 @@ class Roda
|
|
|
75
78
|
end
|
|
76
79
|
|
|
77
80
|
was = /\A#{Regexp.escape(was)}/ unless was.is_a?(Regexp)
|
|
78
|
-
array =
|
|
81
|
+
array = opts[:path_info] ? path_info_rewrites : remaining_path_rewrites
|
|
79
82
|
array << [was, is.dup.freeze].freeze
|
|
80
83
|
end
|
|
81
84
|
end
|
|
@@ -85,10 +88,10 @@ class Roda
|
|
|
85
88
|
def initialize(scope, env)
|
|
86
89
|
path_info = env['PATH_INFO']
|
|
87
90
|
|
|
88
|
-
rewrite_path(scope.class.
|
|
91
|
+
rewrite_path(scope.class.path_info_rewrites, path_info)
|
|
89
92
|
super
|
|
90
93
|
remaining_path = @remaining_path = @remaining_path.dup
|
|
91
|
-
rewrite_path(scope.class.
|
|
94
|
+
rewrite_path(scope.class.remaining_path_rewrites, remaining_path)
|
|
92
95
|
end
|
|
93
96
|
|
|
94
97
|
private
|
|
@@ -280,7 +280,7 @@ class Roda
|
|
|
280
280
|
|
|
281
281
|
# Yield the current Permissions Policy to the block.
|
|
282
282
|
def self.configure(app, opts=OPTS)
|
|
283
|
-
policy = app.opts[:permissions_policy] = if policy = app.
|
|
283
|
+
policy = app.opts[:permissions_policy] = if policy = app.permissions_policy
|
|
284
284
|
policy.dup
|
|
285
285
|
else
|
|
286
286
|
Policy.new
|
|
@@ -296,6 +296,10 @@ class Roda
|
|
|
296
296
|
policy.freeze
|
|
297
297
|
end
|
|
298
298
|
|
|
299
|
+
module ClassMethods
|
|
300
|
+
RodaPlugins.opt_attr_reader(self, :permissions_policy)
|
|
301
|
+
end
|
|
302
|
+
|
|
299
303
|
module InstanceMethods
|
|
300
304
|
# If a block is given, yield the current permission policy. Returns the
|
|
301
305
|
# current permissions policy.
|
|
@@ -315,7 +319,7 @@ class Roda
|
|
|
315
319
|
|
|
316
320
|
# The current permissions policy to be used for this response.
|
|
317
321
|
def permissions_policy
|
|
318
|
-
@permissions_policy ||= roda_class.
|
|
322
|
+
@permissions_policy ||= roda_class.permissions_policy.dup
|
|
319
323
|
end
|
|
320
324
|
|
|
321
325
|
# Do not set a permissions policy header for this response.
|
|
@@ -329,7 +333,7 @@ class Roda
|
|
|
329
333
|
def set_default_headers
|
|
330
334
|
super
|
|
331
335
|
unless @skip_permissions_policy
|
|
332
|
-
(@permissions_policy || roda_class.
|
|
336
|
+
(@permissions_policy || roda_class.permissions_policy).set_header(headers)
|
|
333
337
|
end
|
|
334
338
|
end
|
|
335
339
|
end
|
data/lib/roda/plugins/public.rb
CHANGED
|
@@ -46,10 +46,10 @@ class Roda
|
|
|
46
46
|
ENCODING_MAP = {:zstd=>'zstd', :brotli=>'br', :gzip=>'gzip'}.freeze
|
|
47
47
|
ENCODING_EXTENSIONS = {'br'=>'.br', 'gzip'=>'.gz', 'zstd'=>'.zst'}.freeze
|
|
48
48
|
|
|
49
|
-
# :
|
|
49
|
+
# simplecov:disable
|
|
50
50
|
PARSER = defined?(::URI::RFC2396_PARSER) ? ::URI::RFC2396_PARSER : ::URI::DEFAULT_PARSER
|
|
51
51
|
MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
|
|
52
|
-
# :
|
|
52
|
+
# simplecov:enable
|
|
53
53
|
|
|
54
54
|
# Use options given to setup a Rack::File instance for serving files. Options:
|
|
55
55
|
# :brotli :: Whether to serve already brotli-compressed files with a .br extension
|
|
@@ -69,19 +69,15 @@ class Roda
|
|
|
69
69
|
# :zstd :: Whether to serve already zstd-compressed files with a .zst extension
|
|
70
70
|
# for clients supporting "zstd" transfer encoding.
|
|
71
71
|
def self.configure(app, opts={})
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
elsif !app.opts[:public_root]
|
|
75
|
-
app.opts[:public_root] = app.expand_path("public")
|
|
76
|
-
end
|
|
77
|
-
app.opts[:public_server] = RACK_FILES.new(app.opts[:public_root], opts[:headers]||{}, opts[:default_mime] || 'text/plain')
|
|
72
|
+
app.opts[:public_root] = app.expand_path(opts[:root] || app.public_root || "public")
|
|
73
|
+
app.opts[:public_server] = RACK_FILES.new(app.public_root, opts[:headers]||{}, opts[:default_mime] || 'text/plain')
|
|
78
74
|
|
|
79
75
|
unless encodings = opts[:encodings]
|
|
80
76
|
if ENCODING_MAP.any?{|k,| opts.has_key?(k)}
|
|
81
77
|
encodings = ENCODING_MAP.map{|k, v| [v, ENCODING_EXTENSIONS[v]] if opts[k]}.compact
|
|
82
78
|
end
|
|
83
79
|
end
|
|
84
|
-
encodings = (encodings || app.
|
|
80
|
+
encodings = (encodings || app.public_encodings || EMPTY_ARRAY).map(&:dup).freeze
|
|
85
81
|
encodings.each do |a|
|
|
86
82
|
a << /\b#{a[0]}\b/
|
|
87
83
|
end
|
|
@@ -89,10 +85,16 @@ class Roda
|
|
|
89
85
|
app.opts[:public_encodings] = encodings
|
|
90
86
|
end
|
|
91
87
|
|
|
88
|
+
module ClassMethods
|
|
89
|
+
RodaPlugins.opt_attr_reader(self, :public_root)
|
|
90
|
+
RodaPlugins.opt_attr_reader(self, :public_server)
|
|
91
|
+
RodaPlugins.opt_attr_reader(self, :public_encodings)
|
|
92
|
+
end
|
|
93
|
+
|
|
92
94
|
module RequestMethods
|
|
93
95
|
# Serve files from the public directory if the file exists and this is a GET request.
|
|
94
96
|
def public
|
|
95
|
-
public_serve_with(roda_class.
|
|
97
|
+
public_serve_with(roda_class.public_server)
|
|
96
98
|
end
|
|
97
99
|
|
|
98
100
|
private
|
|
@@ -114,9 +116,9 @@ class Roda
|
|
|
114
116
|
def public_file_readable?(path)
|
|
115
117
|
::File.file?(path) && ::File.readable?(path)
|
|
116
118
|
rescue SystemCallError
|
|
117
|
-
# :
|
|
119
|
+
# simplecov:disable
|
|
118
120
|
false
|
|
119
|
-
# :
|
|
121
|
+
# simplecov:enable
|
|
120
122
|
end
|
|
121
123
|
|
|
122
124
|
def public_serve_with(server)
|
|
@@ -124,11 +126,10 @@ class Roda
|
|
|
124
126
|
path = PARSER.unescape(real_remaining_path)
|
|
125
127
|
return if path.include?("\0")
|
|
126
128
|
|
|
127
|
-
roda_opts = roda_class.opts
|
|
128
129
|
path = ::File.join(server.root, *public_path_segments(path))
|
|
129
130
|
|
|
130
131
|
if accept_encoding = env['HTTP_ACCEPT_ENCODING']
|
|
131
|
-
|
|
132
|
+
roda_class.public_encodings.each do |enc, ext, regexp|
|
|
132
133
|
if regexp.send(MATCH_METHOD, accept_encoding)
|
|
133
134
|
public_serve_compressed(server, path, ext, enc)
|
|
134
135
|
end
|
|
@@ -57,7 +57,7 @@ class Roda
|
|
|
57
57
|
precompiled_file = app.assets_opts[:precompiled]
|
|
58
58
|
prev_mtime = ::File.mtime(precompiled_file)
|
|
59
59
|
app.instance_exec do
|
|
60
|
-
opts[:assets] =
|
|
60
|
+
opts[:assets] = assets_opts.merge(:compiled=>_compiled_assets_initial_hash.replace(assets_opts[:compiled])).freeze
|
|
61
61
|
|
|
62
62
|
define_singleton_method(:recheck_precompiled_assets) do
|
|
63
63
|
new_mtime = ::File.mtime(precompiled_file)
|
|
@@ -53,13 +53,16 @@ class Roda
|
|
|
53
53
|
# uses a hash that redirects GET and HEAD requests with a 301 status,
|
|
54
54
|
# and other request methods with a 307 status.
|
|
55
55
|
def self.configure(app, opts=OPTS)
|
|
56
|
-
|
|
57
|
-
opts = app.opts[:redirect_http_to_https] = previous.merge(opts)
|
|
56
|
+
opts = app.opts[:redirect_http_to_https] = (app.redirect_http_to_https_opts || DEFAULTS).merge(opts)
|
|
58
57
|
opts[:port_string] = opts[:port] ? ":#{opts[:port]}".freeze : "".freeze
|
|
59
58
|
opts[:prefix] = opts[:host] ? "https://#{opts[:host]}#{opts[:port_string]}".freeze : nil
|
|
60
59
|
opts.freeze
|
|
61
60
|
end
|
|
62
61
|
|
|
62
|
+
module ClassMethods
|
|
63
|
+
RodaPlugins.opt_attr_reader(self, :redirect_http_to_https, name: :redirect_http_to_https_opts)
|
|
64
|
+
end
|
|
65
|
+
|
|
63
66
|
module RequestMethods
|
|
64
67
|
# Redirect HTTP requests to HTTPS. While this doesn't secure the
|
|
65
68
|
# current request, it makes it more likely that the browser will submit
|
|
@@ -67,7 +70,7 @@ class Roda
|
|
|
67
70
|
def redirect_http_to_https
|
|
68
71
|
return if ssl?
|
|
69
72
|
|
|
70
|
-
opts = roda_class.
|
|
73
|
+
opts = roda_class.redirect_http_to_https_opts
|
|
71
74
|
|
|
72
75
|
res = response
|
|
73
76
|
|
data/lib/roda/plugins/render.rb
CHANGED
|
@@ -46,8 +46,11 @@ class Roda
|
|
|
46
46
|
#
|
|
47
47
|
# The following plugin options are supported:
|
|
48
48
|
#
|
|
49
|
-
# :allowed_paths :: Set the template
|
|
50
|
-
# of these
|
|
49
|
+
# :allowed_paths :: Set the template directories to allow. Attempts to render paths outside
|
|
50
|
+
# of these directories will raise an error. Defaults to the +:views+ option,
|
|
51
|
+
# if it is a directory. For backwards compatibility, if a non-directory is
|
|
52
|
+
# given, it will be used as a prefix match. Roda 4 will raise if a non-directory
|
|
53
|
+
# is given as an allowed path and +:check_paths+ is true.
|
|
51
54
|
# :assume_fixed_locals :: Set if you are sure all templates in your application use fixed locals
|
|
52
55
|
# to allow for additional optimization. This is ignored unless both
|
|
53
56
|
# compiled methods and fixed locals are not supported.
|
|
@@ -298,42 +301,51 @@ class Roda
|
|
|
298
301
|
def self.tilt_template_fixed_locals?(template)
|
|
299
302
|
template.fixed_locals?
|
|
300
303
|
end
|
|
301
|
-
# :
|
|
304
|
+
# simplecov:disable
|
|
302
305
|
else
|
|
303
306
|
def self.tilt_template_fixed_locals?(template)
|
|
304
307
|
false
|
|
305
308
|
end
|
|
306
309
|
end
|
|
307
|
-
# :
|
|
310
|
+
# simplecov:enable
|
|
308
311
|
|
|
309
312
|
if compiled_method_arity == -2
|
|
310
313
|
def self.tilt_template_compiled_method(template, locals_keys, scope_class)
|
|
311
314
|
template.send(:compiled_method, locals_keys, scope_class)
|
|
312
315
|
end
|
|
313
|
-
# :
|
|
316
|
+
# simplecov:disable
|
|
314
317
|
else
|
|
315
318
|
def self.tilt_template_compiled_method(template, locals_keys, scope_class)
|
|
316
319
|
template.send(:compiled_method, locals_keys)
|
|
317
320
|
end
|
|
318
|
-
# :
|
|
321
|
+
# simplecov:enable
|
|
319
322
|
end
|
|
320
323
|
|
|
321
324
|
# Setup default rendering options. See Render for details.
|
|
322
325
|
def self.configure(app, opts=OPTS)
|
|
323
|
-
if app.
|
|
324
|
-
orig_cache = app.
|
|
325
|
-
orig_method_cache = app.
|
|
326
|
-
opts = app.
|
|
326
|
+
if app.render_opts
|
|
327
|
+
orig_cache = app.render_opts[:cache]
|
|
328
|
+
orig_method_cache = app.render_opts[:template_method_cache]
|
|
329
|
+
opts = app.render_opts[:orig_opts].merge(opts)
|
|
327
330
|
end
|
|
328
331
|
app.opts[:render] = opts.dup
|
|
329
|
-
app.
|
|
332
|
+
app.render_opts[:orig_opts] = opts
|
|
330
333
|
|
|
331
|
-
opts = app.
|
|
334
|
+
opts = app.render_opts
|
|
332
335
|
opts[:engine] = (opts[:engine] || "erb").dup.freeze
|
|
333
336
|
opts[:views] = app.expand_path(opts[:views]||"views").freeze
|
|
334
|
-
opts[:allowed_paths] ||= [opts[:views]].freeze
|
|
335
|
-
opts[:allowed_paths] = opts[:allowed_paths].map{|f| app.expand_path(f, nil)}.uniq.freeze
|
|
336
337
|
opts[:check_paths] = true unless opts.has_key?(:check_paths)
|
|
338
|
+
opts[:allowed_paths] ||= [opts[:views]]
|
|
339
|
+
opts[:allowed_paths] = opts[:allowed_paths].map do |f|
|
|
340
|
+
path = app.expand_path(f, nil)
|
|
341
|
+
if File.directory?(path) || f == opts[:views]
|
|
342
|
+
path << "/"
|
|
343
|
+
elsif opts[:check_paths]
|
|
344
|
+
# RODA4: raise
|
|
345
|
+
RodaPlugins.warn("allowed path #{f.inspect} is not a directory in the file system. For backwards compatibility, will allow prefix matches, but this could result in security issues. Roda 4 will raise if an allowed path is not a directory.")
|
|
346
|
+
end
|
|
347
|
+
path.freeze
|
|
348
|
+
end.uniq.freeze
|
|
337
349
|
opts[:assume_fixed_locals] &&= FIXED_LOCALS_COMPILED_METHOD_SUPPORT
|
|
338
350
|
|
|
339
351
|
unless opts.has_key?(:check_template_mtime)
|
|
@@ -568,7 +580,7 @@ class Roda
|
|
|
568
580
|
# affecting the parent class.
|
|
569
581
|
def inherited(subclass)
|
|
570
582
|
super
|
|
571
|
-
opts = subclass.opts[:render] = subclass.
|
|
583
|
+
opts = subclass.opts[:render] = subclass.render_opts.dup
|
|
572
584
|
if COMPILED_METHOD_SUPPORT
|
|
573
585
|
opts[:template_method_cache] = (opts[:cache_class] || RodaCache).new
|
|
574
586
|
end
|
|
@@ -576,10 +588,7 @@ class Roda
|
|
|
576
588
|
opts.freeze
|
|
577
589
|
end
|
|
578
590
|
|
|
579
|
-
|
|
580
|
-
def render_opts
|
|
581
|
-
opts[:render]
|
|
582
|
-
end
|
|
591
|
+
RodaPlugins.opt_attr_reader(self, :render, name: :render_opts)
|
|
583
592
|
|
|
584
593
|
private
|
|
585
594
|
|
|
@@ -591,13 +600,13 @@ class Roda
|
|
|
591
600
|
# in order for the precompile_templates plugin to work correctly.
|
|
592
601
|
instance.send(:retrieve_template, instance.send(:view_layout_opts, OPTS))
|
|
593
602
|
|
|
594
|
-
if COMPILED_METHOD_SUPPORT && (layout_template = render_opts[:optimize_layout]) && !
|
|
603
|
+
if COMPILED_METHOD_SUPPORT && (layout_template = render_opts[:optimize_layout]) && !render_opts[:optimized_layout_method_created]
|
|
595
604
|
instance.send(:retrieve_template, :template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
596
|
-
layout_method =
|
|
605
|
+
layout_method = render_opts[:template_method_cache][:_roda_layout]
|
|
597
606
|
define_method(:_layout_method){layout_method}
|
|
598
607
|
private :_layout_method
|
|
599
608
|
alias_method(:_layout_method, :_layout_method)
|
|
600
|
-
opts[:render] =
|
|
609
|
+
opts[:render] = render_opts.merge(:optimized_layout_method_created=>true)
|
|
601
610
|
end
|
|
602
611
|
end
|
|
603
612
|
end
|
|
@@ -638,7 +647,7 @@ class Roda
|
|
|
638
647
|
# If we have an optimized template method but no optimized layout method, create the
|
|
639
648
|
# optimized layout method if possible and use it. If you can't create the optimized
|
|
640
649
|
# layout method, fall through to the slower approach.
|
|
641
|
-
if layout_template = self.class.
|
|
650
|
+
if layout_template = self.class.render_opts[:optimize_layout]
|
|
642
651
|
retrieve_template(:template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
643
652
|
if layout_method = _layout_method
|
|
644
653
|
return _call_optimized_template_method(layout_method, OPTS){content}
|
|
@@ -683,7 +692,7 @@ class Roda
|
|
|
683
692
|
|
|
684
693
|
# Return a symbol containing the optimized layout method
|
|
685
694
|
def _layout_method
|
|
686
|
-
self.class.
|
|
695
|
+
self.class.render_opts[:template_method_cache][:_roda_layout]
|
|
687
696
|
end
|
|
688
697
|
|
|
689
698
|
# Use an optimized render path for templates with a hash of locals. Returns the result
|
|
@@ -754,40 +763,28 @@ class Roda
|
|
|
754
763
|
end
|
|
755
764
|
|
|
756
765
|
if RUBY_VERSION >= '3'
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
send(meth, locals, &block)
|
|
763
|
-
end
|
|
766
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
767
|
+
if fixed_locals
|
|
768
|
+
send(meth, **locals, &block)
|
|
769
|
+
else
|
|
770
|
+
send(meth, locals, &block)
|
|
764
771
|
end
|
|
765
|
-
|
|
766
|
-
# :
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if
|
|
771
|
-
|
|
772
|
-
send(meth, &block)
|
|
773
|
-
else
|
|
774
|
-
send(meth, **locals, &block)
|
|
775
|
-
end
|
|
772
|
+
end
|
|
773
|
+
# simplecov:disable
|
|
774
|
+
else
|
|
775
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
776
|
+
if fixed_locals
|
|
777
|
+
if locals.empty?
|
|
778
|
+
send(meth, &block)
|
|
776
779
|
else
|
|
777
|
-
send(meth, locals, &block)
|
|
780
|
+
send(meth, **locals, &block)
|
|
778
781
|
end
|
|
782
|
+
else
|
|
783
|
+
send(meth, locals, &block)
|
|
779
784
|
end
|
|
780
|
-
RUBY
|
|
781
|
-
else
|
|
782
|
-
# Call the optimized template method. This is designed to be used with the
|
|
783
|
-
# method cache, which caches the method name and whether the method uses
|
|
784
|
-
# fixed locals. Methods with fixed locals need to be called with a keyword
|
|
785
|
-
# splat.
|
|
786
|
-
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
787
|
-
send(meth, locals, &block)
|
|
788
785
|
end
|
|
789
786
|
end
|
|
790
|
-
# :
|
|
787
|
+
# simplecov:enable
|
|
791
788
|
else
|
|
792
789
|
def _cached_template_method(_)
|
|
793
790
|
nil
|
|
@@ -832,7 +829,7 @@ class Roda
|
|
|
832
829
|
# Given the template name and options, set the template class, template path/content,
|
|
833
830
|
# template block, and locals to use for the render in the passed options.
|
|
834
831
|
def find_template(opts)
|
|
835
|
-
render_opts = self.class.
|
|
832
|
+
render_opts = self.class.render_opts
|
|
836
833
|
engine_override = opts[:engine]
|
|
837
834
|
engine = opts[:engine] ||= render_opts[:engine]
|
|
838
835
|
if content = opts[:inline]
|
|
@@ -896,7 +893,7 @@ class Roda
|
|
|
896
893
|
end
|
|
897
894
|
cached_template(opts) do
|
|
898
895
|
opts = found_template_opts || find_template(opts)
|
|
899
|
-
render_opts = self.class.
|
|
896
|
+
render_opts = self.class.render_opts
|
|
900
897
|
template_opts = render_opts[:template_opts]
|
|
901
898
|
if engine_opts = render_opts[:engine_opts][opts[:engine]]
|
|
902
899
|
template_opts = template_opts.merge(engine_opts)
|
|
@@ -981,14 +978,14 @@ class Roda
|
|
|
981
978
|
end
|
|
982
979
|
|
|
983
980
|
module AssumeFixedLocalsInstanceMethods
|
|
984
|
-
|
|
981
|
+
private
|
|
982
|
+
|
|
983
|
+
# simplecov:disable
|
|
985
984
|
if RUBY_VERSION >= '3.0'
|
|
986
|
-
# :
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
end
|
|
991
|
-
RUBY
|
|
985
|
+
# simplecov:enable
|
|
986
|
+
def _call_optimized_template_method((meth,_), locals, &block)
|
|
987
|
+
send(meth, **locals, &block)
|
|
988
|
+
end
|
|
992
989
|
end
|
|
993
990
|
end
|
|
994
991
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
2
|
|
|
3
3
|
require 'tilt'
|
|
4
|
-
# :
|
|
4
|
+
# simplecov:disable
|
|
5
5
|
raise 'Tilt version does not support coverable templates' unless Tilt::Template.method_defined?(:compiled_path=)
|
|
6
|
-
# :
|
|
6
|
+
# simplecov:enable
|
|
7
7
|
|
|
8
8
|
#
|
|
9
9
|
class Roda
|
|
@@ -42,11 +42,14 @@ class Roda
|
|
|
42
42
|
# strip.
|
|
43
43
|
def self.configure(app, opts=OPTS)
|
|
44
44
|
app.opts[:render_coverage_strip_paths] = opts[:strip_paths].map{|f| File.expand_path(f)} if opts.has_key?(:strip_paths)
|
|
45
|
-
coverage_dir = app.opts[:render_coverage_dir] = opts[:dir] || app.
|
|
45
|
+
coverage_dir = app.opts[:render_coverage_dir] = opts[:dir] || app.render_coverage_dir || 'coverage/views'
|
|
46
46
|
Dir.mkdir(coverage_dir) unless File.directory?(coverage_dir)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
module ClassMethods
|
|
50
|
+
RodaPlugins.opt_attr_reader(self, :render_coverage_strip_paths)
|
|
51
|
+
RodaPlugins.opt_attr_reader(self, :render_coverage_dir)
|
|
52
|
+
|
|
50
53
|
# Set a compiled path on the created template, if the path for
|
|
51
54
|
# the template is in one of the allowed_views.
|
|
52
55
|
def create_template(opts, template_opts)
|
|
@@ -54,9 +57,10 @@ class Roda
|
|
|
54
57
|
|
|
55
58
|
path = File.expand_path(opts[:path])
|
|
56
59
|
compiled_path = nil
|
|
57
|
-
(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
(render_coverage_strip_paths || render_opts[:allowed_paths]).each do |dir|
|
|
61
|
+
dir += "/" unless dir.end_with?("/")
|
|
62
|
+
if path.start_with?(dir)
|
|
63
|
+
compiled_path = File.join(render_coverage_dir, path[dir.length, 10000000].tr('/', '-'))
|
|
60
64
|
break
|
|
61
65
|
end
|
|
62
66
|
end
|
|
@@ -76,9 +80,9 @@ class Roda
|
|
|
76
80
|
template = super
|
|
77
81
|
|
|
78
82
|
# Set compiled path for template when using older tilt versions.
|
|
79
|
-
# :
|
|
83
|
+
# simplecov:disable
|
|
80
84
|
template.compiled_path = compiled_path if compiled_path
|
|
81
|
-
# :
|
|
85
|
+
# simplecov:enable
|
|
82
86
|
|
|
83
87
|
template
|
|
84
88
|
end
|
|
@@ -171,12 +171,12 @@ class Roda
|
|
|
171
171
|
|
|
172
172
|
File.basename(template).sub(/\..+\z/, '').to_sym
|
|
173
173
|
end
|
|
174
|
-
# :
|
|
174
|
+
# simplecov:disable
|
|
175
175
|
else
|
|
176
176
|
def render_each_default_local(template)
|
|
177
177
|
File.basename(template.to_s).sub(/\..+\z/, '').to_sym
|
|
178
178
|
end
|
|
179
|
-
# :
|
|
179
|
+
# simplecov:enable
|
|
180
180
|
end
|
|
181
181
|
|
|
182
182
|
if Render::COMPILED_METHOD_SUPPORT
|