roda 3.37.0 → 3.42.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +40 -0
- data/MIT-LICENSE +1 -1
- data/doc/release_notes/3.3.0.txt +1 -1
- data/doc/release_notes/3.38.0.txt +5 -0
- data/doc/release_notes/3.39.0.txt +16 -0
- 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/lib/roda.rb +12 -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 +1 -0
- data/lib/roda/plugins/error_email.rb +9 -2
- data/lib/roda/plugins/error_mail.rb +9 -2
- data/lib/roda/plugins/mail_processor.rb +2 -0
- data/lib/roda/plugins/precompile_templates.rb +96 -21
- data/lib/roda/plugins/recheck_precompiled_assets.rb +107 -0
- data/lib/roda/plugins/relative_path.rb +4 -3
- data/lib/roda/plugins/render.rb +83 -15
- data/lib/roda/plugins/render_locals.rb +4 -0
- data/lib/roda/plugins/type_routing.rb +1 -0
- data/lib/roda/plugins/typecast_params.rb +2 -2
- data/lib/roda/version.rb +1 -1
- metadata +22 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2bdd27a69c76000c9c1d2d8f37f9919145fdae55224706c78adec42850c5d21
|
4
|
+
data.tar.gz: 8b92e1dbc9ec83ebca5d2d5b068c67fa282dfee25fe241451c93228a406a4749
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c9f2d3f0f021b7b0a16628cefe28c4d4001f2a80804f2bde9b48b641a70482a5d13d699252cb9f7c747de50710b91df30f893f83263e3e73c874621948b4cc6
|
7
|
+
data.tar.gz: c060f123bfb7bd0f02a83d864169d7d2a021c14cd152b8f3e7641c4c7e0317affb84fce0812afcb0d382e0828e31e2fd97b46482338ae92574fbad97793c8996
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,43 @@
|
|
1
|
+
= 3.42.0 (2021-03-12)
|
2
|
+
|
3
|
+
* Make Roda.plugin support plugins using keyword arguments in Ruby 3 (jeremyevans)
|
4
|
+
|
5
|
+
* Make Roda.use support middleware using keyword arguments in Ruby 3 (pat) (#207)
|
6
|
+
|
7
|
+
* Support common_logger plugin :method option for specifying the method to call on the logger (fnordfish, jeremyevans) (#206)
|
8
|
+
|
9
|
+
* Add recheck_precompiled_assets plugin for checking for updates to the precompiled asset metadata file (jeremyevans)
|
10
|
+
|
11
|
+
* Make compile_assets class method in assets plugin use an atomic approach to writing precompiled metadata file (jeremyevans)
|
12
|
+
|
13
|
+
= 3.41.0 (2021-02-17)
|
14
|
+
|
15
|
+
* Improve view performance with :content option up to 3x by calling compiled template methods directly (jeremyevans)
|
16
|
+
|
17
|
+
= 3.40.0 (2021-01-14)
|
18
|
+
|
19
|
+
* Add freeze_template_caches! to the precompile_templates plugin, which ensures all templates are precompiled, and speeds up template access (jeremyevans)
|
20
|
+
|
21
|
+
* Add precompile_views to the precompile_templates plugin, which precompiles the optimized render methods (jeremyevans)
|
22
|
+
|
23
|
+
* Have RodaCache#freeze return the frozen internal hash (which no longer needs a mutex for thread-safety) (jeremyevans)
|
24
|
+
|
25
|
+
* Speed up the view method in the render plugin even more when freezing the application (jeremyevans)
|
26
|
+
|
27
|
+
* Speed up the view method in the render plugin when called with a single argument (jeremyevans)
|
28
|
+
|
29
|
+
= 3.39.0 (2020-12-15)
|
30
|
+
|
31
|
+
* Speed up relative_path plugin if relative_path or relative_prefix is called more than once (jeremyevans)
|
32
|
+
|
33
|
+
* Avoid method redefinition warnings in verbose warning mode (jeremyevans)
|
34
|
+
|
35
|
+
* Make typecast_params.convert! handle explicit nil values the same as missing values (jeremyevans)
|
36
|
+
|
37
|
+
= 3.38.0 (2020-11-16)
|
38
|
+
|
39
|
+
* Make error_email and error_mail plugins rescue invalid parameter errors when preparing the email body (jeremyevans)
|
40
|
+
|
1
41
|
= 3.37.0 (2020-10-16)
|
2
42
|
|
3
43
|
* Add custom_matchers plugin, for supporting arbitrary objects as matchers (jeremyevans)
|
data/MIT-LICENSE
CHANGED
data/doc/release_notes/3.3.0.txt
CHANGED
@@ -248,7 +248,7 @@
|
|
248
248
|
Note that if there are multiple conversion errors raised inside a
|
249
249
|
convert! or convert_each! block, they are recorded and a single
|
250
250
|
Roda::RodaPlugins::TypecastParams::Error instance is raised after
|
251
|
-
processing the block. TypecastParams::Error#
|
251
|
+
processing the block. TypecastParams::Error#param_names can be
|
252
252
|
called on the exception to get an array of all parameter names
|
253
253
|
with conversion issues, and TypecastParams::Error#all_errors
|
254
254
|
can be used to get an array of all Error instances.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
= Improvements
|
2
|
+
|
3
|
+
* The relative_path plugin is now faster if you are calling
|
4
|
+
relative_path or relative_prefix more than once when handling a
|
5
|
+
request.
|
6
|
+
|
7
|
+
* The typecast_params.convert! method in the typecast_params plugin
|
8
|
+
now handles explicit nil values the same as missing values.
|
9
|
+
Explicit nil values do not generally occur in normal Rack parameter
|
10
|
+
parsing, but they can occur when using the json_parser plugin to
|
11
|
+
parse JSON requests.
|
12
|
+
|
13
|
+
* Roda now avoids method redefinition warnings in verbose mode by
|
14
|
+
using a self alias. As Ruby 3 is dropping uninitialized instance
|
15
|
+
variable warnings, Roda will be verbose warning free if you are
|
16
|
+
using Ruby 3.
|
@@ -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.
|
data/lib/roda.rb
CHANGED
@@ -114,6 +114,7 @@ class Roda
|
|
114
114
|
alias_method meth, temp_method
|
115
115
|
undef_method temp_method
|
116
116
|
private meth
|
117
|
+
alias_method meth, meth
|
117
118
|
meth = :"#{meth}_arity"
|
118
119
|
elsif required_args > 1
|
119
120
|
b = block
|
@@ -144,6 +145,7 @@ class Roda
|
|
144
145
|
|
145
146
|
define_method(meth, &block)
|
146
147
|
private meth
|
148
|
+
alias_method meth, meth
|
147
149
|
|
148
150
|
if arity_meth
|
149
151
|
required_args, optional_args, rest, keyword = _define_roda_method_arg_numbers(instance_method(meth))
|
@@ -167,6 +169,7 @@ class Roda
|
|
167
169
|
send(meth, *a)
|
168
170
|
end
|
169
171
|
private arity_meth
|
172
|
+
alias_method arity_meth, arity_meth
|
170
173
|
end
|
171
174
|
|
172
175
|
call_meth
|
@@ -199,6 +202,7 @@ class Roda
|
|
199
202
|
|
200
203
|
private
|
201
204
|
|
205
|
+
alias set_default_headers set_default_headers
|
202
206
|
def set_default_headers
|
203
207
|
@headers['Content-Type'] ||= 'text/html'
|
204
208
|
end
|
@@ -288,6 +292,9 @@ class Roda
|
|
288
292
|
plugin.configure(self, *args, &block) if plugin.respond_to?(:configure)
|
289
293
|
@app = nil
|
290
294
|
end
|
295
|
+
# :nocov:
|
296
|
+
ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
|
297
|
+
# :nocov:
|
291
298
|
|
292
299
|
# Setup routing tree for the current Roda application, and build the
|
293
300
|
# underlying rack application using the stored middleware. Requires
|
@@ -323,6 +330,9 @@ class Roda
|
|
323
330
|
@middleware << [args, block].freeze
|
324
331
|
@app = nil
|
325
332
|
end
|
333
|
+
# :nocov:
|
334
|
+
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
335
|
+
# :nocov:
|
326
336
|
|
327
337
|
private
|
328
338
|
|
@@ -403,6 +413,7 @@ class Roda
|
|
403
413
|
class_eval("def _roda_before; #{meths.join(';')} end", __FILE__, __LINE__)
|
404
414
|
end
|
405
415
|
private :_roda_before
|
416
|
+
alias_method :_roda_before, :_roda_before
|
406
417
|
end
|
407
418
|
end
|
408
419
|
|
@@ -419,6 +430,7 @@ class Roda
|
|
419
430
|
class_eval("def _roda_after(res); #{meths.map{|s| "#{s}(res)"}.join(';')} end", __FILE__, __LINE__)
|
420
431
|
end
|
421
432
|
private :_roda_after
|
433
|
+
alias_method :_roda_after, :_roda_after
|
422
434
|
end
|
423
435
|
end
|
424
436
|
|
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'
|
@@ -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
|
-
|
83
|
+
if params
|
77
84
|
message << <<END
|
78
85
|
|
79
86
|
Params:
|
80
87
|
|
81
|
-
#{
|
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
|
-
|
101
|
+
if params
|
95
102
|
message << <<END
|
96
103
|
|
97
104
|
Params:
|
98
105
|
|
99
|
-
#{
|
106
|
+
#{params}
|
100
107
|
END
|
101
108
|
end
|
102
109
|
|
@@ -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
|
@@ -41,6 +41,7 @@ class Roda
|
|
41
41
|
# Return a relative prefix to append to an absolute path to a relative path
|
42
42
|
# based on the current path of the request.
|
43
43
|
def relative_prefix
|
44
|
+
return @_relative_prefix if @_relative_prefix
|
44
45
|
env = @_request.env
|
45
46
|
script_name = env["SCRIPT_NAME"]
|
46
47
|
path_info = env["PATH_INFO"]
|
@@ -50,16 +51,16 @@ class Roda
|
|
50
51
|
case script_name.getbyte(0)
|
51
52
|
when nil # SCRIPT_NAME empty
|
52
53
|
unless path_info.getbyte(0) == 47 # PATH_INFO starts with /
|
53
|
-
return ''
|
54
|
+
return(@_relative_prefix = '')
|
54
55
|
end
|
55
56
|
when 47 # SCRIPT_NAME starts with /
|
56
57
|
# nothing
|
57
58
|
else
|
58
|
-
return ''
|
59
|
+
return(@_relative_prefix = '')
|
59
60
|
end
|
60
61
|
|
61
62
|
slash_count = script_name.count('/') + path_info.count('/')
|
62
|
-
if slash_count > 1
|
63
|
+
@_relative_prefix = if slash_count > 1
|
63
64
|
("../" * (slash_count - 2)) << ".."
|
64
65
|
else
|
65
66
|
"."
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -106,7 +106,7 @@ class Roda
|
|
106
106
|
# :template_block :: Pass this block when creating the underlying template,
|
107
107
|
# ignored when using :inline. Disables caching of the
|
108
108
|
# template by default.
|
109
|
-
# :template_class :: Provides the template class to use,
|
109
|
+
# :template_class :: Provides the template class to use, instead of using
|
110
110
|
# Tilt or <tt>Tilt[:engine]</tt>.
|
111
111
|
#
|
112
112
|
# Here's an example of using these options:
|
@@ -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
|
|
@@ -229,7 +229,7 @@ class Roda
|
|
229
229
|
#
|
230
230
|
# Note that if there are multiple conversion Error raised inside a +convert!+ or +convert_each!+
|
231
231
|
# block, they are recorded and a single TypecastParams::Error instance is raised after
|
232
|
-
# processing the block. TypecastParams::Error#
|
232
|
+
# processing the block. TypecastParams::Error#param_names can be called on the exception to
|
233
233
|
# get an array of all parameter names with conversion issues, and TypecastParams::Error#all_errors
|
234
234
|
# can be used to get an array of all Error instances.
|
235
235
|
#
|
@@ -724,7 +724,7 @@ class Roda
|
|
724
724
|
raise Error, "parameter #{param_name(nil)} is not a hash" if do_raise
|
725
725
|
return
|
726
726
|
end
|
727
|
-
present =
|
727
|
+
present = !@obj[key].nil?
|
728
728
|
when Integer
|
729
729
|
unless @obj.is_a?(Array)
|
730
730
|
raise Error, "parameter #{param_name(nil)} is not an array" if do_raise
|
data/lib/roda/version.rb
CHANGED
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.42.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:
|
11
|
+
date: 2021-03-12 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
|
@@ -213,6 +207,17 @@ extra_rdoc_files:
|
|
213
207
|
- doc/release_notes/3.35.0.txt
|
214
208
|
- doc/release_notes/3.36.0.txt
|
215
209
|
- doc/release_notes/3.37.0.txt
|
210
|
+
- doc/release_notes/3.38.0.txt
|
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.5.0.txt
|
217
|
+
- doc/release_notes/3.6.0.txt
|
218
|
+
- doc/release_notes/3.7.0.txt
|
219
|
+
- doc/release_notes/3.8.0.txt
|
220
|
+
- doc/release_notes/3.9.0.txt
|
216
221
|
files:
|
217
222
|
- CHANGELOG
|
218
223
|
- MIT-LICENSE
|
@@ -251,7 +256,12 @@ files:
|
|
251
256
|
- doc/release_notes/3.35.0.txt
|
252
257
|
- doc/release_notes/3.36.0.txt
|
253
258
|
- doc/release_notes/3.37.0.txt
|
259
|
+
- doc/release_notes/3.38.0.txt
|
260
|
+
- doc/release_notes/3.39.0.txt
|
254
261
|
- doc/release_notes/3.4.0.txt
|
262
|
+
- doc/release_notes/3.40.0.txt
|
263
|
+
- doc/release_notes/3.41.0.txt
|
264
|
+
- doc/release_notes/3.42.0.txt
|
255
265
|
- doc/release_notes/3.5.0.txt
|
256
266
|
- doc/release_notes/3.6.0.txt
|
257
267
|
- doc/release_notes/3.7.0.txt
|
@@ -333,6 +343,7 @@ files:
|
|
333
343
|
- lib/roda/plugins/precompile_templates.rb
|
334
344
|
- lib/roda/plugins/public.rb
|
335
345
|
- lib/roda/plugins/r.rb
|
346
|
+
- lib/roda/plugins/recheck_precompiled_assets.rb
|
336
347
|
- lib/roda/plugins/relative_path.rb
|
337
348
|
- lib/roda/plugins/render.rb
|
338
349
|
- lib/roda/plugins/render_each.rb
|
@@ -390,7 +401,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
390
401
|
- !ruby/object:Gem::Version
|
391
402
|
version: '0'
|
392
403
|
requirements: []
|
393
|
-
rubygems_version: 3.
|
404
|
+
rubygems_version: 3.2.3
|
394
405
|
signing_key:
|
395
406
|
specification_version: 4
|
396
407
|
summary: Routing tree web toolkit
|