roda 3.33.0 → 3.38.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f9ede53406b16bff69c2115f7d35e808835e300ca866a5caadde9b9fad9d206
4
- data.tar.gz: f78eb239a3d15e83879b8a7272f07959a00b6d94e93595f6871841144d3d718b
3
+ metadata.gz: 5b28900f838d40c98d868225a5d2f0e1fcfd24ccf0bdbf770f0ccb79ac2ba2fe
4
+ data.tar.gz: 8f2c37b04485af74a1e8c2c68f5eeb31b0531d82b4f55c6df4145cfe344cf86f
5
5
  SHA512:
6
- metadata.gz: 45e3020a3509bacc5f39107659fca5e64ba25c322c18af6a62b976308f5bac2ac44a18d54bc82d9b1d4260e260883208ce8210ac2fb660e09b63b65401abfed6
7
- data.tar.gz: 5a00af14f4d6e01c472a23b59f5a51eec66fc55c2d8d34069de869d7ff487913ff6ab1d21023b8207f5fdf18ad6354f30520b3cf89d2491de4b330c1ae30f10a
6
+ metadata.gz: e2cb67bbc5509acba608530dc8641059cb4aa450f8cf0bdca82f57dabba48c751a50e770f902b0ed64da63d2988c89884e074991a49956d03188ea8efaab7793
7
+ data.tar.gz: 66a2ad67a5f4abd5e275f77eddde74f4dd6430c222cf208d9aafc01577fbfae548367f8b71864834cb4067ae530ece68df66165c4352bfc151b35bee8c5e2041
data/CHANGELOG CHANGED
@@ -1,3 +1,33 @@
1
+ = 3.38.0 (2020-11-16)
2
+
3
+ * Make error_email and error_mail plugins rescue invalid parameter errors when preparing the email body (jeremyevans)
4
+
5
+ = 3.37.0 (2020-10-16)
6
+
7
+ * Add custom_matchers plugin, for supporting arbitrary objects as matchers (jeremyevans)
8
+
9
+ = 3.36.0 (2020-09-14)
10
+
11
+ * Add multi_public plugin, for serving files from multiple public directories (jeremyevans)
12
+
13
+ * Support report-to directive in the content_security_policy plugin (jeremyevans)
14
+
15
+ * Add Vary response header when using type_routing plugin with Accept request header to prevent caching issues (jeremyevans)
16
+
17
+ = 3.35.0 (2020-08-14)
18
+
19
+ * Add r plugin for r method for accessing request, useful when r local variable is not in scope (jeremyevans)
20
+
21
+ * Warn when loading a plugin with arguments or a block if the plugin does not accept arguments or block (jeremyevans)
22
+
23
+ = 3.34.0 (2020-07-14)
24
+
25
+ * Remove unnecessary conditionals (jeremyevans)
26
+
27
+ * Allow loading the match_affix plugin with a single argument (jeremyevans)
28
+
29
+ * Do not include pre/post context sections if empty in the exception_page plugin (jeremyevans)
30
+
1
31
  = 3.33.0 (2020-06-16)
2
32
 
3
33
  * Add :brotli option to public plugin to supplement it to serve brotli-compressed files like :gzip does for gzipped files (hmdne) (#194)
@@ -0,0 +1,17 @@
1
+ = Improvements
2
+
3
+ * Multiple unneeded conditionals have been removed.
4
+
5
+ * pre_content and post_context sections in backtraces are no longer
6
+ included in the exception_page plugin output if they would be
7
+ empty.
8
+
9
+ * The match_affix plugin can be loaded again with a single argument.
10
+ It was originally designed to accept a single argument, but a bug
11
+ introduced in 2.29.0 made it require two arguments.
12
+
13
+ * Core Roda and all plugins that ship with Roda now have 100% branch
14
+ coverage.
15
+
16
+ * The sinatra_helpers plugin no longer emits statement not reached
17
+ warnings in verbose mode.
@@ -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,5 @@
1
+ = Improvements
2
+
3
+ * The error_email and error_mail plugins now rescue invalid parameter
4
+ errors when preparing the email body, because you generally don't
5
+ want your error handler to raise an exception.
@@ -272,6 +272,12 @@ class Roda
272
272
  raise RodaError, "Cannot add a plugin to a frozen Roda class" if frozen?
273
273
  plugin = RodaPlugins.load_plugin(plugin) if plugin.is_a?(Symbol)
274
274
  raise RodaError, "Invalid plugin type: #{plugin.class.inspect}" unless plugin.is_a?(Module)
275
+
276
+ if !plugin.respond_to?(:load_dependencies) && !plugin.respond_to?(:configure) && (!args.empty? || block)
277
+ # RODA4: switch from warning to error
278
+ 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.")
279
+ end
280
+
275
281
  plugin.load_dependencies(self, *args, &block) if plugin.respond_to?(:load_dependencies)
276
282
  include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods)
