roda 3.108.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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/lib/roda/opts.rb +22 -0
  3. data/lib/roda/plugins/Integer_matcher_max.rb +1 -1
  4. data/lib/roda/plugins/_add_script_name.rb +14 -0
  5. data/lib/roda/plugins/_json.rb +22 -0
  6. data/lib/roda/plugins/_symbol_class_matchers.rb +6 -6
  7. data/lib/roda/plugins/additional_render_engines.rb +5 -1
  8. data/lib/roda/plugins/additional_view_directories.rb +5 -1
  9. data/lib/roda/plugins/assets.rb +7 -10
  10. data/lib/roda/plugins/autoload_hash_branches.rb +9 -3
  11. data/lib/roda/plugins/autoload_named_routes.rb +9 -3
  12. data/lib/roda/plugins/capture_erb.rb +5 -1
  13. data/lib/roda/plugins/chunked.rb +11 -5
  14. data/lib/roda/plugins/class_level_routing.rb +5 -3
  15. data/lib/roda/plugins/class_matchers.rb +3 -1
  16. data/lib/roda/plugins/common_logger.rb +7 -3
  17. data/lib/roda/plugins/conditional_sessions.rb +1 -1
  18. data/lib/roda/plugins/content_for.rb +5 -1
  19. data/lib/roda/plugins/content_security_policy.rb +6 -2
  20. data/lib/roda/plugins/cookie_flags.rb +6 -2
  21. data/lib/roda/plugins/cookies.rb +7 -3
  22. data/lib/roda/plugins/custom_block_results.rb +5 -3
  23. data/lib/roda/plugins/custom_matchers.rb +4 -2
  24. data/lib/roda/plugins/default_headers.rb +2 -5
  25. data/lib/roda/plugins/default_status.rb +1 -0
  26. data/lib/roda/plugins/each_part.rb +19 -23
  27. data/lib/roda/plugins/environments.rb +3 -7
  28. data/lib/roda/plugins/error_email.rb +9 -5
  29. data/lib/roda/plugins/error_handler.rb +5 -3
  30. data/lib/roda/plugins/error_mail.rb +6 -2
  31. data/lib/roda/plugins/hash_branch_view_subdir.rb +6 -4
  32. data/lib/roda/plugins/hash_branches.rb +7 -5
  33. data/lib/roda/plugins/hash_paths.rb +7 -5
  34. data/lib/roda/plugins/hash_public.rb +11 -7
  35. data/lib/roda/plugins/hash_public_cache.rb +9 -7
  36. data/lib/roda/plugins/hash_routes.rb +5 -3
  37. data/lib/roda/plugins/heartbeat.rb +6 -2
  38. data/lib/roda/plugins/hmac_paths.rb +9 -3
  39. data/lib/roda/plugins/hooks.rb +13 -10
  40. data/lib/roda/plugins/host_authorization.rb +7 -2
  41. data/lib/roda/plugins/host_routing.rb +7 -2
  42. data/lib/roda/plugins/invalid_request_body.rb +20 -4
  43. data/lib/roda/plugins/ip_from_header.rb +5 -1
  44. data/lib/roda/plugins/json.rb +10 -8
  45. data/lib/roda/plugins/json_parser.rb +26 -12
  46. data/lib/roda/plugins/mail_processor.rb +9 -6
  47. data/lib/roda/plugins/mailer.rb +5 -3
  48. data/lib/roda/plugins/match_affix.rb +6 -1
  49. data/lib/roda/plugins/match_hook_args.rb +7 -5
  50. data/lib/roda/plugins/middleware.rb +13 -7
  51. data/lib/roda/plugins/module_include.rb +1 -1
  52. data/lib/roda/plugins/multi_public.rb +6 -2
  53. data/lib/roda/plugins/multi_route.rb +1 -1
  54. data/lib/roda/plugins/multi_run.rb +13 -17
  55. data/lib/roda/plugins/named_routes.rb +8 -6
  56. data/lib/roda/plugins/named_templates.rb +5 -3
  57. data/lib/roda/plugins/path.rb +16 -14
  58. data/lib/roda/plugins/path_rewriter.rb +8 -5
  59. data/lib/roda/plugins/permissions_policy.rb +7 -3
  60. data/lib/roda/plugins/public.rb +11 -10
  61. data/lib/roda/plugins/recheck_precompiled_assets.rb +1 -1
  62. data/lib/roda/plugins/redirect_http_to_https.rb +6 -3
  63. data/lib/roda/plugins/render.rb +34 -49
  64. data/lib/roda/plugins/render_coverage.rb +6 -3
  65. data/lib/roda/plugins/render_locals.rb +12 -6
  66. data/lib/roda/plugins/request_aref.rb +1 -1
  67. data/lib/roda/plugins/response_content_type.rb +7 -3
  68. data/lib/roda/plugins/route_csrf.rb +7 -2
  69. data/lib/roda/plugins/run_append_slash.rb +5 -1
  70. data/lib/roda/plugins/sec_fetch_site_csrf.rb +6 -2
  71. data/lib/roda/plugins/sessions.rb +15 -11
  72. data/lib/roda/plugins/shape_friendly.rb +3 -1
  73. data/lib/roda/plugins/sinatra_helpers.rb +7 -3
  74. data/lib/roda/plugins/status_handler.rb +5 -3
  75. data/lib/roda/plugins/strip_path_prefix.rb +4 -2
  76. data/lib/roda/plugins/symbol_matchers.rb +3 -1
  77. data/lib/roda/plugins/symbol_views.rb +1 -1
  78. data/lib/roda/plugins/timestamp_public.rb +8 -4
  79. data/lib/roda/plugins/type_routing.rb +9 -5
  80. data/lib/roda/plugins/typecast_params.rb +2 -2
  81. data/lib/roda/plugins/typecast_params_sized_integers.rb +1 -1
  82. data/lib/roda/plugins/url_escape.rb +2 -14
  83. data/lib/roda/plugins.rb +10 -0
  84. data/lib/roda/version.rb +1 -1
  85. data/lib/roda.rb +25 -14
  86. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88f17808e272159aaa6d5f1239eeb3453935aa188c0933b753e5a66046212e03
