roda 3.81.0 → 3.82.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9d94f31e568bd2774c3e582152f0bcdbffdf3fcd7e5a95241e73b3fd5b7ac3c
4
- data.tar.gz: b9ad44642acdbaa0035cbd6ece521ca3432d2e7c23d75a331172ecd6c6288c60
3
+ metadata.gz: bdf5797c96af1d28dc4d13f0c95ac0468d60eb30b6a128b2cee2c26a08eb0a7f
4
+ data.tar.gz: d687b3cd03657bdcfda6f87a7a7a2a3b55e9a0c4f856cabd3d080865cc7d9ad2
5
5
  SHA512:
6
- metadata.gz: 1cd64e84b47a1a7a01d9165d16e14d95d2c43287b58f2557d9492a201832c1388157152eab1fd12052c7691e3969e5ce1cb9c0ad706e73ad6de822dcc29e1590
7
- data.tar.gz: 86320c603d0c9b90e2c82f16cc5229b76afe66dde5bdbccce9b28ce076e80808a35410098fe0eae5cb3f5b3180b79f4265b3d525e7861a9dd65890315d274fad
6
+ metadata.gz: e518b9638e98713ed54fd4c609069edf81a81987adf53ce2b7a09fa7110244f67b109cbe91c9c03d713f824900649d1c4b2ad435823852dcdfd01094f506afae
7
+ data.tar.gz: '00877a1338b70a5b96dab7fb946e21c7c916c776666286f80251b362e29ca6e040ffeb9b7a7258cf3af4c9246de5a6c35c5332385bd3cb926505776102c9112c'
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ = 3.82.0 (2024-07-12)
2
+
3
+ * Add :encodings option to public plugin to support configurable encoding order (jeremyevans)
4
+
5
+ * Add :zstd option to public plugin to supplement it to serve zstd-compressed files with .zst extension (jeremyevans)
6
+
7
+ * Make capture_erb plugin call integrate better with erubi/capture_block (jeremyevans)
8
+
1
9
  = 3.81.0 (2024-06-12)
2
10
 
3
11
  * Make assets plugin :early_hints option follow Rack 3 SPEC if using Rack 3 (jeremyevans)
@@ -0,0 +1,43 @@
1
+ = New Features
2
+
3
+ * A :zstd option has been added to the public and multi_public
4
+ plugins to support serving zstd-compressed files with a .zst
5
+ extension. This option is similar to the existing :gzip and
6
+ :brotli plugin options. Chrome started supporting zstd encoding
7
+ in March.
8
+
9
+ * An :encodings option has been added to the public and multi_public
10
+ plugins, for more control over how encodings are handled. This
11
+ allows for changing the order in which encodings are attempted, the
12
+ use of custom encodings, and the use of different file extensions
13
+ for encodings. Example:
14
+
15
+ plugin :public, encodings: {'zstd'=>'.zst', 'deflate'=>'.deflate'}
16
+
17
+ If the :encodings option is not provided, the :zstd, :brotli, and
18
+ :gzip options are used to build an equivalent :encodings option.
19
+
20
+ = Other Improvements
21
+
22
+ * The capture_erb plugin now integrates better when using
23
+ erubi/capture_block for <%= method do %> support in ERB templates,
24
+ using the native capture method provided by the buffer object.
25
+
26
+ * Encoding handling has been more optimized in the public plugin.
27
+ Regexps for the encodings are precomputed, avoiding a regexp
28
+ allocation per request per encoding attempted. On Ruby 2.4+
29
+ Regexp#match? is used for better performance. If the
30
+ Accept-Encoding header is not present, no encoding matching
31
+ is attemped.
32
+
33
+ = Backwards Compatibility
34
+
35
+ * The private public_serve_compressed request method in the public
36
+ plugin now assumes it is called after the encoding is already
37
+ valid. If you are calling this method in your own code, you now
38
+ need to perform checks to make sure the client can accept the
39
+ encoding before calling this method.
40
+
41
+ * The :public_gzip and :public_brotli application options are no
42
+ longer set by the public plugin. The :public_encodings option
43
+ is now set.
@@ -15,6 +15,11 @@ class Roda
15
15
  # inside templates. It can be combined with the inject_erb plugin
16
16
  # to wrap template blocks with arbitrary output and then inject the
17
17
  # wrapped output into the template.
18
+ #
19
+ # If the output buffer object responds to +capture+ (e.g. when
20
+ # +erubi/capture_block+ is being used as the template engine),
21
+ # this will call +capture+ on the output buffer object, instead
22
+ # of setting the output buffer object temporarily to a new object.
18
23
  module CaptureERB
19
24
  def self.load_dependencies(app)
20
25
  app.plugin :render
@@ -25,13 +30,20 @@ class Roda
25
30
  # with an empty string, and then yield to the block.
