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
|
@@ -289,6 +289,10 @@ class Roda
|
|
|
289
289
|
policy.freeze
|
|
290
290
|
end
|
|
291
291
|
|
|
292
|
+
module ClassMethods
|
|
293
|
+
RodaPlugins.opt_attr_reader(self, :content_security_policy)
|
|
294
|
+
end
|
|
295
|
+
|
|
292
296
|
module InstanceMethods
|
|
293
297
|
# If a block is given, yield the current content security policy. Returns the
|
|
294
298
|
# current content security policy.
|
|
@@ -308,7 +312,7 @@ class Roda
|
|
|
308
312
|
|
|
309
313
|
# The current content security policy to be used for this response.
|
|
310
314
|
def content_security_policy
|
|
311
|
-
@content_security_policy ||= roda_class.
|
|
315
|
+
@content_security_policy ||= roda_class.content_security_policy.dup
|
|
312
316
|
end
|
|
313
317
|
|
|
314
318
|
# Do not set a content security policy header for this response.
|
|
@@ -322,7 +326,7 @@ class Roda
|
|
|
322
326
|
def set_default_headers
|
|
323
327
|
super
|
|
324
328
|
unless @skip_content_security_policy
|
|
325
|
-
(@content_security_policy || roda_class.
|
|
329
|
+
(@content_security_policy || roda_class.content_security_policy).set_header(headers)
|
|
326
330
|
end
|
|
327
331
|
end
|
|
328
332
|
end
|
|
@@ -48,9 +48,9 @@ class Roda
|
|
|
48
48
|
# Note that this plugin only affects cookies set by the application, and does not
|
|
49
49
|
# affect cookies set by middleware the application is using.
|
|
50
50
|
module CookieFlags
|
|
51
|
-
# :
|
|
51
|
+
# simplecov:disable
|
|
52
52
|
MATCH_METH = RUBY_VERSION >= '2.4' ? :match? : :match
|
|
53
|
-
# :
|
|
53
|
+
# simplecov:enable
|
|
54
54
|
private_constant :MATCH_METH
|
|
55
55
|
|
|
56
56
|
DEFAULTS = {:secure=>true, :httponly=>true, :same_site=>'strict', :action=>:modify}.freeze
|
|
@@ -61,7 +61,7 @@ class Roda
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def self.configure(app, opts=OPTS)
|
|
64
|
-
previous = app.
|
|
64
|
+
previous = app.cookie_flags || DEFAULTS
|
|
65
65
|
opts = app.opts[:cookie_flags] = previous.merge(opts)
|
|
66
66
|
|
|
67
67
|
case opts[:same_site]
|
|
@@ -74,11 +74,15 @@ class Roda
|
|
|
74
74
|
opts.freeze
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
module ClassMethods
|
|
78
|
+
RodaPlugins.opt_attr_reader(self, :cookie_flags)
|
|
79
|
+
end
|
|
80
|
+
|
|
77
81
|
module InstanceMethods
|
|
78
82
|
private
|
|
79
83
|
|
|
80
84
|
def _handle_cookie_flags_array(cookies)
|
|
81
|
-
opts = self.class.
|
|
85
|
+
opts = self.class.cookie_flags
|
|
82
86
|
needs_secure = opts[:secure]
|
|
83
87
|
needs_httponly = opts[:httponly]
|
|
84
88
|
if needs_same_site = opts[:same_site]
|
data/lib/roda/plugins/cookies.rb
CHANGED
|
@@ -20,7 +20,11 @@ class Roda
|
|
|
20
20
|
module Cookies
|
|
21
21
|
# Allow setting default cookie options when loading the cookies plugin.
|
|
22
22
|
def self.configure(app, opts={})
|
|
23
|
-
app.opts[:cookies_opts] = (app.
|
|
23
|
+
app.opts[:cookies_opts] = (app.cookies_opts||{}).merge(opts).freeze
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module ClassMethods
|
|
27
|
+
RodaPlugins.opt_attr_reader(self, :cookies_opts)
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
module ResponseMethods
|
|
@@ -32,7 +36,7 @@ class Roda
|
|
|
32
36
|
# response.delete_cookie('foo')
|
|
33
37
|
# response.delete_cookie('foo', domain: 'example.org')
|
|
34
38
|
def delete_cookie(key, value = {})
|
|
35
|
-
::Rack::Utils.delete_cookie_header!(@headers, key, roda_class.
|
|
39
|
+
::Rack::Utils.delete_cookie_header!(@headers, key, roda_class.cookies_opts.merge(value))
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
# Set the cookie with the given key in the headers.
|
|
@@ -41,7 +45,7 @@ class Roda
|
|
|
41
45
|
# response.set_cookie('foo', value: 'bar', domain: 'example.org')
|
|
42
46
|
def set_cookie(key, value)
|
|
43
47
|
value = { :value=>value } unless value.respond_to?(:keys)
|
|
44
|
-
::Rack::Utils.set_cookie_header!(@headers, key, roda_class.
|
|
48
|
+
::Rack::Utils.set_cookie_header!(@headers, key, roda_class.cookies_opts.merge(value))
|
|
45
49
|
end
|
|
46
50
|
end
|
|
47
51
|
end
|
|
@@ -44,16 +44,18 @@ class Roda
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
module ClassMethods
|
|
47
|
+
RodaPlugins.opt_attr_reader(self, :custom_block_results)
|
|
48
|
+
|
|
47
49
|
# Freeze the configured custom block results when freezing the app.
|
|
48
50
|
def freeze
|
|
49
|
-
|
|
51
|
+
custom_block_results.freeze
|
|
50
52
|
super
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
# Specify a block that will be called when an instance of klass
|
|
54
56
|
# is returned as a block result. The block defines a method.
|
|
55
57
|
def handle_block_result(klass, &block)
|
|
56
|
-
|
|
58
|
+
custom_block_results[klass] = define_roda_method(custom_block_results[klass] || "custom_block_result_#{klass}", 1, &block)
|
|
57
59
|
end
|
|
58
60
|
end
|
|
59
61
|
|
|
@@ -63,7 +65,7 @@ class Roda
|
|
|
63
65
|
# Try each configured custom block result, and call the related method
|
|
64
66
|
# to get the block result.
|
|
65
67
|
def unsupported_block_result(result)
|
|
66
|
-
roda_class.
|
|
68
|
+
roda_class.custom_block_results.each do |klass, meth|
|
|
67
69
|
if klass === result
|
|
68
70
|
result = scope.send(meth, result)
|
|
69
71
|
|
|
@@ -58,8 +58,10 @@ class Roda
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
module ClassMethods
|
|
61
|
+
RodaPlugins.opt_attr_reader(self, :custom_matchers)
|
|
62
|
+
|
|
61
63
|
def custom_matcher(match_class, &block)
|
|
62
|
-
custom_matchers = Hash[
|
|
64
|
+
custom_matchers = Hash[self.custom_matchers]
|
|
63
65
|
meth = custom_matchers[match_class] = custom_matchers[match_class] || :"_custom_matcher_#{match_class}"
|
|
64
66
|
opts[:custom_matchers] = custom_matchers.freeze
|
|
65
67
|
self::RodaRequest.send(:define_method, meth, &block)
|
|
@@ -72,7 +74,7 @@ class Roda
|
|
|
72
74
|
|
|
73
75
|
# Try custom matchers before calling super
|
|
74
76
|
def unsupported_matcher(matcher)
|
|
75
|
-
roda_class.
|
|
77
|
+
roda_class.custom_matchers.each do |match_class, meth|
|
|
76
78
|
if match_class === matcher
|
|
77
79
|
return send(meth, matcher)
|
|
78
80
|
end
|
|
@@ -27,15 +27,12 @@ class Roda
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
module ClassMethods
|
|
30
|
-
|
|
31
|
-
def default_headers
|
|
32
|
-
opts[:default_headers]
|
|
33
|
-
end
|
|
30
|
+
RodaPlugins.opt_attr_reader(self, :default_headers)
|
|
34
31
|
|
|
35
32
|
# Optimize the response class set_default_headers method if it hasn't been
|
|
36
33
|
# overridden and all default headers are strings.
|
|
37
34
|
def freeze
|
|
38
|
-
if (headers =
|
|
35
|
+
if (headers = default_headers).all?{|k, v| k.is_a?(String) && v.is_a?(String)} &&
|
|
39
36
|
(self::RodaResponse.instance_method(:set_default_headers).owner == Base::ResponseMethods)
|
|
40
37
|
self::RodaResponse.class_eval(<<-END, __FILE__, __LINE__+1)
|
|
41
38
|
private
|
|
@@ -18,6 +18,7 @@ class Roda
|
|
|
18
18
|
def self.configure(app, &block)
|
|
19
19
|
raise RodaError, "default_status plugin requires a block" unless block
|
|
20
20
|
if check_arity = app.opts.fetch(:check_arity, true)
|
|
21
|
+
# RODA4: Remove support for :check_arity, default to false behavior
|
|
21
22
|
unless block.arity == 0
|
|
22
23
|
if check_arity == :warn
|
|
23
24
|
RodaPlugins.warn "Arity mismatch in block passed to plugin :default_status. Expected Arity 0, but arguments required for #{block.inspect}"
|
|
@@ -38,46 +38,42 @@ class Roda
|
|
|
38
38
|
|
|
39
39
|
module InstanceMethods
|
|
40
40
|
if RUBY_VERSION >= '2.7'
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
RUBY
|
|
46
|
-
# :nocov:
|
|
41
|
+
def each_part(enum, template, **locals, &block)
|
|
42
|
+
render_each(enum, template, :locals=>locals, &block)
|
|
43
|
+
end
|
|
44
|
+
# simplecov:disable
|
|
47
45
|
else
|
|
48
46
|
def each_part(enum, template, locals=OPTS, &block)
|
|
49
47
|
render_each(enum, template, :locals=>locals, &block)
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
|
-
# :
|
|
50
|
+
# simplecov:enable
|
|
53
51
|
end
|
|
54
52
|
|
|
55
53
|
module AssumeFixedLocalsInstanceMethods
|
|
56
|
-
# :
|
|
54
|
+
# simplecov:disable
|
|
57
55
|
if RUBY_VERSION >= '3.0'
|
|
58
|
-
# :
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
yield send(optimized_method, **locals)
|
|
68
|
-
end
|
|
69
|
-
nil
|
|
70
|
-
else
|
|
71
|
-
enum.map do |v|
|
|
72
|
-
locals[as] = v
|
|
73
|
-
send(optimized_method, **locals)
|
|
74
|
-
end.join
|
|
56
|
+
# simplecov:enable
|
|
57
|
+
def each_part(enum, template, **locals, &block)
|
|
58
|
+
if optimized_method = _cached_render_each_template_method(template)
|
|
59
|
+
optimized_method = optimized_method[0]
|
|
60
|
+
as = render_each_default_local(template)
|
|
61
|
+
if defined?(yield)
|
|
62
|
+
enum.each do |v|
|
|
63
|
+
locals[as] = v
|
|
64
|
+
yield send(optimized_method, **locals)
|
|
75
65
|
end
|
|
66
|
+
nil
|
|
76
67
|
else
|
|
77
|
-
|
|
68
|
+
enum.map do |v|
|
|
69
|
+
locals[as] = v
|
|
70
|
+
send(optimized_method, **locals)
|
|
71
|
+
end.join
|
|
78
72
|
end
|
|
73
|
+
else
|
|
74
|
+
render_each(enum, template, :locals=>locals, &block)
|
|
79
75
|
end
|
|
80
|
-
|
|
76
|
+
end
|
|
81
77
|
end
|
|
82
78
|
end
|
|
83
79
|
end
|
|
@@ -44,10 +44,12 @@ class Roda
|
|
|
44
44
|
# if no environment is given. If ENV['RACK_ENV'] is not set and
|
|
45
45
|
# no environment is given, assume the development environment.
|
|
46
46
|
def self.configure(app, env=ENV["RACK_ENV"])
|
|
47
|
-
app.environment = (env || 'development').to_sym
|
|
47
|
+
app.opts[:environment] = (env || 'development').to_sym
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
module ClassMethods
|
|
51
|
+
RodaPlugins.opt_attr_reader(self, :environment)
|
|
52
|
+
|
|
51
53
|
# If no environments are given or one of the given environments
|
|
52
54
|
# matches the current environment, yield the receiver to the block.
|
|
53
55
|
def configure(*envs)
|
|
@@ -56,12 +58,6 @@ class Roda
|
|
|
56
58
|
end
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
# The current environment for the application, which should be stored
|
|
60
|
-
# as a symbol.
|
|
61
|
-
def environment
|
|
62
|
-
opts[:environment]
|
|
63
|
-
end
|
|
64
|
-
|
|
65
61
|
# Override the environment for the application, instead of using
|
|
66
62
|
# RACK_ENV.
|
|
67
63
|
def environment=(v)
|
|
@@ -44,9 +44,9 @@ class Roda
|
|
|
44
44
|
:filter=>lambda{|k,v| false},
|
|
45
45
|
:headers=>OPTS,
|
|
46
46
|
:host=>'localhost',
|
|
47
|
-
# :
|
|
47
|
+
# simplecov:disable
|
|
48
48
|
:emailer=>lambda{|h| Net::SMTP.start(h[:host]){|s| s.send_message(h[:message], h[:from], h[:to])}},
|
|
49
|
-
# :
|
|
49
|
+
# simplecov:enable
|
|
50
50
|
:default_headers=>lambda do |h, e|
|
|
51
51
|
subject = if e.respond_to?(:message)
|
|
52
52
|
"#{e.class}: #{e.message}"
|
|
@@ -56,7 +56,7 @@ class Roda
|
|
|
56
56
|
{'From'=>h[:from], 'To'=>h[:to], 'Subject'=>"#{h[:prefix]}#{subject}"}
|
|
57
57
|
end,
|
|
58
58
|
:body=>lambda do |s, e|
|
|
59
|
-
filter = s.
|
|
59
|
+
filter = s.class.error_email_opts[:filter]
|
|
60
60
|
format = lambda do |h|
|
|
61
61
|
h = h.map{|k, v| "#{k.inspect} => #{filter.call(k, v) ? 'FILTERED' : v.inspect}"}
|
|
62
62
|
h.sort!
|
|
@@ -120,8 +120,12 @@ END
|
|
|
120
120
|
raise RodaError, "must provide :to and :from options to error_email plugin"
|
|
121
121
|
end
|
|
122
122
|
app.opts[:error_email] = email_opts
|
|
123
|
-
app.
|
|
124
|
-
app.
|
|
123
|
+
app.error_email_opts[:headers].freeze
|
|
124
|
+
app.error_email_opts.freeze
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
module ClassMethods
|
|
128
|
+
RodaPlugins.opt_attr_reader(self, :error_email, name: :error_email_opts)
|
|
125
129
|
end
|
|
126
130
|
|
|
127
131
|
module InstanceMethods
|
|
@@ -129,7 +133,7 @@ END
|
|
|
129
133
|
# instance, but it can be a plain string which is used as the subject for
|
|
130
134
|
# the email.
|
|
131
135
|
def error_email(exception)
|
|
132
|
-
email_opts = self.class.
|
|
136
|
+
email_opts = self.class.error_email_opts.dup
|
|
133
137
|
email_opts[:message] = error_email_content(exception)
|
|
134
138
|
email_opts[:emailer].call(email_opts)
|
|
135
139
|
end
|
|
@@ -137,7 +141,7 @@ END
|
|
|
137
141
|
# The content of the email to send, include the headers and the body.
|
|
138
142
|
# Takes the same argument as #error_email.
|
|
139
143
|
def error_email_content(exception)
|
|
140
|
-
email_opts = self.class.
|
|
144
|
+
email_opts = self.class.error_email_opts
|
|
141
145
|
headers = email_opts[:default_headers].call(email_opts, exception)
|
|
142
146
|
headers = headers.merge(email_opts[:headers])
|
|
143
147
|
headers = headers.map{|k,v| "#{k}: #{v.gsub(/\r?\n/m, "\r\n ")}"}.sort.join("\r\n")
|
|
@@ -49,7 +49,7 @@ class Roda
|
|
|
49
49
|
# If a block is given, automatically call the +error+ method on
|
|
50
50
|
# the Roda class with it.
|
|
51
51
|
def self.configure(app, opts={}, &block)
|
|
52
|
-
app.opts[:error_handler_classes] = (opts[:classes] || app.
|
|
52
|
+
app.opts[:error_handler_classes] = (opts[:classes] || app.error_handler_classes || DEFAULT_ERROR_HANDLER_CLASSES).dup.freeze
|
|
53
53
|
|
|
54
54
|
if block
|
|
55
55
|
app.error(&block)
|
|
@@ -57,6 +57,8 @@ class Roda
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
module ClassMethods
|
|
60
|
+
RodaPlugins.opt_attr_reader(self, :error_handler_classes)
|
|
61
|
+
|
|
60
62
|
# Install the given block as the error handler, so that if routing
|
|
61
63
|
# the request raises an exception, the block will be called with
|
|
62
64
|
# the exception in the scope of the Roda instance.
|
|
@@ -77,7 +79,7 @@ class Roda
|
|
|
77
79
|
ensure
|
|
78
80
|
_roda_after(res)
|
|
79
81
|
end
|
|
80
|
-
rescue *
|
|
82
|
+
rescue *self.class.error_handler_classes => e
|
|
81
83
|
_handle_error(e)
|
|
82
84
|
end
|
|
83
85
|
|
|
@@ -89,7 +91,7 @@ class Roda
|
|
|
89
91
|
ensure
|
|
90
92
|
_roda_after(res)
|
|
91
93
|
end
|
|
92
|
-
rescue *
|
|
94
|
+
rescue *self.class.error_handler_classes => e
|
|
93
95
|
_handle_error(e)
|
|
94
96
|
end
|
|
95
97
|
|
|
@@ -44,12 +44,16 @@ class Roda
|
|
|
44
44
|
|
|
45
45
|
# Set default opts for plugin. See ErrorEmail module RDoc for options.
|
|
46
46
|
def self.configure(app, opts=OPTS)
|
|
47
|
-
app.opts[:error_mail] = email_opts = (app.
|
|
47
|
+
app.opts[:error_mail] = email_opts = (app.error_mail_opts || {:filter=>DEFAULT_FILTER}).merge(opts).freeze
|
|
48
48
|
unless email_opts[:to] && email_opts[:from]
|
|
49
49
|
raise RodaError, "must provide :to and :from options to error_mail plugin"
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
module ClassMethods
|
|
54
|
+
RodaPlugins.opt_attr_reader(self, :error_mail, name: :error_mail_opts)
|
|
55
|
+
end
|
|
56
|
+
|
|
53
57
|
module InstanceMethods
|
|
54
58
|
# Send an email for the given error. +exception+ is usually an exception
|
|
55
59
|
# instance, but it can be a plain string which is used as the subject for
|
|
@@ -67,7 +71,7 @@ class Roda
|
|
|
67
71
|
private
|
|
68
72
|
|
|
69
73
|
def _error_mail(e)
|
|
70
|
-
email_opts = self.class.
|
|
74
|
+
email_opts = self.class.error_mail_opts
|
|
71
75
|
subject = if e.respond_to?(:message)
|
|
72
76
|
"#{e.class}: #{e.message}"
|
|
73
77
|
else
|
|
@@ -415,14 +415,14 @@ END
|
|
|
415
415
|
def exception_page_exception_message(exception)
|
|
416
416
|
exception.detailed_message(highlight: false).to_s
|
|
417
417
|
end
|
|
418
|
-
# :
|
|
418
|
+
# simplecov:disable
|
|
419
419
|
else
|
|
420
420
|
# Return message to use for exception.
|
|
421
421
|
def exception_page_exception_message(exception)
|
|
422
422
|
exception.message.to_s
|
|
423
423
|
end
|
|
424
424
|
end
|
|
425
|
-
# :
|
|
425
|
+
# simplecov:enable
|
|
426
426
|
end
|
|
427
427
|
|
|
428
428
|
module RequestMethods
|
data/lib/roda/plugins/h.rb
CHANGED
|
@@ -17,10 +17,10 @@ class Roda
|
|
|
17
17
|
begin
|
|
18
18
|
require 'cgi/escape'
|
|
19
19
|
unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
|
|
20
|
-
# :
|
|
20
|
+
# simplecov:disable
|
|
21
21
|
CGI = Object.new
|
|
22
22
|
CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
|
|
23
|
-
# :
|
|
23
|
+
# simplecov:enable
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
module InstanceMethods
|
|
@@ -30,7 +30,7 @@ class Roda
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
rescue LoadError
|
|
33
|
-
# :
|
|
33
|
+
# simplecov:disable
|
|
34
34
|
|
|
35
35
|
# A Hash of entities and their escaped equivalents,
|
|
36
36
|
# to be escaped by h().
|
|
@@ -50,7 +50,7 @@ class Roda
|
|
|
50
50
|
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
# :
|
|
53
|
+
# simplecov:enable
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -33,9 +33,11 @@ class Roda
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
module ClassMethods
|
|
36
|
+
RodaPlugins.opt_attr_reader(self, :hash_branch_view_subdir_methods)
|
|
37
|
+
|
|
36
38
|
# Freeze the hash_branch_view_subdir metadata when freezing the app.
|
|
37
39
|
def freeze
|
|
38
|
-
|
|
40
|
+
hash_branch_view_subdir_methods.freeze.each_value(&:freeze)
|
|
39
41
|
super
|
|
40
42
|
end
|
|
41
43
|
|
|
@@ -43,8 +45,8 @@ class Roda
|
|
|
43
45
|
def inherited(subclass)
|
|
44
46
|
super
|
|
45
47
|
|
|
46
|
-
h = subclass.
|
|
47
|
-
|
|
48
|
+
h = subclass.hash_branch_view_subdir_methods
|
|
49
|
+
hash_branch_view_subdir_methods.each do |namespace, routes|
|
|
48
50
|
h[namespace] = routes.dup
|
|
49
51
|
end
|
|
50
52
|
end
|
|
@@ -53,7 +55,7 @@ class Roda
|
|
|
53
55
|
# by modifying the generated method to append the view subdirectory before
|
|
54
56
|
# dispatching to the original block.
|
|
55
57
|
def hash_branch(namespace='', segment, &block)
|
|
56
|
-
meths =
|
|
58
|
+
meths = hash_branch_view_subdir_methods[namespace] ||= {}
|
|
57
59
|
|
|
58
60
|
if block
|
|
59
61
|
meth = meths[segment] = define_roda_method(meths[segment] || "_hash_branch_view_subdir_#{namespace}_#{segment}", 1, &convert_route_block(block))
|
|
@@ -88,9 +88,11 @@ class Roda
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
module ClassMethods
|
|
91
|
+
RodaPlugins.opt_attr_reader(self, :hash_branches)
|
|
92
|
+
|
|
91
93
|
# Freeze the hash_branches metadata when freezing the app.
|
|
92
94
|
def freeze
|
|
93
|
-
|
|
95
|
+
hash_branches.freeze.each_value(&:freeze)
|
|
94
96
|
super
|
|
95
97
|
end
|
|
96
98
|
|
|
@@ -98,8 +100,8 @@ class Roda
|
|
|
98
100
|
def inherited(subclass)
|
|
99
101
|
super
|
|
100
102
|
|
|
101
|
-
h = subclass.
|
|
102
|
-
|
|
103
|
+
h = subclass.hash_branches
|
|
104
|
+
hash_branches.each do |namespace, routes|
|
|
103
105
|
h[namespace] = routes.dup
|
|
104
106
|
end
|
|
105
107
|
end
|
|
@@ -108,7 +110,7 @@ class Roda
|
|
|
108
110
|
# a block, removes the existing branch handler if it exists.
|
|
109
111
|
def hash_branch(namespace='', segment, &block)
|
|
110
112
|
segment = "/#{segment}"
|
|
111
|
-
routes =
|
|
113
|
+
routes = hash_branches[namespace] ||= {}
|
|
112
114
|
if block
|
|
113
115
|
routes[segment] = define_roda_method(routes[segment] || "hash_branch_#{namespace}_#{segment}", 1, &convert_route_block(block))
|
|
114
116
|
elsif meth = routes.delete(segment)
|
|
@@ -125,15 +127,19 @@ class Roda
|
|
|
125
127
|
|
|
126
128
|
return unless rp.getbyte(0) == 47 # "/"
|
|
127
129
|
|
|
128
|
-
if routes = roda_class.
|
|
130
|
+
if routes = roda_class.hash_branches[namespace]
|
|
129
131
|
if segment_end = rp.index('/', 1)
|
|
130
132
|
if meth = routes[rp[0, segment_end]]
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
always do
|
|
134
|
+
@remaining_path = rp[segment_end, 100000000]
|
|
135
|
+
scope.send(meth, self)
|
|
136
|
+
end
|
|
133
137
|
end
|
|
134
138
|
elsif meth = routes[rp]
|
|
135
|
-
|
|
136
|
-
|
|
139
|
+
always do
|
|
140
|
+
@remaining_path = ''
|
|
141
|
+
scope.send(meth, self)
|
|
142
|
+
end
|
|
137
143
|
end
|
|
138
144
|
end
|
|
139
145
|
end
|
|
@@ -80,9 +80,11 @@ class Roda
|
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
module ClassMethods
|
|
83
|
+
RodaPlugins.opt_attr_reader(self, :hash_paths)
|
|
84
|
+
|
|
83
85
|
# Freeze the hash_paths metadata when freezing the app.
|
|
84
86
|
def freeze
|
|
85
|
-
|
|
87
|
+
hash_paths.freeze.each_value(&:freeze)
|
|
86
88
|
super
|
|
87
89
|
end
|
|
88
90
|
|
|
@@ -90,8 +92,8 @@ class Roda
|
|
|
90
92
|
def inherited(subclass)
|
|
91
93
|
super
|
|
92
94
|
|
|
93
|
-
h = subclass.
|
|
94
|
-
|
|
95
|
+
h = subclass.hash_paths
|
|
96
|
+
hash_paths.each do |namespace, routes|
|
|
95
97
|
h[namespace] = routes.dup
|
|
96
98
|
end
|
|
97
99
|
end
|
|
@@ -102,7 +104,7 @@ class Roda
|
|
|
102
104
|
# there is one. If called without a block, removes the existing
|
|
103
105
|
# path handler if it exists.
|
|
104
106
|
def hash_path(namespace='', path, &block)
|
|
105
|
-
routes =
|
|
107
|
+
routes = hash_paths[namespace] ||= {}
|
|
106
108
|
if block
|
|
107
109
|
routes[path] = define_roda_method(routes[path] || "hash_path_#{namespace}_#{path}", 1, &convert_route_block(block))
|
|
108
110
|
elsif meth = routes.delete(path)
|
|
@@ -115,9 +117,11 @@ class Roda
|
|
|
115
117
|
# Checks the matching hash_path namespace for a branch matching the
|
|
116
118
|
# remaining path, and dispatch to that block if there is one.
|
|
117
119
|
def hash_paths(namespace=matched_path)
|
|
118
|
-
if (routes = roda_class.
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
if (routes = roda_class.hash_paths[namespace]) && (meth = routes[@remaining_path])
|
|
121
|
+
always do
|
|
122
|
+
@remaining_path = ''
|
|
123
|
+
scope.send(meth, self)
|
|
124
|
+
end
|
|
121
125
|
end
|
|
122
126
|
end
|
|
123
127
|
end
|
|
@@ -51,11 +51,11 @@ class Roda
|
|
|
51
51
|
Digest = begin
|
|
52
52
|
require 'openssl'
|
|
53
53
|
::OpenSSL::Digest
|
|
54
|
-
# :
|
|
54
|
+
# simplecov:disable
|
|
55
55
|
rescue LoadError
|
|
56
56
|
require 'digest/sha2'
|
|
57
57
|
::Digest
|
|
58
|
-
# :
|
|
58
|
+
# simplecov:enable
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def self.load_dependencies(app, opts = OPTS)
|
|
@@ -78,16 +78,20 @@ class Roda
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
module ClassMethods
|
|
81
|
+
RodaPlugins.opt_attr_reader(self, :hash_public_prefix)
|
|
82
|
+
RodaPlugins.opt_attr_reader(self, :hash_public_length)
|
|
83
|
+
RodaPlugins.opt_attr_reader(self, :hash_public_mutex)
|
|
84
|
+
RodaPlugins.opt_attr_reader(self, :hash_public_cache)
|
|
85
|
+
|
|
81
86
|
# The digest for the given file to use in hash_path.
|
|
82
87
|
def hash_path_digest(file)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
mutex = opts[:hash_public_mutex]
|
|
88
|
+
cache = hash_public_cache
|
|
89
|
+
mutex = hash_public_mutex
|
|
86
90
|
unless digest = mutex.synchronize{cache[file]}
|
|
87
|
-
digest = Digest::SHA256.file(File.join(
|
|
91
|
+
digest = Digest::SHA256.file(File.join(public_root, file)).base64digest
|
|
88
92
|
digest.chomp!("=")
|
|
89
93
|
digest.tr!("+/", "-_")
|
|
90
|
-
if length =
|
|
94
|
+
if length = hash_public_length
|
|
91
95
|
digest = digest[0, length]
|
|
92
96
|
end
|
|
93
97
|
digest.freeze
|
|
@@ -102,7 +106,7 @@ class Roda
|
|
|
102
106
|
# This does not check the file is inside the directory for performance
|
|
103
107
|
# reasons, so this should not be called with untrusted input.
|
|
104
108
|
def hash_path(file)
|
|
105
|
-
"/#{
|
|
109
|
+
"/#{self.class.hash_public_prefix}/#{self.class.hash_path_digest(file)}/#{file}"
|
|
106
110
|
end
|
|
107
111
|
end
|
|
108
112
|
|
|
@@ -112,7 +116,7 @@ class Roda
|
|
|
112
116
|
# a string segment for the content hash, and this is a GET request.
|
|
113
117
|
def hash_public
|
|
114
118
|
if is_get?
|
|
115
|
-
on roda_class.
|
|
119
|
+
on roda_class.hash_public_prefix, String do |_|
|
|
116
120
|
public
|
|
117
121
|
end
|
|
118
122
|
end
|