4
- data.tar.gz: 952920ff3e1dafeefea80acefcbd3fb5e03f0033c2ca909757c697e2acd7c955
3
+ metadata.gz: f8fe0436b7983ecbf3bbbf1cf8d7d8867f87184550ef097d6fad894a17211c5e
4
+ data.tar.gz: a19800b130b13b745d3ff6a77c22ac88e4c44b70a778c243e07212c0fcebd019
5
5
  SHA512:
6
- metadata.gz: fdd21d3152a257f21d1cfb33562334b71de82e1f017899dae0d5e9974f9b1028824c017f82d54405d5f88bed4d53607f6c238bb21c1907492da38b3b755e1a73
7
- data.tar.gz: 61ed1ac97df5919e486a82abb62ad47b45603ec3c46d230ce6aa6f7ac697cd52b45af167a597ad691ec6de5267886e6b420efc0d3117a3604ba45f613703e80d
6
+ metadata.gz: e0778c049972903e98c14287074ed0833eb63b201834c61ed8731cafb9be5da523963e931eb8ed668a975f61682d866c6ffcd1e61a4aaa19d62ec663f2318fc1
7
+ data.tar.gz: 17e2cd71b7593a18b398fd2965a2673280e11f76db7f02f9de1eaa604e4632857fbe8c1875cfd9fc3ec9d6af2152ad4a63d757065a1ae53cb46766de5690238a
data/lib/roda/opts.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen-string-literal: true
2
+
3
+ class Roda
4
+ class RodaOpts < Hash
5
+ # The Roda class to set instance variables on when calling #[]=
6
+ attr_writer :roda_class
7
+
8
+ # If duplicating the hash, detach the Roda class.
9
+ def initialize_copy(_)
10
+ super
11
+ @roda_class = nil
12
+ end
13
+
14
+ # Set an <tt>@opt_*</tt> instance variable in the Roda class if it is tied
15
+ # to a Roda class.
16
+ def []=(k, v)
17
+ super
18
+ @roda_class.instance_variable_set(:"@opt_#{k}", v) if @roda_class && Symbol === k
19
+ v
20
+ end
21
+ end
22
+ end
@@ -23,7 +23,7 @@ class Roda
23
23
  module IntegerMatcherMax