26
31
  # Return the value of the block, converted to a string.
27
32
  # Restore the previous ERB output buffer before returning.
28
- def capture_erb
33
+ def capture_erb(&block)
29
34
  outvar = render_opts[:template_opts][:outvar]
30
35
  buf_was = instance_variable_get(outvar)
31
- instance_variable_set(outvar, String.new)
32
- yield.to_s
33
- ensure
34
- instance_variable_set(outvar, buf_was) if outvar && buf_was
36
+
37
+ if buf_was.respond_to?(:capture)
38
+ buf_was.capture(&block)
39
+ else
40
+ begin
41
+ instance_variable_set(outvar, String.new)
42
+ yield.to_s
43
+ ensure
44
+ instance_variable_set(outvar, buf_was) if outvar && buf_was
45
+ end
46
+ end
35
47
  end
36
48
  end
37
49
  end
@@ -3,7 +3,7 @@
3
3
  #
4
4
  class Roda
5
5
  module RodaPlugins
6
- # The response_headers_plain_hash plugin will change Roda to
6
+ # The plain_hash_response_headers plugin will change Roda to
7
7
  # use a plain hash for response headers. This is Roda's
8
8
  # default behavior on Rack 2, but on Rack 3+, Roda defaults
9
9
  # to using Rack::Headers for response headers for backwards
@@ -44,15 +44,30 @@ class Roda
44
44
  SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact)
45
45
  PARSER = URI::DEFAULT_PARSER
46
46
  RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
47
+ ENCODING_MAP = {:zstd=>'zstd', :brotli=>'br', :gzip=>'gzip'}.freeze
48
+ ENCODING_EXTENSIONS = {'br'=>'.br', 'gzip'=>'.gz', 'zstd'=>'.zst'}.freeze
49
+
50
+ # :nocov:
51
+ MATCH_METHOD = RUBY_VERSION >= '2.4' ? :match? : :match
52
+ # :nocov:
47
53
 
48
54
  # Use options given to setup a Rack::File instance for serving files. Options:
55
+ # :brotli :: Whether to serve already brotli-compressed files with a .br extension
56
+ # for clients supporting "br" transfer encoding.
49
57
  # :default_mime :: The default mime type to use if the mime type is not recognized.
58
+ # :encodings :: An enumerable of pairs to handle accepted encodings. The first
59
+ # element of the pair is the accepted encoding name (e.g. 'gzip'),
60
+ # and the second element of the pair is the file extension (e.g.
61
+ # '.gz'). This allows configuration of the order in which encodings
62
+ # are tried, to prefer brotli to zstd for example, or to support
63
+ # encodings other than zstd, brotli, and gzip. This takes
64
+ # precedence over the :brotli, :gzip, and :zstd options if given.
50
65
  # :gzip :: Whether to serve already gzipped files with a .gz extension for clients
51
- # supporting gzipped transfer encoding.
52
- # :brotli :: Whether to serve already brotli-compressed files with a .br extension
53
- # for clients supporting brotli transfer encoding.
66
+ # supporting "gzip" transfer encoding.
54
67
  # :headers :: A hash of headers to use for statically served files
55
68
  # :root :: Use this option for the root of the public directory (default: "public")
69
+ # :zstd :: Whether to serve already zstd-compressed files with a .zst extension
70
+ # for clients supporting "zstd" transfer encoding.
56
71
  def self.configure(app, opts={})
57
72
  if opts[:root]
58
73
  app.opts[:public_root] = app.expand_path(opts[:root])
@@ -60,8 +75,18 @@ class Roda
60
75
  app.opts[:public_root] = app.expand_path("public")
61
76
  end
62
77
  app.opts[:public_server] = RACK_FILES.new(app.opts[:public_root], opts[:headers]||{}, opts[:default_mime] || 'text/plain')
63
- app.opts[:public_gzip] = opts[:gzip]
64
- app.opts[:public_brotli] = opts[:brotli]
78
+
79
+ unless encodings = opts[:encodings]
80
+ if ENCODING_MAP.any?{|k,| opts.has_key?(k)}
81
+ encodings = ENCODING_MAP.map{|k, v| [v, ENCODING_EXTENSIONS[v]] if opts[k]}.compact
82
+ end
83
+ end
84
+ encodings = (encodings || app.opts[:public_encodings] || EMPTY_ARRAY).map(&:dup).freeze
85
+ encodings.each do |a|
86
+ a << /\b#{a[0]}\b/
87
+ end
88
+ encodings.each(&:freeze)
89
+ app.opts[:public_encodings] = encodings
65
90
  end
66
91
 
67
92
  module RequestMethods
@@ -102,8 +127,13 @@ class Roda
102
127
  roda_opts = roda_class.opts
