roda 3.39.0 → 3.43.1
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/CHANGELOG +36 -0
- data/MIT-LICENSE +1 -1
- data/doc/release_notes/3.40.0.txt +24 -0
- data/doc/release_notes/3.41.0.txt +9 -0
- data/doc/release_notes/3.42.0.txt +21 -0
- data/doc/release_notes/3.43.0.txt +34 -0
- data/lib/roda.rb +6 -0
- data/lib/roda/cache.rb +7 -0
- data/lib/roda/plugins/assets.rb +31 -10
- data/lib/roda/plugins/common_logger.rb +3 -2
- data/lib/roda/plugins/custom_matchers.rb +2 -0
- data/lib/roda/plugins/default_headers.rb +15 -14
- data/lib/roda/plugins/host_authorization.rb +156 -0
- data/lib/roda/plugins/precompile_templates.rb +96 -21
- data/lib/roda/plugins/recheck_precompiled_assets.rb +107 -0
- data/lib/roda/plugins/render.rb +82 -14
- data/lib/roda/plugins/render_locals.rb +4 -0
- data/lib/roda/version.rb +2 -2
- metadata +21 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 95c0306a50aefb2cfa3bd700403ff2d8441acb78438dcca36bdb0341708208fe
|
|
4
|
+
data.tar.gz: df524e11cab19b33d6f4e2a139f2813b89dc0832863aa2e0ac70c69cc4f98e68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc0c531cdc7d9ae5d122cae6394b49ebd9014811c6dfba64ef70bc2b4ffe03d66efe2a10dd11f180a1c68db15a7bf3e048f8fdd8550fa72584d2190d8b3d59ea
|
|
7
|
+
data.tar.gz: 3f42b2dd764cae91a0b81f43933c230b5795f29f33aebf18a7cbfd800071dfdb9ce29f80b471747105e6caa524571877f207cffde621fdaf3d04b89d5c295f8e
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
= 3.43.1 (2021-04-13)
|
|
2
|
+
|
|
3
|
+
* [SECURITY] Fix issue where loading content_security_policy plugin after default_headers plugin had no effect (jeremyevans)
|
|
4
|
+
|
|
5
|
+
= 3.43.0 (2021-04-12)
|
|
6
|
+
|
|
7
|
+
* Add host_authorization plugin, for checking that requests are submitted using an approved host (jeremyevans)
|
|
8
|
+
|
|
9
|
+
= 3.42.0 (2021-03-12)
|
|
10
|
+
|
|
11
|
+
* Make Roda.plugin support plugins using keyword arguments in Ruby 3 (jeremyevans)
|
|
12
|
+
|
|
13
|
+
* Make Roda.use support middleware using keyword arguments in Ruby 3 (pat) (#207)
|
|
14
|
+
|
|
15
|
+
* Support common_logger plugin :method option for specifying the method to call on the logger (fnordfish, jeremyevans) (#206)
|
|
16
|
+
|
|
17
|
+
* Add recheck_precompiled_assets plugin for checking for updates to the precompiled asset metadata file (jeremyevans)
|
|
18
|
+
|
|
19
|
+
* Make compile_assets class method in assets plugin use an atomic approach to writing precompiled metadata file (jeremyevans)
|
|
20
|
+
|
|
21
|
+
= 3.41.0 (2021-02-17)
|
|
22
|
+
|
|
23
|
+
* Improve view performance with :content option up to 3x by calling compiled template methods directly (jeremyevans)
|
|
24
|
+
|
|
25
|
+
= 3.40.0 (2021-01-14)
|
|
26
|
+
|
|
27
|
+
* Add freeze_template_caches! to the precompile_templates plugin, which ensures all templates are precompiled, and speeds up template access (jeremyevans)
|
|
28
|
+
|
|
29
|
+
* Add precompile_views to the precompile_templates plugin, which precompiles the optimized render methods (jeremyevans)
|
|
30
|
+
|
|
31
|
+
* Have RodaCache#freeze return the frozen internal hash (which no longer needs a mutex for thread-safety) (jeremyevans)
|
|
32
|
+
|
|
33
|
+
* Speed up the view method in the render plugin even more when freezing the application (jeremyevans)
|
|
34
|
+
|
|
35
|
+
* Speed up the view method in the render plugin when called with a single argument (jeremyevans)
|
|
36
|
+
|
|
1
37
|
= 3.39.0 (2020-12-15)
|
|
2
38
|
|
|
3
39
|
* Speed up relative_path plugin if relative_path or relative_prefix is called more than once (jeremyevans)
|
data/MIT-LICENSE
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A precompile_views method has been added to the
|
|
4
|
+
precompile_templates plugin. This method works with Roda's
|
|
5
|
+
optimized compiled view methods, allowing additional memory
|
|
6
|
+
sharing between parent and child processes.
|
|
7
|
+
|
|
8
|
+
* A freeze_template_caches! method has been added to the
|
|
9
|
+
precompile_templates plugin. This freezes the template caches,
|
|
10
|
+
preventing the compilation of additional templates, useful for
|
|
11
|
+
enforcing that only precompiled templates are used. Additionally,
|
|
12
|
+
this speeds up access to the template caches.
|
|
13
|
+
|
|
14
|
+
* RodaCache#freeze now returns the frozen internal hash, which can
|
|
15
|
+
then be accessed without a mutex. Previously, freeze only froze
|
|
16
|
+
the receiver and not the internal hash, so it didn't have the
|
|
17
|
+
expected effect.
|
|
18
|
+
|
|
19
|
+
= Other Improvements
|
|
20
|
+
|
|
21
|
+
* The view method in the render plugin is now faster in most cases
|
|
22
|
+
when a single argument is used. When freezing the application,
|
|
23
|
+
an additional optimization is performed to increase the
|
|
24
|
+
performance of the view method even further.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
= Improvements
|
|
2
|
+
|
|
3
|
+
* The performance of the render plugin's view method when passed the
|
|
4
|
+
:content option and no other options or arguments has been improved
|
|
5
|
+
by about 3x, by calling compiled template methods directly.
|
|
6
|
+
|
|
7
|
+
* The compiled template method for the layout is cleared when the
|
|
8
|
+
render plugin is loaded again, which can fix issues when it is
|
|
9
|
+
loaded with different options that affect the layout.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A recheck_precompiled_assets plugin has been added, which allows
|
|
4
|
+
for checking for updates to the precompiled asset metadata file,
|
|
5
|
+
and automatically using the updated data.
|
|
6
|
+
|
|
7
|
+
* The common_logger plugin now supports a :method plugin option to
|
|
8
|
+
specify the method to call on the logger.
|
|
9
|
+
|
|
10
|
+
= Other Improvements
|
|
11
|
+
|
|
12
|
+
* Plugins and middleware that use keyword arguments are now supported
|
|
13
|
+
in Ruby 3.
|
|
14
|
+
|
|
15
|
+
* The compile_assets class method in the assets plugin now uses an
|
|
16
|
+
atomic approach to writing the precompiled asset metadata file.
|
|
17
|
+
|
|
18
|
+
* Minor method visibility issues have been fixed. The custom_matchers
|
|
19
|
+
plugin no longer makes the unsupported_matcher request method
|
|
20
|
+
public, and the render plugin no longer makes the _layout_method
|
|
21
|
+
public when the application is frozen.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A host_authorization plugin has been added to verify the requested
|
|
4
|
+
Host header is authorized. Using it can prevent DNS rebinding
|
|
5
|
+
attacks in cases where the application can receive requests for
|
|
6
|
+
arbitrary hosts.
|
|
7
|
+
|
|
8
|
+
To check for authorized hosts in your routing tree, you call the
|
|
9
|
+
check_host_authorization! method. For example, if you want to
|
|
10
|
+
check for authorized hosts after serving requests for public
|
|
11
|
+
files, you could do:
|
|
12
|
+
|
|
13
|
+
plugin :public
|
|
14
|
+
plugin :host_authorization, 'my-domain-name.example.com'
|
|
15
|
+
|
|
16
|
+
route do |r|
|
|
17
|
+
r.public
|
|
18
|
+
check_host_authorized!
|
|
19
|
+
|
|
20
|
+
# ... rest of routing tree
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
In addition to handling single domain names via a string, you can
|
|
24
|
+
provide an array of domain names, a regexp to match again, or a
|
|
25
|
+
proc.
|
|
26
|
+
|
|
27
|
+
By default, requests using unauthorized hosts receive an empty 403
|
|
28
|
+
response. If you would like to customize the response, you can
|
|
29
|
+
pass a block when loading the plugin:
|
|
30
|
+
|
|
31
|
+
plugin :host_authorization, 'my-domain-name.example.com' do |r|
|
|
32
|
+
response.status = 403
|
|
33
|
+
"Response Body Here"
|
|
34
|
+
end
|
data/lib/roda.rb
CHANGED
|
@@ -292,6 +292,9 @@ class Roda
|
|
|
292
292
|
plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
|
|
293
293
|
@app = nil
|
|
294
294
|
end
|
|
295
|
+
# :nocov:
|
|
296
|
+
ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
|
|
297
|
+
# :nocov:
|
|
295
298
|
|
|
296
299
|
# Setup routing tree for the current Roda application, and build the
|
|
297
300
|
# underlying rack application using the stored middleware. Requires
|
|
@@ -327,6 +330,9 @@ class Roda
|
|
|
327
330
|
@middleware << [args, block].freeze
|
|
328
331
|
@app = nil
|
|
329
332
|
end
|
|
333
|
+
# :nocov:
|
|
334
|
+
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
|
335
|
+
# :nocov:
|
|
330
336
|
|
|
331
337
|
private
|
|
332
338
|
|
data/lib/roda/cache.rb
CHANGED
|
@@ -22,6 +22,13 @@ class Roda
|
|
|
22
22
|
@mutex.synchronize{@hash[key] = value}
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# Return the frozen internal hash. The internal hash can then
|
|
26
|
+
# be accessed directly since it is frozen and there are no
|
|
27
|
+
# thread safety issues.
|
|
28
|
+
def freeze
|
|
29
|
+
@hash.freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
25
32
|
private
|
|
26
33
|
|
|
27
34
|
# Create a copy of the cache with a separate mutex.
|
data/lib/roda/plugins/assets.rb
CHANGED
|
@@ -376,7 +376,7 @@ class Roda
|
|
|
376
376
|
|
|
377
377
|
if opts[:precompiled] && !opts[:compiled] && ::File.exist?(opts[:precompiled])
|
|
378
378
|
require 'json'
|
|
379
|
-
opts[:compiled] =
|
|
379
|
+
opts[:compiled] = app.send(:_precompiled_asset_metadata, opts[:precompiled])
|
|
380
380
|
end
|
|
381
381
|
|
|
382
382
|
if opts[:early_hints]
|
|
@@ -455,7 +455,7 @@ class Roda
|
|
|
455
455
|
require 'fileutils'
|
|
456
456
|
|
|
457
457
|
unless assets_opts[:compiled]
|
|
458
|
-
opts[:assets] = assets_opts.merge(:compiled =>
|
|
458
|
+
opts[:assets] = assets_opts.merge(:compiled => _compiled_assets_initial_hash).freeze
|
|
459
459
|
end
|
|
460
460
|
|
|
461
461
|
if type == nil
|
|
@@ -465,10 +465,12 @@ class Roda
|
|
|
465
465
|
_compile_assets(type)
|
|
466
466
|
end
|
|
467
467
|
|
|
468
|
-
if assets_opts[:precompiled]
|
|
468
|
+
if precompile_file = assets_opts[:precompiled]
|
|
469
469
|
require 'json'
|
|
470
|
-
::FileUtils.mkdir_p(File.dirname(
|
|
471
|
-
|
|
470
|
+
::FileUtils.mkdir_p(File.dirname(precompile_file))
|
|
471
|
+
tmp_file = "#{precompile_file}.tmp"
|
|
472
|
+
::File.open(tmp_file, 'wb'){|f| f.write((opts[:json_serializer] || :to_json.to_proc).call(assets_opts[:compiled]))}
|
|
473
|
+
::File.rename(tmp_file, precompile_file)
|
|
472
474
|
end
|
|
473
475
|
|
|
474
476
|
assets_opts[:compiled]
|
|
@@ -476,6 +478,11 @@ class Roda
|
|
|
476
478
|
|
|
477
479
|
private
|
|
478
480
|
|
|
481
|
+
# The initial hash to use to store compiled asset metadata.
|
|
482
|
+
def _compiled_assets_initial_hash
|
|
483
|
+
{}
|
|
484
|
+
end
|
|
485
|
+
|
|
479
486
|
# Internals of compile_assets, handling recursive calls for loading
|
|
480
487
|
# all asset groups under the given type.
|
|
481
488
|
def _compile_assets(type)
|
|
@@ -493,6 +500,11 @@ class Roda
|
|
|
493
500
|
end
|
|
494
501
|
end
|
|
495
502
|
|
|
503
|
+
# The precompiled asset metadata stored in the given file
|
|
504
|
+
def _precompiled_asset_metadata(file)
|
|
505
|
+
(opts[:json_parser] || ::JSON.method(:parse)).call(::File.read(file))
|
|
506
|
+
end
|
|
507
|
+
|
|
496
508
|
# Compile each array of files for the given type into a single
|
|
497
509
|
# file. Dirs should be an array of asset group names, if these
|
|
498
510
|
# are files in an asset group.
|
|
@@ -794,23 +806,32 @@ class Roda
|
|
|
794
806
|
# handled.
|
|
795
807
|
def assets_matchers
|
|
796
808
|
@assets_matchers ||= [:css, :js].map do |t|
|
|
797
|
-
|
|
809
|
+
if regexp = assets_regexp(t)
|
|
810
|
+
[t, regexp].freeze
|
|
811
|
+
end
|
|
798
812
|
end.compact.freeze
|
|
799
813
|
end
|
|
800
814
|
|
|
801
815
|
private
|
|
802
816
|
|
|
817
|
+
# A string for the asset filename for the asset type, key, and digest.
|
|
818
|
+
def _asset_regexp(type, key, digest)
|
|
819
|
+
"#{key.sub(/\A#{type}/, '')}.#{digest}.#{type}"
|
|
820
|
+
end
|
|
821
|
+
|
|
803
822
|
# The regexp matcher to use for the given type. This handles any asset groups
|
|
804
823
|
# for the asset types.
|
|
805
824
|
def assets_regexp(type)
|
|
806
825
|
o = roda_class.assets_opts
|
|
807
826
|
if compiled = o[:compiled]
|
|
808
|
-
assets = compiled.
|
|
809
|
-
|
|
810
|
-
|
|
827
|
+
assets = compiled.
|
|
828
|
+
select{|k,_| k =~ /\A#{type}/}.
|
|
829
|
+
map{|k, md| _asset_regexp(type, k, md)}
|
|
830
|
+
return if assets.empty?
|
|
811
831
|
/#{o[:"compiled_#{type}_prefix"]}(#{Regexp.union(assets)})/
|
|
812
832
|
else
|
|
813
|
-
assets =
|
|
833
|
+
return unless assets = o[type]
|
|
834
|
+
assets = unnest_assets_hash(assets)
|
|
814
835
|
ts = o[:timestamp_paths]
|
|
815
836
|
/#{o[:"#{type}_prefix"]}#{"\\d+#{ts}" if ts}(#{Regexp.union(assets.uniq)})#{o[:"#{type}_suffix"]}/
|
|
816
837
|
end
|
|
@@ -18,10 +18,11 @@ class Roda
|
|
|
18
18
|
# plugin :common_logger
|
|
19
19
|
# plugin :common_logger, $stdout
|
|
20
20
|
# plugin :common_logger, Logger.new('filename')
|
|
21
|
+
# plugin :common_logger, Logger.new('filename'), method: :debug
|
|
21
22
|
module CommonLogger
|
|
22
|
-
def self.configure(app, logger=nil)
|
|
23
|
+
def self.configure(app, logger=nil, opts=OPTS)
|
|
23
24
|
app.opts[:common_logger] = logger || app.opts[:common_logger] || $stderr
|
|
24
|
-
app.opts[:common_logger_meth] = app.opts[:common_logger].method(logger.respond_to?(:write) ? :write : :<<)
|
|
25
|
+
app.opts[:common_logger_meth] = app.opts[:common_logger].method(opts.fetch(:method){logger.respond_to?(:write) ? :write : :<<})
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
if RUBY_VERSION >= '2.1'
|
|
@@ -23,30 +23,31 @@ class Roda
|
|
|
23
23
|
module DefaultHeaders
|
|
24
24
|
# Merge the given headers into the existing default headers, if any.
|
|
25
25
|
def self.configure(app, headers={})
|
|
26
|
-
|
|
26
|
+
app.opts[:default_headers] = (app.default_headers || app::RodaResponse::DEFAULT_HEADERS).merge(headers).freeze
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module ClassMethods
|
|
30
|
+
# The default response headers to use for the current class.
|
|
31
|
+
def default_headers
|
|
32
|
+
opts[:default_headers]
|
|
33
|
+
end
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
# Optimize the response class set_default_headers method if it hasn't been
|
|
36
|
+
# overridden and all default headers are strings.
|
|
37
|
+
def freeze
|
|
38
|
+
if (headers = opts[:default_headers]).all?{|k, v| k.is_a?(String) && v.is_a?(String)} &&
|
|
39
|
+
(self::RodaResponse.instance_method(:set_default_headers).owner == Base::ResponseMethods)
|
|
40
|
+
self::RodaResponse.class_eval(<<-END, __FILE__, __LINE__+1)
|
|
34
41
|
private
|
|
35
42
|
|
|
36
|
-
alias set_default_headers set_default_headers
|
|
37
43
|
def set_default_headers
|
|
38
44
|
h = @headers
|
|
39
45
|
#{headers.map{|k,v| "h[#{k.inspect}] ||= #{v.inspect}"}.join('; ')}
|
|
40
46
|
end
|
|
41
47
|
END
|
|
42
48
|
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
# The default response headers to use for the current class.
|
|
48
|
-
def default_headers
|
|
49
|
-
opts[:default_headers]
|
|
50
|
+
super
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
53
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The host_authorization plugin allows configuring an authorized host or
|
|
7
|
+
# an array of authorized hosts. Then in the routing tree, you can check
|
|
8
|
+
# whether the request uses an authorized host via the +check_host_authorized!+
|
|
9
|
+
# method.
|
|
10
|
+
#
|
|
11
|
+
# If the request doesn't match one of the authorized hosts, the
|
|
12
|
+
# request processing stops at that point. Using this plugin can prevent
|
|
13
|
+
# DNS rebinding attacks if the application can receive requests for
|
|
14
|
+
# arbitrary hosts.
|
|
15
|
+
#
|
|
16
|
+
# By default, an empty response using status 403 will be returned for requests
|
|
17
|
+
# with unauthorized hosts.
|
|
18
|
+
#
|
|
19
|
+
# Because +check_host_authorized!+ is an instance method, you can easily choose
|
|
20
|
+
# to only check for authorization in certain routes, or to check it after
|
|
21
|
+
# other processing. For example, you could check for authorized hosts after
|
|
22
|
+
# serving static files, since the serving of static files should not be
|
|
23
|
+
# vulnerable to DNS rebinding attacks.
|
|
24
|
+
#
|
|
25
|
+
# = Usage
|
|
26
|
+
#
|
|
27
|
+
# In your routing tree, call the +check_host_authorized!+ method at the point you
|
|
28
|
+
# want to check for authorized hosts:
|
|
29
|
+
#
|
|
30
|
+
# plugin :host_authorization, 'www.example.com'
|
|
31
|
+
# plugin :public
|
|
32
|
+
#
|
|
33
|
+
# route do |r|
|
|
34
|
+
# r.public
|
|
35
|
+
# check_host_authorized!
|
|
36
|
+
#
|
|
37
|
+
# # ...
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# = Specifying authorized hosts
|
|
41
|
+
#
|
|
42
|
+
# For applications hosted on a single domain name, you can use a single string:
|
|
43
|
+
#
|
|
44
|
+
# plugin :host_authorization, 'www.example.com'
|
|
45
|
+
#
|
|
46
|
+
# For applications hosted on multiple domain names, you can use an array of strings:
|
|
47
|
+
#
|
|
48
|
+
# plugin :host_authorization, %w'www.example.com www.example2.com'
|
|
49
|
+
#
|
|
50
|
+
# For applications supporting arbitrary subdomains, you can use a regexp. If using
|
|
51
|
+
# a regexp, make sure you use <tt>\A<tt> and <tt>\z</tt> in your regexp, and restrict
|
|
52
|
+
# the allowed characters to the minimum required, otherwise you can potentionally
|
|
53
|
+
# introduce a security issue:
|
|
54
|
+
#
|
|
55
|
+
# plugin :host_authorization, /\A[-0-9a-f]+\.example\.com\z/
|
|
56
|
+
#
|
|
57
|
+
# For applications with more complex requirements, you can use a proc. Similarly
|
|
58
|
+
# to the regexp case, the proc should be aware the host contains user-submitted
|
|
59
|
+
# values, and not assume it is in any particular format:
|
|
60
|
+
#
|
|
61
|
+
# plugin :host_authorization, proc{|host| ExternalService.allowed_host?(host)}
|
|
62
|
+
#
|
|
63
|
+
# If an array of values is passed as the host argument, the host is authorized if
|
|
64
|
+
# it matches any value in the array. All host authorization checks use the
|
|
65
|
+
# <tt>===</tt> method, which is why it works for strings, regexps, and procs.
|
|
66
|
+
# It can also work with arbitrary objects that support <tt>===</tt>.
|
|
67
|
+
#
|
|
68
|
+
# For security reasons, only the +Host+ header is checked by default. If you are
|
|
69
|
+
# sure that your application is being run behind a forwarding proxy that sets the
|
|
70
|
+
# <tt>X-Forwarded-Host</tt> header, you should enable support for checking that
|
|
71
|
+
# header using the +:check_forwarded+ option:
|
|
72
|
+
#
|
|
73
|
+
# plugin :host_authorization, 'www.example.com', check_forwarded: true
|
|
74
|
+
#
|
|
75
|
+
# In this case, the trailing host in the <tt>X-Forwarded-Host</tt> header is checked,
|
|
76
|
+
# which should be the host set by the forwarding proxy closest to the application.
|
|
77
|
+
# In cases where multiple forwarding proxies are used that append to the
|
|
78
|
+
# <tt>X-Forwarded-Host</tt> header, you should not use this plugin.
|
|
79
|
+
#
|
|
80
|
+
# = Customizing behavior
|
|
81
|
+
#
|
|
82
|
+
# By default, an unauthorized host will receive an empty 403 response. You can
|
|
83
|
+
# customize this by passing a block when loading the plugin. For example, for
|
|
84
|
+
# sites using the render plugin, you could return a page that uses your default
|
|
85
|
+
# layout:
|
|
86
|
+
#
|
|
87
|
+
# plugin :render
|
|
88
|
+
# plugin :host_authorization, 'www.example.com' do |r|
|
|
89
|
+
# response.status = 403
|
|
90
|
+
# view(:content=>"<h1>Forbidden</h1>")
|
|
91
|
+
# end
|
|
92
|
+
#
|
|
93
|
+
# The block passed to this plugin is treated as a match block.
|
|
94
|
+
module HostAuthorization
|
|
95
|
+
def self.configure(app, host, opts=OPTS, &block)
|
|
96
|
+
app.opts[:host_authorization_host] = host
|
|
97
|
+
app.opts[:host_authorization_check_forwarded] = opts[:check_forwarded] if opts.key?(:check_forwarded)
|
|
98
|
+
|
|
99
|
+
if block
|
|
100
|
+
app.define_roda_method(:host_authorization_unauthorized, 1, &block)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
module InstanceMethods
|
|
105
|
+
# Check whether the host is authorized. If not authorized, return a response
|
|
106
|
+
# immediately based on the plugin block.
|
|
107
|
+
def check_host_authorization!
|
|
108
|
+
r = @_request
|
|
109
|
+
return if host_authorized?(_convert_host_for_authorization(r.env["HTTP_HOST"].to_s.dup))
|
|
110
|
+
|
|
111
|
+
if opts[:host_authorization_check_forwarded] && (host = r.env["HTTP_X_FORWARDED_HOST"])
|
|
112
|
+
if i = host.rindex(',')
|
|
113
|
+
host = host[i+1, 10000000].to_s
|
|
114
|
+
end
|
|
115
|
+
host = _convert_host_for_authorization(host.strip)
|
|
116
|
+
|
|
117
|
+
if !host.empty? && host_authorized?(host)
|
|
118
|
+
return
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
r.on do
|
|
123
|
+
host_authorization_unauthorized(r)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
# Remove the port information from the passed string (mutates the passed argument).
|
|
130
|
+
def _convert_host_for_authorization(host)
|
|
131
|
+
host.sub!(/:\d+\z/, "")
|
|
132
|
+
host
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Whether the host given is one of the authorized hosts for this application.
|
|
136
|
+
def host_authorized?(host, authorized_host = opts[:host_authorization_host])
|
|
137
|
+
case authorized_host
|
|
138
|
+
when Array
|
|
139
|
+
authorized_host.any?{|auth_host| host_authorized?(host, auth_host)}
|
|
140
|
+
else
|
|
141
|
+
authorized_host === host
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Action to take for unauthorized hosts. Sets a 403 status by default.
|
|
146
|
+
def host_authorization_unauthorized(_)
|
|
147
|
+
@_response.status = 403
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
register_plugin(:host_authorization, HostAuthorization)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
@@ -13,32 +13,33 @@ class Roda
|
|
|
13
13
|
# all of the child processes can use the same precompiled templates, which
|
|
14
14
|
# saves memory.
|
|
15
15
|
#
|
|
16
|
-
#
|
|
17
|
-
# the
|
|
16
|
+
# Another advantage of the precompile_templates plugin is that after
|
|
17
|
+
# template precompilation, access to the template file in the file system is
|
|
18
|
+
# no longer needed, so this can be used with security features that do not
|
|
19
|
+
# allow access to the template files at runtime.
|
|
20
|
+
#
|
|
21
|
+
# After loading the plugin, you should call precompile_views with an array
|
|
22
|
+
# of views to compile, using the same argument you are passing to view or
|
|
23
|
+
# render:
|
|
18
24
|
#
|
|
19
25
|
# plugin :precompile_templates
|
|
20
|
-
#
|
|
26
|
+
# precompile_views %w'view1 view2'
|
|
27
|
+
#
|
|
28
|
+
# If the view requires local variables, you should call precompile_views with a second
|
|
29
|
+
# argument for the local variables:
|
|
21
30
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
31
|
+
# plugin :precompile_templates
|
|
32
|
+
# precompile_views :view3, [:local_var1, :local_var2]
|
|
24
33
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
34
|
+
# After all templates are precompiled, you can optionally use freeze_template_caches!,
|
|
35
|
+
# which will freeze the template caches so that any template compilation at runtime
|
|
36
|
+
# will result in an error. This also speeds up template cache access, since the
|
|
37
|
+
# template caches no longer need a mutex.
|
|
27
38
|
#
|
|
28
|
-
#
|
|
39
|
+
# freeze_template_caches!
|
|
29
40
|
#
|
|
30
41
|
# Note that you should use Tilt 2.0.1+ if you are using this plugin, so
|
|
31
42
|
# that locals are handled in the same order.
|
|
32
|
-
#
|
|
33
|
-
# You can specify other render options when calling +precompile_templates+,
|
|
34
|
-
# including +:cache_key+, +:template_class+, and +:template_opts+. If you
|
|
35
|
-
# are passing any of those options to render/view for the template, you
|
|
36
|
-
# should pass the same options when precompiling the template.
|
|
37
|
-
#
|
|
38
|
-
# To compile inline templates, just pass a single hash containing an :inline
|
|
39
|
-
# to +precompile_templates+:
|
|
40
|
-
#
|
|
41
|
-
# precompile_templates inline: some_template_string
|
|
42
43
|
module PrecompileTemplates
|
|
43
44
|
# Load the render plugin as precompile_templates depends on it.
|
|
44
45
|
def self.load_dependencies(app, opts=OPTS)
|
|
@@ -46,8 +47,49 @@ class Roda
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
module ClassMethods
|
|
49
|
-
#
|
|
50
|
-
#
|
|
50
|
+
# Freeze the template caches. Should be called after precompiling all templates during
|
|
51
|
+
# application startup, if you don't want to allow templates to be cached at runtime.
|
|
52
|
+
# In addition to ensuring that no templates are compiled at runtime, this also speeds
|
|
53
|
+
# up rendering by freezing the template caches, so that a mutex is not needed to access
|
|
54
|
+
# them.
|
|
55
|
+
def freeze_template_caches!
|
|
56
|
+
_freeze_layout_method
|
|
57
|
+
|
|
58
|
+
opts[:render] = render_opts.merge(
|
|
59
|
+
:cache=>render_opts[:cache].freeze,
|
|
60
|
+
:template_method_cache=>render_opts[:template_method_cache].freeze,
|
|
61
|
+
).freeze
|
|
62
|
+
self::RodaCompiledTemplates.freeze
|
|
63
|
+
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Precompile the templates using the given options. Note that this doesn't
|
|
68
|
+
# handle optimized template methods supported in newer versions of Roda, but
|
|
69
|
+
# there are still cases where makes sense to use it.
|
|
70
|
+
#
|
|
71
|
+
# You can call +precompile_templates+ with the pattern of templates you would
|
|
72
|
+
# like to precompile:
|
|
73
|
+
#
|
|
74
|
+
# precompile_templates "views/**/*.erb"
|
|
75
|
+
#
|
|
76
|
+
# That will precompile all erb template files in the views directory or
|
|
77
|
+
# any subdirectory.
|
|
78
|
+
#
|
|
79
|
+
# If the templates use local variables, you need to specify which local
|
|
80
|
+
# variables to precompile, which should be an array of symbols:
|
|
81
|
+
#
|
|
82
|
+
# precompile_templates 'views/users/_*.erb', locals: [:user]
|
|
83
|
+
#
|
|
84
|
+
# You can specify other render options when calling +precompile_templates+,
|
|
85
|
+
# including +:cache_key+, +:template_class+, and +:template_opts+. If you
|
|
86
|
+
# are passing any of those options to render/view for the template, you
|
|
87
|
+
# should pass the same options when precompiling the template.
|
|
88
|
+
#
|
|
89
|
+
# To compile inline templates, just pass a single hash containing an :inline
|
|
90
|
+
# to +precompile_templates+:
|
|
91
|
+
#
|
|
92
|
+
# precompile_templates inline: some_template_string
|
|
51
93
|
def precompile_templates(pattern, opts=OPTS)
|
|
52
94
|
if pattern.is_a?(Hash)
|
|
53
95
|
opts = pattern.merge(opts)
|
|
@@ -68,7 +110,40 @@ class Roda
|
|
|
68
110
|
instance = allocate
|
|
69
111
|
compile_opts.each do |compile_opt|
|
|
70
112
|
template = instance.send(:retrieve_template, compile_opt)
|
|
71
|
-
|
|
113
|
+
begin
|
|
114
|
+
Render.tilt_template_compiled_method(template, locals, self)
|
|
115
|
+
rescue NotImplementedError
|
|
116
|
+
# When freezing template caches, you may want to precompile a template for a
|
|
117
|
+
# template type that doesn't support template precompilation, just to populate
|
|
118
|
+
# the cache. Tilt rescues NotImplementedError in this case, which we can ignore.
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
nil
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Precompile the given views with the given locals, handling optimized template methods.
|
|
127
|
+
def precompile_views(views, locals=EMPTY_ARRAY)
|
|
128
|
+
instance = allocate
|
|
129
|
+
views = Array(views)
|
|
130
|
+
|
|
131
|
+
if locals.empty?
|
|
132
|
+
opts = OPTS
|
|
133
|
+
else
|
|
134
|
+
locals_hash = {}
|
|
135
|
+
locals.each{|k| locals_hash[k] = nil}
|
|
136
|
+
opts = {:locals=>locals_hash}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
views.each do |view|
|
|
140
|
+
instance.send(:retrieve_template, instance.send(:render_template_opts, view, opts))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
if locals_hash
|
|
144
|
+
views.each do |view|
|
|
145
|
+
instance.send(:_optimized_render_method_for_locals, view, locals_hash)
|
|
146
|
+
end
|
|
72
147
|
end
|
|
73
148
|
|
|
74
149
|
nil
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The recheck_precompiled_assets plugin enables checking for the precompiled asset metadata file.
|
|
7
|
+
# You need to have already loaded the assets plugin with the +:precompiled+ option and the file
|
|
8
|
+
# specified by the +:precompiled+ option must already exist in order to use the
|
|
9
|
+
# recheck_precompiled_assets plugin.
|
|
10
|
+
#
|
|
11
|
+
# Any time you want to check whether the precompiled asset metadata file has changed and should be
|
|
12
|
+
# reloaded, you can call the +recheck_precompiled_assets+ class method. This method will check
|
|
13
|
+
# whether the file has changed, and reload it if it has. If you want to check for modifications on
|
|
14
|
+
# every request, you can use +self.class.recheck_precompiled_assets+ inside your route block.
|
|
15
|
+
module RecheckPrecompiledAssets
|
|
16
|
+
# Thread safe wrapper for the compiled asset metadata hash. Does not wrap all
|
|
17
|
+
# hash methods, only a few that are used.
|
|
18
|
+
class CompiledAssetsHash
|
|
19
|
+
include Enumerable
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@hash = {}
|
|
23
|
+
@mutex = Mutex.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def [](key)
|
|
27
|
+
@mutex.synchronize{@hash[key]}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def []=(key, value)
|
|
31
|
+
@mutex.synchronize{@hash[key] = value}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def replace(hash)
|
|
35
|
+
hash = hash.instance_variable_get(:@hash) if (CompiledAssetsHash === hash)
|
|
36
|
+
@mutex.synchronize{@hash.replace(hash)}
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def each(&block)
|
|
41
|
+
@mutex.synchronize{@hash.dup}.each(&block)
|
|
42
|
+
self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_json(*args)
|
|
46
|
+
@mutex.synchronize{@hash.dup}.to_json(*args)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.load_dependencies(app)
|
|
51
|
+
unless app.respond_to?(:assets_opts) && app.assets_opts[:precompiled]
|
|
52
|
+
raise RodaError, "must load assets plugin with precompiled option before loading recheck_precompiled_assets plugin"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.configure(app)
|
|
57
|
+
precompiled_file = app.assets_opts[:precompiled]
|
|
58
|
+
prev_mtime = ::File.mtime(precompiled_file)
|
|
59
|
+
app.instance_exec do
|
|
60
|
+
opts[:assets] = opts[:assets].merge(:compiled=>_compiled_assets_initial_hash.replace(assets_opts[:compiled])).freeze
|
|
61
|
+
|
|
62
|
+
define_singleton_method(:recheck_precompiled_assets) do
|
|
63
|
+
new_mtime = ::File.mtime(precompiled_file)
|
|
64
|
+
if new_mtime != prev_mtime
|
|
65
|
+
prev_mtime = new_mtime
|
|
66
|
+
assets_opts[:compiled].replace(_precompiled_asset_metadata(precompiled_file))
|
|
67
|
+
|
|
68
|
+
# Unset the cached asset matchers, so new ones will be generated.
|
|
69
|
+
# This is needed in case the new precompiled metadata uses
|
|
70
|
+
# different files.
|
|
71
|
+
app::RodaRequest.instance_variable_set(:@assets_matchers, nil)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
singleton_class.send(:alias_method, :recheck_precompiled_assets, :recheck_precompiled_assets)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
module ClassMethods
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
# Wrap the precompiled asset metadata in a thread-safe hash.
|
|
82
|
+
def _precompiled_asset_metadata(file)
|
|
83
|
+
CompiledAssetsHash.new.replace(super)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Use a thread-safe wrapper of a hash for the :compiled assets option, since
|
|
87
|
+
# the recheck_precompiled_asset_metadata can modify it at runtime.
|
|
88
|
+
def _compiled_assets_initial_hash
|
|
89
|
+
CompiledAssetsHash.new
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
module RequestClassMethods
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
# Use a regexp that matches any digest. When the precompiled asset metadata
|
|
97
|
+
# file is updated, this allows requests for a previous precompiled asset to
|
|
98
|
+
# still work.
|
|
99
|
+
def _asset_regexp(type, key, _)
|
|
100
|
+
/#{Regexp.escape(key.sub(/\A#{type}/, ''))}\.[0-9a-fA-F]+\.#{type}/
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
register_plugin(:recheck_precompiled_assets, RecheckPrecompiledAssets)
|
|
106
|
+
end
|
|
107
|
+
end
|
data/lib/roda/plugins/render.rb
CHANGED
|
@@ -183,6 +183,7 @@ class Roda
|
|
|
183
183
|
app.const_set(:RodaCompiledTemplates, compiled_templates_module)
|
|
184
184
|
end
|
|
185
185
|
opts[:template_method_cache] = orig_method_cache || (opts[:cache_class] || RodaCache).new
|
|
186
|
+
opts[:template_method_cache][:_roda_layout] = nil if opts[:template_method_cache][:_roda_layout]
|
|
186
187
|
opts[:cache] = orig_cache || (opts[:cache_class] || RodaCache).new
|
|
187
188
|
|
|
188
189
|
opts[:layout_opts] = (opts[:layout_opts] || {}).dup
|
|
@@ -333,6 +334,25 @@ class Roda
|
|
|
333
334
|
end
|
|
334
335
|
|
|
335
336
|
module ClassMethods
|
|
337
|
+
# :nocov:
|
|
338
|
+
if COMPILED_METHOD_SUPPORT
|
|
339
|
+
# :nocov:
|
|
340
|
+
# If using compiled methods and there is an optimized layout, speed up
|
|
341
|
+
# access to the layout method to improve the performance of view.
|
|
342
|
+
def freeze
|
|
343
|
+
begin
|
|
344
|
+
_freeze_layout_method
|
|
345
|
+
rescue
|
|
346
|
+
# This is only for optimization, if any errors occur, they can be ignored.
|
|
347
|
+
# One possibility for error is the app doesn't use a layout, but doesn't
|
|
348
|
+
# specifically set the :layout=>false plugin option.
|
|
349
|
+
nil
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
super
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
336
356
|
# Copy the rendering options into the subclass, duping
|
|
337
357
|
# them as necessary to prevent changes in the subclass
|
|
338
358
|
# affecting the parent class.
|
|
@@ -352,6 +372,29 @@ class Roda
|
|
|
352
372
|
def render_opts
|
|
353
373
|
opts[:render]
|
|
354
374
|
end
|
|
375
|
+
|
|
376
|
+
private
|
|
377
|
+
|
|
378
|
+
# Precompile the layout method, to reduce method calls to look it up at runtime.
|
|
379
|
+
def _freeze_layout_method
|
|
380
|
+
if render_opts[:layout]
|
|
381
|
+
instance = allocate
|
|
382
|
+
instance.send(:retrieve_template, instance.send(:view_layout_opts, OPTS))
|
|
383
|
+
|
|
384
|
+
# :nocov:
|
|
385
|
+
if COMPILED_METHOD_SUPPORT
|
|
386
|
+
# :nocov:
|
|
387
|
+
if (layout_template = render_opts[:optimize_layout]) && !opts[:render][:optimized_layout_method_created]
|
|
388
|
+
instance.send(:retrieve_template, :template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
389
|
+
layout_method = opts[:render][:template_method_cache][:_roda_layout]
|
|
390
|
+
define_method(:_layout_method){layout_method}
|
|
391
|
+
private :_layout_method
|
|
392
|
+
alias_method(:_layout_method, :_layout_method)
|
|
393
|
+
opts[:render] = opts[:render].merge(:optimized_layout_method_created=>true)
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
355
398
|
end
|
|
356
399
|
|
|
357
400
|
module InstanceMethods
|
|
@@ -375,19 +418,21 @@ class Roda
|
|
|
375
418
|
# Render the given template. If there is a default layout
|
|
376
419
|
# for the class, take the result of the template rendering
|
|
377
420
|
# and render it inside the layout. See Render for details.
|
|
378
|
-
def view(template, opts = (
|
|
379
|
-
if
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
retrieve_template(:template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
387
|
-
layout_method = method_cache[:_roda_layout]
|
|
388
|
-
end
|
|
421
|
+
def view(template, opts = (content = _optimized_view_content(template); OPTS))
|
|
422
|
+
if content
|
|
423
|
+
# First, check if the optimized layout method has already been created,
|
|
424
|
+
# and use it if so. This way avoids the extra conditional and local variable
|
|
425
|
+
# assignments in the next section.
|
|
426
|
+
if layout_method = _layout_method
|
|
427
|
+
return send(layout_method, OPTS){content}
|
|
428
|
+
end
|
|
389
429
|
|
|
390
|
-
|
|
430
|
+
# If we have an optimized template method but no optimized layout method, create the
|
|
431
|
+
# optimized layout method if possible and use it. If you can't create the optimized
|
|
432
|
+
# layout method, fall through to the slower approach.
|
|
433
|
+
if layout_template = self.class.opts[:render][:optimize_layout]
|
|
434
|
+
retrieve_template(:template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
435
|
+
if layout_method = _layout_method
|
|
391
436
|
return send(layout_method, OPTS){content}
|
|
392
437
|
end
|
|
393
438
|
end
|
|
@@ -428,6 +473,11 @@ class Roda
|
|
|
428
473
|
method_cache[template]
|
|
429
474
|
end
|
|
430
475
|
|
|
476
|
+
# Return a symbol containing the optimized layout method
|
|
477
|
+
def _layout_method
|
|
478
|
+
self.class.opts[:render][:template_method_cache][:_roda_layout]
|
|
479
|
+
end
|
|
480
|
+
|
|
431
481
|
# Use an optimized render path for templates with a hash of locals. Returns the result
|
|
432
482
|
# of the template render if the optimized path is used, or nil if the optimized
|
|
433
483
|
# path is not used and the long method needs to be used.
|
|
@@ -469,19 +519,37 @@ class Roda
|
|
|
469
519
|
end
|
|
470
520
|
end
|
|
471
521
|
end
|
|
522
|
+
|
|
523
|
+
# Get the content for #view, or return nil to use the unoptimized approach. Only called if
|
|
524
|
+
# a single argument is passed to view.
|
|
525
|
+
def _optimized_view_content(template)
|
|
526
|
+
if optimized_template = _cached_template_method(template)
|
|
527
|
+
send(optimized_template, OPTS)
|
|
528
|
+
elsif template.is_a?(Hash) && template.length == 1
|
|
529
|
+
template[:content]
|
|
530
|
+
end
|
|
531
|
+
end
|
|
472
532
|
else
|
|
473
533
|
# :nocov:
|
|
474
|
-
def _cached_template_method(
|
|
534
|
+
def _cached_template_method(_)
|
|
475
535
|
nil
|
|
476
536
|
end
|
|
477
537
|
|
|
478
|
-
def _cached_template_method_key(
|
|
538
|
+
def _cached_template_method_key(_)
|
|
539
|
+
nil
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def _layout_method
|
|
479
543
|
nil
|
|
480
544
|
end
|
|
481
545
|
|
|
482
546
|
def _optimized_render_method_for_locals(_, _)
|
|
483
547
|
nil
|
|
484
548
|
end
|
|
549
|
+
|
|
550
|
+
def _optimized_view_content(template)
|
|
551
|
+
nil
|
|
552
|
+
end
|
|
485
553
|
# :nocov:
|
|
486
554
|
end
|
|
487
555
|
|
data/lib/roda/version.rb
CHANGED
|
@@ -4,11 +4,11 @@ class Roda
|
|
|
4
4
|
RodaMajorVersion = 3
|
|
5
5
|
|
|
6
6
|
# The minor version of Roda, updated for new feature releases of Roda.
|
|
7
|
-
RodaMinorVersion =
|
|
7
|
+
RodaMinorVersion = 43
|
|
8
8
|
|
|
9
9
|
# The patch version of Roda, updated only for bug fixes from the last
|
|
10
10
|
# feature release.
|
|
11
|
-
RodaPatchVersion =
|
|
11
|
+
RodaPatchVersion = 1
|
|
12
12
|
|
|
13
13
|
# The full version of Roda as a string.
|
|
14
14
|
RodaVersion = "#{RodaMajorVersion}.#{RodaMinorVersion}.#{RodaPatchVersion}".freeze
|
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.
|
|
4
|
+
version: 3.43.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-04-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -174,16 +174,8 @@ extra_rdoc_files:
|
|
|
174
174
|
- MIT-LICENSE
|
|
175
175
|
- CHANGELOG
|
|
176
176
|
- doc/conventions.rdoc
|
|
177
|
-
- doc/release_notes/3.7.0.txt
|
|
178
177
|
- doc/release_notes/3.0.0.txt
|
|
179
178
|
- doc/release_notes/3.1.0.txt
|
|
180
|
-
- doc/release_notes/3.2.0.txt
|
|
181
|
-
- doc/release_notes/3.3.0.txt
|
|
182
|
-
- doc/release_notes/3.4.0.txt
|
|
183
|
-
- doc/release_notes/3.5.0.txt
|
|
184
|
-
- doc/release_notes/3.6.0.txt
|
|
185
|
-
- doc/release_notes/3.8.0.txt
|
|
186
|
-
- doc/release_notes/3.9.0.txt
|
|
187
179
|
- doc/release_notes/3.10.0.txt
|
|
188
180
|
- doc/release_notes/3.11.0.txt
|
|
189
181
|
- doc/release_notes/3.12.0.txt
|
|
@@ -195,6 +187,7 @@ extra_rdoc_files:
|
|
|
195
187
|
- doc/release_notes/3.17.0.txt
|
|
196
188
|
- doc/release_notes/3.18.0.txt
|
|
197
189
|
- doc/release_notes/3.19.0.txt
|
|
190
|
+
- doc/release_notes/3.2.0.txt
|
|
198
191
|
- doc/release_notes/3.20.0.txt
|
|
199
192
|
- doc/release_notes/3.21.0.txt
|
|
200
193
|
- doc/release_notes/3.22.0.txt
|
|
@@ -205,6 +198,7 @@ extra_rdoc_files:
|
|
|
205
198
|
- doc/release_notes/3.27.0.txt
|
|
206
199
|
- doc/release_notes/3.28.0.txt
|
|
207
200
|
- doc/release_notes/3.29.0.txt
|
|
201
|
+
- doc/release_notes/3.3.0.txt
|
|
208
202
|
- doc/release_notes/3.30.0.txt
|
|
209
203
|
- doc/release_notes/3.31.0.txt
|
|
210
204
|
- doc/release_notes/3.32.0.txt
|
|
@@ -215,6 +209,16 @@ extra_rdoc_files:
|
|
|
215
209
|
- doc/release_notes/3.37.0.txt
|
|
216
210
|
- doc/release_notes/3.38.0.txt
|
|
217
211
|
- doc/release_notes/3.39.0.txt
|
|
212
|
+
- doc/release_notes/3.4.0.txt
|
|
213
|
+
- doc/release_notes/3.40.0.txt
|
|
214
|
+
- doc/release_notes/3.41.0.txt
|
|
215
|
+
- doc/release_notes/3.42.0.txt
|
|
216
|
+
- doc/release_notes/3.43.0.txt
|
|
217
|
+
- doc/release_notes/3.5.0.txt
|
|
218
|
+
- doc/release_notes/3.6.0.txt
|
|
219
|
+
- doc/release_notes/3.7.0.txt
|
|
220
|
+
- doc/release_notes/3.8.0.txt
|
|
221
|
+
- doc/release_notes/3.9.0.txt
|
|
218
222
|
files:
|
|
219
223
|
- CHANGELOG
|
|
220
224
|
- MIT-LICENSE
|
|
@@ -256,6 +260,10 @@ files:
|
|
|
256
260
|
- doc/release_notes/3.38.0.txt
|
|
257
261
|
- doc/release_notes/3.39.0.txt
|
|
258
262
|
- doc/release_notes/3.4.0.txt
|
|
263
|
+
- doc/release_notes/3.40.0.txt
|
|
264
|
+
- doc/release_notes/3.41.0.txt
|
|
265
|
+
- doc/release_notes/3.42.0.txt
|
|
266
|
+
- doc/release_notes/3.43.0.txt
|
|
259
267
|
- doc/release_notes/3.5.0.txt
|
|
260
268
|
- doc/release_notes/3.6.0.txt
|
|
261
269
|
- doc/release_notes/3.7.0.txt
|
|
@@ -306,6 +314,7 @@ files:
|
|
|
306
314
|
- lib/roda/plugins/header_matchers.rb
|
|
307
315
|
- lib/roda/plugins/heartbeat.rb
|
|
308
316
|
- lib/roda/plugins/hooks.rb
|
|
317
|
+
- lib/roda/plugins/host_authorization.rb
|
|
309
318
|
- lib/roda/plugins/indifferent_params.rb
|
|
310
319
|
- lib/roda/plugins/json.rb
|
|
311
320
|
- lib/roda/plugins/json_parser.rb
|
|
@@ -337,6 +346,7 @@ files:
|
|
|
337
346
|
- lib/roda/plugins/precompile_templates.rb
|
|
338
347
|
- lib/roda/plugins/public.rb
|
|
339
348
|
- lib/roda/plugins/r.rb
|
|
349
|
+
- lib/roda/plugins/recheck_precompiled_assets.rb
|
|
340
350
|
- lib/roda/plugins/relative_path.rb
|
|
341
351
|
- lib/roda/plugins/render.rb
|
|
342
352
|
- lib/roda/plugins/render_each.rb
|
|
@@ -394,7 +404,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
394
404
|
- !ruby/object:Gem::Version
|
|
395
405
|
version: '0'
|
|
396
406
|
requirements: []
|
|
397
|
-
rubygems_version: 3.
|
|
407
|
+
rubygems_version: 3.2.15
|
|
398
408
|
signing_key:
|
|
399
409
|
specification_version: 4
|
|
400
410
|
summary: Routing tree web toolkit
|