24
24
  def self.configure(app, max=nil)
25
25
  if max
26
- app.class_eval do
26
+ app.class_exec do
27
27
  meth = :_max_value_convert_class_Integer
28
28
  define_method(meth){max}
29
29
  alias_method meth, meth
@@ -0,0 +1,14 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ module AddScriptName_
7
+ module ClassMethods
8
+ RodaPlugins.opt_attr_reader(self, :add_script_name, name: :add_script_name?)
9
+ end
10
+ end
11
+
12
+ register_plugin(:_add_script_name, AddScriptName_)
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'json'
4
+
5
+ #
6
+ class Roda
7
+ module RodaPlugins
8
+ module JSON_
9
+ module ClassMethods
10
+ def json_parser
11
+ @opt_json_parser || ::JSON.method(:parse)
12
+ end
13
+
14
+ def json_serializer
15
+ @opt_json_serializer || :to_json.to_proc
16
+ end
17
+ end
18
+ end
19
+
20
+ register_plugin(:_json, JSON_)
21
+ end
22
+ end
@@ -26,11 +26,11 @@ class Roda
26
26
  regexp = matcher
27
27
  consume_regexp = self::RodaRequest.send(:consume_pattern, regexp)
28
28
  when Symbol
29
- unless opts[:symbol_matchers]
29
+ unless respond_to?(:symbol_matchers)
30
30
  raise RodaError, "cannot provide Symbol matcher to class_matcher unless using symbol_matchers plugin: #{matcher.inspect}"
31
31
  end
32
32
 
33
- regexp, consume_regexp, convert_meth, consume_meth = opts[:symbol_matchers][matcher]
33
+ regexp, consume_regexp, convert_meth, consume_meth = symbol_matchers[matcher]
34
34
 
35
35
  unless regexp
36
36
  raise RodaError, "unregistered symbol matcher given to #{type}_matcher: #{matcher.inspect}"
@@ -38,11 +38,11 @@ class Roda
38
38
 
39
39
  block = _merge_matcher_blocks(type, obj, block, convert_meth)
40
40
  when Class
41
- unless opts[:class_matchers]
41
+ unless respond_to?(:class_matchers)
42
42
  raise RodaError, "cannot provide Class matcher to symbol_matcher unless using class_matchers plugin: #{matcher.inspect}"
43
43
  end
44
44
 
45
- regexp, consume_regexp, convert_meth, consume_meth = opts[:class_matchers][matcher]
45
+ regexp, consume_regexp, convert_meth, consume_meth = class_matchers[matcher]
46
46
  unless regexp
47
47
  raise RodaError, "unregistered class matcher given to #{type}_matcher: #{matcher.inspect}"
48
48
  end
@@ -60,9 +60,9 @@ class Roda
60
60
  end
61
61
 
62
62
  consume_meth ||= options[:segment] ? :_consume_single_segment : :consume
63
- array = opts[:"#{type}_matchers"][obj] = [regexp, consume_regexp, convert_meth, consume_meth].freeze
63
+ array = (obj.is_a?(Symbol) ? symbol_matchers : class_matchers)[obj] = [regexp, consume_regexp, convert_meth, consume_meth].freeze
64
64
 
65
- self::RodaRequest.class_eval do
65
+ self::RodaRequest.class_exec do
66
66
  class_exec(meth, array, &request_class_block)
67
67
  private meth
68
68
  end
@@ -34,6 +34,10 @@ class Roda
34
34
  app.opts[:additional_render_engines] = render_engines.dup.freeze
35
35
  end
36
36
 
37
+ module ClassMethods
38
+ RodaPlugins.opt_attr_reader(self, :additional_render_engines)
39
+ end
40
+
37
41
  module InstanceMethods
38
42
  private
39
43
 
@@ -45,7 +49,7 @@ class Roda
45
49
  orig_path = super
46
50
 
47
51
  unless File.file?(orig_path)
48
- self.opts[:additional_render_engines].each do |engine|
52
+ self.class.additional_render_engines.each do |engine|
49
53
  path = super(opts.merge(:engine=>engine))
