roda 3.41.0 → 3.42.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: f09e850b5f4ee0406c5686317145571fa0bc5e8158b0b39c5161bb9a3cbb3878
4
- data.tar.gz: d839bbfa3ff4e7ef4a37501a46ce65c12425dc620ccbdf9aae174a059898aa84
3
+ metadata.gz: e2bdd27a69c76000c9c1d2d8f37f9919145fdae55224706c78adec42850c5d21
4
+ data.tar.gz: 8b92e1dbc9ec83ebca5d2d5b068c67fa282dfee25fe241451c93228a406a4749
5
5
  SHA512:
6
- metadata.gz: dee1ec11e6ca9ca18f74fedf260f10e25e9c49efa3297ca2df02aab02efa6282464dd88f0f1f79e3529c8c56239749c72779d8bfb7fbf8508b40047470e4f6f6
7
- data.tar.gz: c7b4d4e1d4cdf7f60707621a57cfdd6a622ef91f6d6724abd1b344fc18b59ab4271ee7536513417023c20c3f470cac73c94ff50675661162d6ec1e62df02cd70
6
+ metadata.gz: 5c9f2d3f0f021b7b0a16628cefe28c4d4001f2a80804f2bde9b48b641a70482a5d13d699252cb9f7c747de50710b91df30f893f83263e3e73c874621948b4cc6
7
+ data.tar.gz: c060f123bfb7bd0f02a83d864169d7d2a021c14cd152b8f3e7641c4c7e0317affb84fce0812afcb0d382e0828e31e2fd97b46482338ae92574fbad97793c8996
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
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
+
1
13
  = 3.41.0 (2021-02-17)
2
14
 
3
15
  * Improve view performance with :content option up to 3x by calling compiled template methods directly (jeremyevans)
@@ -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
@@ -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
 
@@ -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] = (app.opts[:json_parser] || ::JSON.method(:parse)).call(::File.read(opts[:precompiled]))
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(assets_opts[:precompiled]))
471
- ::File.open(assets_opts[:precompiled], 'wb'){|f| f.write((opts[:json_serializer] || :to_json.to_proc).call(assets_opts[:compiled]))}
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
- [t, assets_regexp(t)].freeze if roda_class.assets_opts[t]
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.select{|k,_| k =~ /\A#{type}/}.map do |k, md|
809
- "#{k.sub(/\A#{type}/, '')}.#{md}.#{type}"
810
- end
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 = unnest_assets_hash(o[type])
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'
@@ -68,6 +68,8 @@ class Roda
68
68
  end
69
69
 
70
70
  module RequestMethods
71
+ private
72
+
71
73
  # Try custom matchers before calling super
72
74
  def unsupported_matcher(matcher)
73
75
  roda_class.opts[:custom_matchers].each do |match_class, meth|
@@ -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
@@ -388,6 +388,8 @@ class Roda
388
388
  instance.send(:retrieve_template, :template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
389
389
  layout_method = opts[:render][:template_method_cache][:_roda_layout]
390
390
  define_method(:_layout_method){layout_method}
391
+ private :_layout_method
392
+ alias_method(:_layout_method, :_layout_method)
391
393
  opts[:render] = opts[:render].merge(:optimized_layout_method_created=>true)
392
394
  end
393
395
  end
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 = 41
7
+ RodaMinorVersion = 42
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.41.0
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: 2021-02-17 00:00:00.000000000 Z
11
+ date: 2021-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -212,6 +212,7 @@ extra_rdoc_files:
212
212
  - doc/release_notes/3.4.0.txt
213
213
  - doc/release_notes/3.40.0.txt
214
214
  - doc/release_notes/3.41.0.txt
215
+ - doc/release_notes/3.42.0.txt
215
216
  - doc/release_notes/3.5.0.txt
216
217
  - doc/release_notes/3.6.0.txt
217
218
  - doc/release_notes/3.7.0.txt
@@ -260,6 +261,7 @@ files:
260
261
  - doc/release_notes/3.4.0.txt
261
262
  - doc/release_notes/3.40.0.txt
262
263
  - doc/release_notes/3.41.0.txt
264
+ - doc/release_notes/3.42.0.txt
263
265
  - doc/release_notes/3.5.0.txt
264
266
  - doc/release_notes/3.6.0.txt
265
267
  - doc/release_notes/3.7.0.txt
@@ -341,6 +343,7 @@ files:
341
343
  - lib/roda/plugins/precompile_templates.rb
342
344
  - lib/roda/plugins/public.rb
343
345
  - lib/roda/plugins/r.rb
346
+ - lib/roda/plugins/recheck_precompiled_assets.rb
344
347
  - lib/roda/plugins/relative_path.rb
345
348
  - lib/roda/plugins/render.rb
346
349
  - lib/roda/plugins/render_each.rb