103
128
  path = ::File.join(server.root, *public_path_segments(path))
104
129
 
105
- public_serve_compressed(server, path, '.br', 'br') if roda_opts[:public_brotli]
106
- public_serve_compressed(server, path, '.gz', 'gzip') if roda_opts[:public_gzip]
130
+ if accept_encoding = env['HTTP_ACCEPT_ENCODING']
131
+ roda_opts[:public_encodings].each do |enc, ext, regexp|
132
+ if regexp.send(MATCH_METHOD, accept_encoding)
133
+ public_serve_compressed(server, path, ext, enc)
134
+ end
135
+ end
136
+ end
107
137
 
108
138
  if public_file_readable?(path)
109
139
  s, h, b = public_serve(server, path)
@@ -113,22 +143,22 @@ class Roda
113
143
  end
114
144
  end
115
145
 
146
+ # Serve the compressed file if it exists. This should only
147
+ # be called if the client will accept the related encoding.
116
148
  def public_serve_compressed(server, path, suffix, encoding)
117
- if env['HTTP_ACCEPT_ENCODING'] =~ /\b#{encoding}\b/
118
- compressed_path = path + suffix
149
+ compressed_path = path + suffix
119
150
 
120
- if public_file_readable?(compressed_path)
121
- s, h, b = public_serve(server, compressed_path)
122
- headers = response.headers
123
- headers.replace(h)
124
-
125
- unless s == 304
126
- headers[RodaResponseHeaders::CONTENT_TYPE] = ::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
127
- headers[RodaResponseHeaders::CONTENT_ENCODING] = encoding
128
- end
151
+ if public_file_readable?(compressed_path)
152
+ s, h, b = public_serve(server, compressed_path)
153
+ headers = response.headers
154
+ headers.replace(h)
129
155
 
130
- halt [s, headers, b]
156
+ unless s == 304
157
+ headers[RodaResponseHeaders::CONTENT_TYPE] = ::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
158
+ headers[RodaResponseHeaders::CONTENT_ENCODING] = encoding
131
159
  end
160
+
161
+ halt [s, headers, b]
132
162
  end
133
163
  end
134
164
 
@@ -148,6 +148,19 @@ class Roda
148
148
  # inject the content into the output. To get similar behavior with Roda, you have
149
149
  # a few different options you can use.
150
150
  #
151
+ # == Use Erubi::CaptureBlockEngine
152
+ #
153
+ # Roda defaults to using Erubi for erb template rendering. Erubi 1.13.0+ includes
154
+ # support for an erb variant that supports blocks in <tt><%=</tt> and <tt><%==</tt>
155
+ # tags. To use it:
156
+ #
157
+ # require 'erubi/capture_block'
158
+ # plugin :render, template_opts: {engine_class: Erubi::CaptureBlockEngine}
159
+ #
160
+ # See the Erubi documentation for how to capture data inside the block. Make sure
161
+ # the method call (+some_method+ in the example) returns the output you want added
162
+ # to the rendered body.
163
+ #
151
164
  # == Directly Inject Template Output
152
165
  #
153
166
  # You can switch from a <tt><%=</tt> tag to using a <tt><%</tt> tag:
data/lib/roda/version.rb CHANGED
@@ -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 = 81
7
+ RodaMinorVersion = 82
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.81.0
4
+ version: 3.82.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: 2024-06-12 00:00:00.000000000 Z
11
+ date: 2024-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -122,20 +122,6 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: sassc
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
125
  - !ruby/object:Gem::Dependency
140
126
  name: json
141
127
  requirement: !ruby/object:Gem::Requirement
@@ -256,6 +242,7 @@ extra_rdoc_files:
256
242
  - doc/release_notes/3.8.0.txt
257
243
  - doc/release_notes/3.80.0.txt
258
244
  - doc/release_notes/3.81.0.txt
245
+ - doc/release_notes/3.82.0.txt
259
246
  - doc/release_notes/3.9.0.txt
260
247
  files:
261
248
  - CHANGELOG
@@ -344,6 +331,7 @@ files:
344
331
  - doc/release_notes/3.8.0.txt
345
332
  - doc/release_notes/3.80.0.txt
346
333
  - doc/release_notes/3.81.0.txt
334
+ - doc/release_notes/3.82.0.txt
347
335
  - doc/release_notes/3.9.0.txt
348
336
  - lib/roda.rb
349
337
  - lib/roda/cache.rb
@@ -509,7 +497,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
509
497
  - !ruby/object:Gem::Version
510
498
  version: '0'
511
499
  requirements: []
512
- rubygems_version: 3.5.9
500
+ rubygems_version: 3.5.11
513
501
  signing_key:
514
502
  specification_version: 4
515
503
  summary: Routing tree web toolkit