50
54
  return path if File.file?(path)
51
55
  end
@@ -39,6 +39,10 @@ class Roda
39
39
  app.plugin :render, :allowed_paths=>(app.opts[:render][:allowed_paths] + view_dirs).uniq.freeze
40
40
  end
41
41
 
42
+ module ClassMethods
43
+ RodaPlugins.opt_attr_reader(self, :additional_view_directories)
44
+ end
45
+
42
46
  module InstanceMethods
43
47
  private
44
48
 
@@ -50,7 +54,7 @@ class Roda
50
54
  orig_path = super
51
55
 
52
56
  unless File.file?(orig_path)
53
- self.opts[:additional_view_directories].each do |view_dir|
57
+ self.class.additional_view_directories.each do |view_dir|
54
58
  path = super(opts.merge(:views=>view_dir))
55
59
  return path if File.file?(path)
56
60
  end
@@ -371,6 +371,8 @@ class Roda
371
371
  # Load the render, caching, and h plugins, since the assets plugin
372
372
  # depends on them.
373
373
  def self.load_dependencies(app, opts = OPTS)
374
+ app.plugin :_json
375
+ app.plugin :_add_script_name
374
376
  app.plugin :render
375
377
  app.plugin :caching
376
378
  app.plugin :h
@@ -401,7 +403,7 @@ class Roda
401
403
  app.opts[:assets] = opts.dup
402
404
  app.opts[:assets][:orig_opts] = opts
403
405
  end
404
- opts = app.opts[:assets]
406
+ opts = app.assets_opts
405
407
  opts[:path] = app.expand_path(opts[:path]||"assets").freeze
406
408
  opts[:public] = app.expand_path(opts[:public]||"public").freeze
407
409
 
@@ -420,7 +422,6 @@ class Roda
420
422
  end
421
423
 
422
424
  if opts[:precompiled] && !opts[:compiled] && ::File.exist?(opts[:precompiled])
423
- require 'json'
424
425
  opts[:compiled] = app.send(:_precompiled_asset_metadata, opts[:precompiled])
425
426
  end
426
427
 
@@ -498,10 +499,7 @@ class Roda
498
499
  end
499
500
 
500
501
  module ClassMethods
501
- # Return the assets options for this class.
502
- def assets_opts
503
- opts[:assets]
504
- end
502
+ RodaPlugins.opt_attr_reader(self, :assets, name: :assets_opts)
505
503
 
506
504
  # Compile options for the given asset type. If no asset_type
507
505
  # is given, compile both the :css and :js asset types. You
@@ -522,10 +520,9 @@ class Roda
522
520
  end
523
521
 
524
522
  if precompile_file = assets_opts[:precompiled]
525
- require 'json'
526
523
  ::FileUtils.mkdir_p(File.dirname(precompile_file))
527
524
  tmp_file = "#{precompile_file}.tmp"
528
- ::File.open(tmp_file, 'wb'){|f| f.write((opts[:json_serializer] || :to_json.to_proc).call(assets_opts[:compiled]))}
525
+ ::File.open(tmp_file, 'wb'){|f| f.write(json_serializer.call(assets_opts[:compiled]))}
529
526
  ::File.rename(tmp_file, precompile_file)
530
527
  end
531
528
 
@@ -558,7 +555,7 @@ class Roda
558
555
 
559
556
  # The precompiled asset metadata stored in the given file
560
557
  def _precompiled_asset_metadata(file)
561
- (opts[:json_parser] || ::JSON.method(:parse)).call(::File.read(file))
558
+ json_parser.call(::File.read(file))
562
559
  end
563
560
 
564
561
  # Compile each array of files for the given type into a single
@@ -711,7 +708,7 @@ class Roda
711
708
  end
712
709
  stype = ltype.to_s
713
710
 
714
- url_prefix = request.script_name if self.class.opts[:add_script_name]
711
+ url_prefix = request.script_name if self.class.add_script_name?
715
712
  relative_paths = o[:relative_paths]
716
713
 
717
714
  paths = if o[:compiled]
@@ -38,13 +38,15 @@ class Roda
38
38
  end