277
283
  extend(plugin::ClassMethods) if defined?(plugin::ClassMethods)
@@ -34,7 +34,9 @@ class Roda
34
34
  module AllVerbs
35
35
  module RequestMethods
36
36
  %w'delete head options link patch put trace unlink'.each do |verb|
37
+ # :nocov:
37
38
  if ::Rack::Request.method_defined?("#{verb}?")
39
+ # :nocov:
38
40
  class_eval(<<-END, __FILE__, __LINE__+1)
39
41
  def #{verb}(*args, &block)
40
42
  _verb(args, &block) if #{verb}?
@@ -33,9 +33,7 @@ class Roda
33
33
  # elements. If the remaining elements could not be
34
34
  # matched, reset the state and continue to the next
35
35
  # entry in the array.
36
- def _match_array(arg, rest=nil)
37
- return super unless rest
38
-
36
+ def _match_array(arg, rest)
39
37
  path = @remaining_path
40
38
  captures = @captures
41
39
  caps = captures.dup
@@ -56,7 +56,6 @@ class Roda
56
56
  # currently have a routing block, setup an empty routing block so that things will still work if
57
57
  # a routing block isn't added.
58
58
  def self.configure(app)
59
- app.route{} unless app.route_block
60
59
  app.opts[:class_level_routes] ||= []
61
60
  end
62
61
 
@@ -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
- if args.empty?
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
+
@@ -1,6 +1,8 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ # :nocov:
3
4
  raise LoadError, "disallow_file_uploads plugin not supported on Rack <1.6" if Rack.release < '1.6'
5
+ # :nocov:
4
6
 
5
7
  #
6
8
  class Roda
@@ -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
- unless s.request.params.empty?
83
+ if params
77
84
  message << <<END
78
85
 
79
86
  Params:
80
87
 
81
- #{format[s.request.params]}
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
- unless request.params.empty?
101
+ if params
95
102
  message << <<END
96
103
 
97
104
  Params:
98
105
 
99
- #{format[request.params]}
106
+ #{params}
100
107
  END
101
108
  end
102
109
 
@@ -252,11 +252,21 @@ END
252
252
  begin
253
253
  lineno -= 1
254
254
  lines = ::File.readlines(filename)
255
- pre_lineno = frame[:pre_context_lineno] = [lineno-context, 0].max
256
- frame[:pre_context] = lines[pre_lineno...lineno]
257
- frame[:context_line] = lines[lineno].chomp
258
- post_lineno = frame[:post_context_lineno] = [lineno+context, lines.size].min
259
- frame[:post_context] = lines[lineno+1..post_lineno]
255
+ if line = lines[lineno]
256
+ pre_lineno = [lineno-context, 0].max
257
+ if (pre_context = lines[pre_lineno...lineno]) && !pre_context.empty?
258
+ frame[:pre_context_lineno] = pre_lineno
259
+ frame[:pre_context] = pre_context
260
+ end
261
+
262
+ post_lineno = [lineno+context, lines.size].min
263
+ if (post_context = lines[lineno+1..post_lineno]) && !post_context.empty?
264
+ frame[:post_context_lineno] = post_lineno
265
+ frame[:post_context] = post_context
266
+ end
267
+
268
+ frame[:context_line] = line.chomp
269
+ end
260
270
  rescue
261
271
  end
262
272
 
@@ -28,7 +28,7 @@ class Roda
28
28
  #
29
29
  # This plugin automatically loads the placeholder_string_matchers plugin.
30
30
  module MatchAffix
31
- def self.load_dependencies(app, _prefix, _suffix)
31
+ def self.load_dependencies(app, _prefix, _suffix=nil)
32
32
  app.plugin :placeholder_string_matchers
33
33
  end
34
34
 
@@ -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
+
@@ -104,7 +104,9 @@ class Roda
104
104
  # arguments, record the verb used. If given an argument, add an is
105
105
  # check with the arguments.
106
106
  %w'get post delete head options link patch put trace unlink'.each do |verb|
107
+ # :nocov:
107
108
  if ::Rack::Request.method_defined?("#{verb}?")
109
+ # :nocov:
108
110
  class_eval(<<-END, __FILE__, __LINE__+1)
109
111
  def #{verb}(*args, &block)
110
112
  if (empty = args.empty?) && @_is_verbs
@@ -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
- if is_get?
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
@@ -110,9 +112,7 @@ class Roda
110
112
  headers = res[1]
