roda 3.81.0 → 3.82.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: 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