39
39
 
40
40
  module ClassMethods
41
+ RodaPlugins.opt_attr_reader(self, :autoload_hash_branch_files)
42
+
41
43
  # Autoload the given file when there is request for the hash branch.
42
44
  # The given file should configure the hash branch specified.
43
45
  def autoload_hash_branch(namespace='', segment, file)
44
46
  segment = "/#{segment}"
45
47
  file = File.expand_path(file)
46
- opts[:autoload_hash_branch_files] << file
47
- routes = opts[:hash_branches][namespace] ||= {}
48
+ autoload_hash_branch_files << file
49
+ routes = hash_branches[namespace] ||= {}
48
50
  meth = routes[segment] = define_roda_method(routes[segment] || "hash_branch_#{namespace}_#{segment}", 1) do |r|
49
51
  loc = method(routes[segment]).source_location
50
52
  require file
@@ -68,7 +70,11 @@ class Roda
68
70
 
69
71
  # Eagerly load all hash branches when freezing the application.
70
72
  def freeze
71
- opts.delete(:autoload_hash_branch_files).each{|file| require file} unless opts.frozen?
73
+ unless opts.frozen?
74
+ autoload_hash_branch_files.each{|file| require file}
75
+ opts[:autoload_hash_branch_files] = nil
76
+ opts.delete(:autoload_hash_branch_files)
77
+ end
72
78
  super
73
79
  end
74
80
  end
@@ -35,12 +35,14 @@ class Roda
35
35
  end
36
36
 
37
37
  module ClassMethods
38
+ RodaPlugins.opt_attr_reader(self, :autoload_named_route_files)
39
+
38
40
  # Autoload the given file when there is request for the named route.
39
41
  # The given file should configure the named route specified.
40
42
  def autoload_named_route(namespace=nil, name, file)
41
43
  file = File.expand_path(file)
42
- opts[:autoload_named_route_files] << file
43
- routes = opts[:namespaced_routes][namespace] ||= {}
44
+ autoload_named_route_files << file
45
+ routes = namespaced_routes[namespace] ||= {}
44
46
  meth = routes[name] = define_roda_method(routes[name] || "named_routes_#{namespace}_#{name}", 1) do |r|
45
47
  loc = method(routes[name]).source_location
46
48
  require file
@@ -54,7 +56,11 @@ class Roda
54
56
 
55
57
  # Eagerly load all autoloaded named routes when freezing the application.
56
58
  def freeze
57
- opts.delete(:autoload_named_route_files).each{|file| require file} unless opts.frozen?
59
+ unless opts.frozen?
60
+ autoload_named_route_files.each{|file| require file}
61
+ opts[:autoload_named_route_files] = nil
62
+ opts.delete(:autoload_named_route_files)
63
+ end
58
64
  super
59
65
  end
60
66
  end
@@ -51,6 +51,10 @@ class Roda
51
51
  app.opts[:capture_erb_returns] = opts[:returns] if opts.has_key?(:returns)
52
52
  end
53
53
 
54
+ module ClassMethods
55
+ RodaPlugins.opt_attr_reader(self, :capture_erb_returns)
56
+ end
57
+
54
58
  module InstanceMethods
55
59
  # Temporarily replace the ERB output buffer
56
60
  # with an empty string, and then yield to the block.
@@ -71,7 +75,7 @@ class Roda
71
75
  if buf_was.respond_to?(:capture) && !buf_was.instance_of?(String)
72
76
  buf_was.capture(&block)
73
77
  else
74
- returns = opts.fetch(:returns) { self.opts[:capture_erb_returns] }
78
+ returns = opts.fetch(:returns) { self.class.capture_erb_returns }
75
79
 
76
80
  begin
77
81
  instance_variable_set(outvar, String.new)
@@ -162,7 +162,7 @@ class Roda
162
162
  app.opts[:chunk_by_default] = opts[:chunk_by_default]
163
163
  app.opts[:force_chunked_encoding] = opts[:force_chunked_encoding]
164
164
  if opts[:headers]
165
- app.opts[:chunk_headers] = (app.opts[:chunk_headers] || {}).merge(opts[:headers]).freeze
165
+ app.opts[:chunk_headers] = (app.chunk_headers || {}).merge(opts[:headers]).freeze
166
166
  end