111
113
 
112
114
  unless res[0] == 304
113
- if mime_type = ::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
114
- headers['Content-Type'] = mime_type
115
- end
115
+ headers['Content-Type'] = ::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
116
116
  headers['Content-Encoding'] = encoding
117
117
  end
118
118
 
@@ -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
@@ -287,7 +287,9 @@ class Roda
287
287
  false
288
288
  end
289
289
 
290
+ # :nocov:
290
291
  if COMPILED_METHOD_SUPPORT
292
+ # :nocov:
291
293
  # Compile a method in the given module with the given name that will
292
294
  # call the compiled template method, updating the compiled template method
293
295
  def define_compiled_method(roda_class, method_name, locals_keys=EMPTY_ARRAY)
@@ -337,7 +339,9 @@ class Roda
337
339
  def inherited(subclass)
338
340
  super
339
341
  opts = subclass.opts[:render] = subclass.opts[:render].dup
342
+ # :nocov:
340
343
  if COMPILED_METHOD_SUPPORT
344
+ # :nocov:
341
345
  opts[:template_method_cache] = (opts[:cache_class] || RodaCache).new
342
346
  end
343
347
  opts[:cache] = opts[:cache].dup
@@ -43,7 +43,9 @@ class Roda
43
43
  module InstanceMethods
44
44
  private
45
45
 
46
+ # :nocov:
46
47
  if Render::COMPILED_METHOD_SUPPORT
48
+ # :nocov:
47
49
  # Disable use of cached templates, since it assumes a render/view call with no
48
50
  # options will have no locals.
49
51
  def _cached_template_method(template)
@@ -374,7 +374,7 @@ class Roda
374
374
 
375
375
  module ResponseMethods
376
376
  # Set or retrieve the response status code.
377
- def status(value = (return @status; nil))
377
+ def status(value = nil || (return @status))
378
378
  @status = value
379
379
  end
380
380
 
@@ -401,7 +401,7 @@ class Roda
401
401
 
402
402
  # Set multiple response headers with Hash, or return the headers if no
403
403
  # argument is given.
404
- def headers(hash = (return @headers; nil))
404
+ def headers(hash = nil || (return @headers))
405
405
  @headers.merge!(hash)
406
406
  end
407
407
 
@@ -412,7 +412,7 @@ class Roda
412
412
 
413
413
  # Set the Content-Type of the response body given a media type or file
414
414
  # extension. See plugin documentation for options.
415
- def content_type(type = (return @headers["Content-Type"]; nil), opts = OPTS)
415
+ def content_type(type = nil || (return @headers["Content-Type"]), opts = OPTS)
416
416
  unless (mime_type = mime_type(type) || opts[:default])
417
417
  raise RodaError, "Unknown media type: #{type}"
418
418
  end
@@ -478,7 +478,7 @@ class Roda
478
478
  # If a type and value are given, set the value in Rack's MIME registry.
479
479
  # If only a type is given, lookup the type in Rack's MIME registry and
480
480
  # return it.
481
- def mime_type(type=(return; nil), value = nil)
481
+ def mime_type(type=nil || (return), value = nil)
482
482
  return type.to_s if type.to_s.include?('/')
483
483
  type = ".#{type}" unless type.to_s[0] == ?.
484
484
  if value