167
167
  end
168
168
 
@@ -207,6 +207,12 @@ class Roda
207
207
  end
208
208
  end
209
209
 
210
+ module ClassMethods
211
+ RodaPlugins.opt_attr_reader(self, :chunk_by_default, name: :chunk_by_default?)
212
+ RodaPlugins.opt_attr_reader(self, :force_chunked_encoding, name: :force_chunked_encoding?)
213
+ RodaPlugins.opt_attr_reader(self, :chunk_headers)
214
+ end
215
+
210
216
  module InstanceMethods
211
217
  # Disable chunking for the current request. Mostly useful when
212
218
  # chunking is turned on by default.
@@ -217,7 +223,7 @@ class Roda
217
223
  # If chunking by default, call chunked if it hasn't yet been
218
224
  # called and chunking is not specifically disabled.
219
225
  def view(*a)
220
- if opts[:chunk_by_default] && @_chunked != false && !defined?(yield)
226
+ if self.class.chunk_by_default? && @_chunked != false && !defined?(yield)
221
227
  chunked(*a)
222
228
  else
223
229
  super
@@ -228,7 +234,7 @@ class Roda
228
234
  # an overview. If a block is given, it is passed to #delay.
229
235
  def chunked(template, opts=OPTS, &block)
230
236
  if @_chunked.nil?
231
- @_chunked = !self.opts[:force_chunked_encoding] || @_request.http_version == "HTTP/1.1"
237
+ @_chunked = !self.class.force_chunked_encoding? || @_request.http_version == "HTTP/1.1"
232
238
  end
233
239
 
234
240
  if block
@@ -255,10 +261,10 @@ class Roda
255
261
 
256
262
  res = response
257
263
  headers = res.headers
258
- if chunk_headers = self.opts[:chunk_headers]
264
+ if chunk_headers = self.class.chunk_headers
259
265
  headers.merge!(chunk_headers)
260
266
  end
261
- if self.opts[:force_chunked_encoding]
267
+ if self.class.force_chunked_encoding?
262
268
  res[RodaResponseHeaders::TRANSFER_ENCODING] = 'chunked'
263
269
  body = Body.new(self)
264
270
  else
@@ -60,17 +60,19 @@ class Roda
60
60
  end
61
61
 
62
62
  module ClassMethods
63
+ RodaPlugins.opt_attr_reader(self, :class_level_routes)
64
+
63
65
  # Define routing methods that will store class level routes.
64
66
  [:root, :on, :is, :get, :post, :delete, :head, :options, :link, :patch, :put, :trace, :unlink].each do |request_meth|
65
67
  define_method(request_meth) do |*args, &block|
66
68
  meth = define_roda_method("class_level_routing_#{request_meth}", :any, &block)
67
- opts[:class_level_routes] << [request_meth, args, meth].freeze
69
+ class_level_routes << [request_meth, args, meth].freeze
68
70
  end
69
71
  end
70
72
 
71
73
  # Freeze the class level routes so that there can be no thread safety issues at runtime.
72
74
  def freeze
73
- opts[:class_level_routes].freeze
75
+ class_level_routes.freeze
74
76
  super
75
77
  end
76
78
  end
@@ -93,7 +95,7 @@ class Roda
93
95
  @_response.status = nil
94
96
  result.replace(_roda_handle_route do
95
97
  r = @_request
96
- opts[:class_level_routes].each do |request_meth, args, meth|
98
+ self.class.class_level_routes.each do |request_meth, args, meth|
97
99
  r.instance_variable_set(:@remaining_path, @_original_remaining_path)
98
100
  r.public_send(request_meth, *args) do |*a|
99
101
  send(meth, *a)
@@ -93,6 +93,8 @@ class Roda
93
93
  end
94
94
 
95
95
  module ClassMethods
96
+ RodaPlugins.opt_attr_reader(self, :class_matchers)
97
+
96
98
  # Set the matcher and block to use for the given class.
97
99
  # The matcher can be a regexp, registered class matcher, or registered symbol
98
100
  # matcher (if using the symbol_matchers plugin).
@@ -119,7 +121,7 @@ class Roda
119
121
 
120
122
  # Freeze the class_matchers hash when freezing the app.
121
123
  def freeze
122
- opts[:class_matchers].freeze
124
+ class_matchers.freeze
123
125
  super
124
126
  end
125
127
  end
@@ -26,8 +26,8 @@ class Roda
26
26
  private_constant :MUTATE_LINE
27
27
 
28
28
  def self.configure(app, logger=nil, opts=OPTS)
29
- app.opts[:common_logger] = logger || app.opts[:common_logger] || $stderr
30
- app.opts[:common_logger_meth] = app.opts[:common_logger].method(opts.fetch(:method){logger.respond_to?(:write) ? :write : :<<})
29
+ common_logger = app.opts[:common_logger] = logger || app.opts[:common_logger] || $stderr # RODA4: drop setting of option, no longer used
30
+ app.opts[:common_logger_meth] = common_logger.method(opts.fetch(:method){logger.respond_to?(:write) ? :write : :<<})
31
31
  end
32
32
 
33
33
  if RUBY_VERSION >= '2.1'
@@ -43,6 +43,10 @@ class Roda
43
43
  # simplecov:enable
44
44
  end
45
45
 
46
+ module ClassMethods
47
+ RodaPlugins.opt_attr_reader(self, :common_logger_meth)
48
+ end
49
+
46
50
  module InstanceMethods
47
51
  private
48
52
 
@@ -67,7 +71,7 @@ class Roda
67
71
  # simplecov:enable
68
72
  end
69
73
  line[-1] = "\n"
70
- opts[:common_logger_meth].call(line)
74
+ self.class.common_logger_meth.call(line)
71
75
  end
72
76
 
73
77
  # Create timer instance used for timing
@@ -23,7 +23,7 @@ class Roda
23
23
  # a request method for whether sessions are allowed.
24
24
  def self.load_dependencies(app, opts=OPTS, &block)
25
25
  app.plugin :sessions, opts
26
- app::RodaRequest.class_eval do
26
+ app::RodaRequest.class_exec do
27
27
  define_method(:use_sessions?, &block)
28
28
  alias use_sessions? use_sessions?
29
29
  end
@@ -69,13 +69,17 @@ class Roda
69
69
  app.opts[:append_content_for] = opts.fetch(:append, true)
70
70
  end
71
71
 
72
+ module ClassMethods
73
+ RodaPlugins.opt_attr_reader(self, :append_content_for, name: :append_content_for?)
74
+ end
75
+
72
76
  module InstanceMethods
73
77
  # If called with a block, store content enclosed by block
74
78
  # under the given key. If called without a block, retrieve
75
79
  # stored content with the given key, or return nil if there
76
80
  # is no content stored with that key.
77
81
  def content_for(key, value=nil, &block)
78
- append = opts[:append_content_for]
82
+ append = self.class.append_content_for?
79
83
 
80
84
  if block || value
81
85
  if block
@@ -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.opts[:content_security_policy].dup
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.opts[:content_security_policy]).set_header(headers)
329
+ (@content_security_policy || roda_class.content_security_policy).set_header(headers)
326
330
  end
327
331
  end
328
332
  end
@@ -61,7 +61,7 @@ class Roda
61
61
  end
62
62
 
63
63
  def self.configure(app, opts=OPTS)
64
- previous = app.opts[:cookie_flags] || DEFAULTS
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.opts[:cookie_flags]
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]
@@ -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.opts[:cookies_opts]||{}).merge(opts).freeze
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.opts[:cookies_opts].merge(value))
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.opts[:cookies_opts].merge(value))
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
- opts[:custom_block_results].freeze
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
- opts[:custom_block_results][klass] = define_roda_method(opts[:custom_block_results][klass] || "custom_block_result_#{klass}", 1, &block)
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.opts[:custom_block_results].each do |klass, meth|
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[opts[:custom_matchers]]
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.opts[:custom_matchers].each do |match_class, meth|
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
- # The default response headers to use for the current class.
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 = opts[:default_headers]).all?{|k, v| k.is_a?(String) && v.is_a?(String)} &&
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}"