@@ -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: {
@@ -195,6 +199,7 @@ class Roda
195
199
  @env['HTTP_ACCEPT'].to_s.split(/\s*,\s*/).map do |part|
196
200
  mime, _= part.split(/\s*;\s*/, 2)
197
201
  if sym = mimes[mime]
202
+ response['Vary'] = (vary = response['Vary']) ? "#{vary}, Accept" : 'Accept'
198
203
  return sym
199
204
  end
200
205
  end
@@ -609,10 +609,9 @@ class Roda
609
609
  when nil
610
610
  keys = (0...@obj.length)
611
611
 
612
- valid = case @obj
613
- when Array
612
+ valid = if @obj.is_a?(Array)
614
613
  true
615
- when Hash
614
+ else
616
615
  keys = keys.map(&:to_s)
617
616
  keys.all?{|k| @obj.has_key?(k)}
618
617
  end
@@ -742,9 +741,7 @@ class Roda
742
741
  return
743
742
  end
744
743
 
745
- if v = self[key]
746
- v.subkey(keys, do_raise)
747
- end
744
+ self[key].subkey(keys, do_raise)
748
745
  rescue => e
749
746
  handle_error(key, reason, e)
750
747
  end
@@ -808,10 +805,10 @@ class Roda
808
805
  @nested_params = nil
809
806
 
810
807
  if capturing_started
811
- # Unset capturing if capturing was not already started.
808
+ # Unset capturing if capturing was already started.
812
809
  @capture = nil
813
810
  else
814
- # If capturing was already started, update cached nested params
811
+ # If capturing was not already started, update cached nested params
815
812
  # before resetting symbolize setting.
816
813
  @nested_params = nested_params
817
814
  end
@@ -878,7 +875,7 @@ class Roda
878
875
  def handle_error(key, reason, e, do_raise=false)
879
876
  case e
880
877
  when String
881
- handle_error(key, reason, Error.new(e), do_raise=false)
878
+ handle_error(key, reason, Error.new(e), do_raise)
882
879
  when Error, ArgumentError
883
880
  if @capture && (le = @capture.last) && le == e
884
881
  raise e if do_raise
@@ -126,7 +126,9 @@ class Roda
126
126
 
127
127
  private
128
128
 
129
+ # :nocov:
129
130
  if Render::COMPILED_METHOD_SUPPORT
131
+ # :nocov:
130
132
  # Return nil if using custom view or layout options.
131
133
  # If using a view subdir, prefix the template key with the subdir.
132
134
  def _cached_template_method_key(template)
@@ -466,10 +466,8 @@ class Roda
466
466
  rp = @remaining_path
467
467
  if rp.getbyte(0) == 47
468
468
  if last = rp.index('/', 1)
469
- if last > 1
470
- @captures << rp[1, last-1]
471
- @remaining_path = rp[last, rp.length]
472
- end
469
+ @captures << rp[1, last-1]
470
+ @remaining_path = rp[last, rp.length]
473
471
  elsif rp.length > 1
474
472
  @captures << rp[1,rp.length]
475
473
  @remaining_path = ""
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 33
7
+ RodaMinorVersion = 38
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
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.33.0
4
+ version: 3.38.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-06-16 00:00:00.000000000 Z
11
+ date: 2020-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -209,6 +209,11 @@ extra_rdoc_files:
209
209
  - doc/release_notes/3.31.0.txt
210
210
  - doc/release_notes/3.32.0.txt
211
211
  - doc/release_notes/3.33.0.txt
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
212
217
  files:
213
218
  - CHANGELOG
214
219
  - MIT-LICENSE
@@ -243,6 +248,11 @@ files:
243
248
  - doc/release_notes/3.31.0.txt
244
249
  - doc/release_notes/3.32.0.txt
245
250
  - doc/release_notes/3.33.0.txt
251
+ - doc/release_notes/3.34.0.txt
252
+ - doc/release_notes/3.35.0.txt
253
+ - doc/release_notes/3.36.0.txt
254
+ - doc/release_notes/3.37.0.txt
255
+ - doc/release_notes/3.38.0.txt
246
256
  - doc/release_notes/3.4.0.txt
247
257
  - doc/release_notes/3.5.0.txt
248
258
  - doc/release_notes/3.6.0.txt
@@ -269,6 +279,7 @@ files:
269
279
  - lib/roda/plugins/content_security_policy.rb
270
280
  - lib/roda/plugins/cookies.rb
271
281
  - lib/roda/plugins/csrf.rb
282
+ - lib/roda/plugins/custom_matchers.rb
272
283
  - lib/roda/plugins/default_headers.rb
273
284
  - lib/roda/plugins/default_status.rb
274
285
  - lib/roda/plugins/delay_build.rb
@@ -303,6 +314,7 @@ files:
303
314
  - lib/roda/plugins/middleware.rb
304
315
  - lib/roda/plugins/middleware_stack.rb
305
316
  - lib/roda/plugins/module_include.rb
317
+ - lib/roda/plugins/multi_public.rb
306
318
  - lib/roda/plugins/multi_route.rb
307
319
  - lib/roda/plugins/multi_run.rb
308
320
  - lib/roda/plugins/multi_view.rb
@@ -322,6 +334,7 @@ files:
322
334
  - lib/roda/plugins/placeholder_string_matchers.rb
323
335
  - lib/roda/plugins/precompile_templates.rb
324
336
  - lib/roda/plugins/public.rb
337
+ - lib/roda/plugins/r.rb
325
338
  - lib/roda/plugins/relative_path.rb
326
339
  - lib/roda/plugins/render.rb
327
340
  - lib/roda/plugins/render_each.rb
@@ -379,7 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
379
392
  - !ruby/object:Gem::Version
380
393
  version: '0'
381
394
  requirements: []
382
- rubygems_version: 3.1.2
395
+ rubygems_version: 3.1.4
383
396
  signing_key:
384
397
  specification_version: 4
385
398
  summary: Routing tree web toolkit