roda 3.109.0 → 3.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/roda/cache.rb +0 -2
- data/lib/roda/opts.rb +1 -1
- data/lib/roda/plugins/_symbol_class_matchers.rb +2 -4
- data/lib/roda/plugins/additional_view_directories.rb +2 -2
- data/lib/roda/plugins/assets.rb +173 -142
- data/lib/roda/plugins/assets_preloading.rb +26 -10
- data/lib/roda/plugins/backtracking_array.rb +2 -2
- data/lib/roda/plugins/branch_locals.rb +4 -4
- data/lib/roda/plugins/break.rb +2 -2
- data/lib/roda/plugins/caching.rb +27 -17
- data/lib/roda/plugins/capture_erb.rb +15 -12
- data/lib/roda/plugins/chunked.rb +32 -30
- data/lib/roda/plugins/class_level_routing.rb +5 -2
- data/lib/roda/plugins/class_matchers.rb +11 -6
- data/lib/roda/plugins/common_logger.rb +21 -3
- data/lib/roda/plugins/conditional_sessions.rb +3 -3
- data/lib/roda/plugins/content_for.rb +7 -6
- data/lib/roda/plugins/content_security_policy.rb +11 -12
- data/lib/roda/plugins/cookie_flags.rb +12 -6
- data/lib/roda/plugins/cookies.rb +7 -5
- data/lib/roda/plugins/csrf.rb +2 -1
- data/lib/roda/plugins/custom_block_results.rb +6 -7
- data/lib/roda/plugins/default_headers.rb +2 -2
- data/lib/roda/plugins/delay_build.rb +1 -1
- data/lib/roda/plugins/delegate.rb +7 -6
- data/lib/roda/plugins/direct_call.rb +5 -1
- data/lib/roda/plugins/disallow_file_uploads.rb +2 -3
- data/lib/roda/plugins/drop_body.rb +3 -6
- data/lib/roda/plugins/each_part.rb +4 -6
- data/lib/roda/plugins/early_hints.rb +4 -4
- data/lib/roda/plugins/empty_root.rb +5 -5
- data/lib/roda/plugins/environments.rb +8 -8
- data/lib/roda/plugins/erb_h.rb +4 -4
- data/lib/roda/plugins/error_email.rb +16 -17
- data/lib/roda/plugins/error_handler.rb +24 -32
- data/lib/roda/plugins/error_mail.rb +26 -15
- data/lib/roda/plugins/exception_page.rb +41 -35
- data/lib/roda/plugins/filter_common_logger.rb +6 -4
- data/lib/roda/plugins/flash.rb +16 -13
- data/lib/roda/plugins/freeze_request_response.rb +24 -0
- data/lib/roda/plugins/hash_routes.rb +1 -1
- data/lib/roda/plugins/permissions_policy.rb +14 -16
- data/lib/roda/plugins/public.rb +11 -0
- data/lib/roda/plugins/recheck_precompiled_assets.rb +30 -8
- data/lib/roda/plugins/render.rb +3 -0
- data/lib/roda/plugins/render_locals.rb +1 -1
- data/lib/roda/plugins/sessions.rb +158 -137
- data/lib/roda/plugins/symbol_matchers.rb +8 -5
- data/lib/roda/plugins/unescape_path.rb +22 -4
- data/lib/roda/plugins.rb +24 -0
- data/lib/roda/request.rb +8 -7
- data/lib/roda/response.rb +9 -7
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +12 -9
- metadata +2 -1
data/lib/roda/plugins/assets.rb
CHANGED
|
@@ -21,7 +21,7 @@ class Roda
|
|
|
21
21
|
#
|
|
22
22
|
# == Usage
|
|
23
23
|
#
|
|
24
|
-
# When loading the plugin, use the
|
|
24
|
+
# When loading the plugin, use the +:css+ and +:js+ options
|
|
25
25
|
# to set the source file(s) to use for CSS and javascript assets:
|
|
26
26
|
#
|
|
27
27
|
# plugin :assets, css: 'some_file.scss', js: 'some_file.coffee'
|
|
@@ -31,7 +31,7 @@ class Roda
|
|
|
31
31
|
# assets/css/some_file.scss
|
|
32
32
|
# assets/js/some_file.coffee
|
|
33
33
|
#
|
|
34
|
-
# The values for the
|
|
34
|
+
# The values for the +:css+ and +:js+ options can be arrays to load multiple
|
|
35
35
|
# files. If you want to change the paths where asset files are stored, see the
|
|
36
36
|
# Options section below.
|
|
37
37
|
#
|
|
@@ -49,7 +49,7 @@ class Roda
|
|
|
49
49
|
#
|
|
50
50
|
# === Views
|
|
51
51
|
#
|
|
52
|
-
# In your layout view, use the assets method to add links to your CSS and
|
|
52
|
+
# In your layout view, use the +assets+ method to add links to your CSS and
|
|
53
53
|
# javascript assets:
|
|
54
54
|
#
|
|
55
55
|
# <%= assets(:css) %>
|
|
@@ -66,7 +66,7 @@ class Roda
|
|
|
66
66
|
# == Asset Paths
|
|
67
67
|
#
|
|
68
68
|
# If you just want the paths rather than the full tags, you can use
|
|
69
|
-
# assets_paths instead. This will return an array of the sources that
|
|
69
|
+
# +assets_paths+ instead. This will return an array of the sources that
|
|
70
70
|
# the assets function would have put into tags:
|
|
71
71
|
#
|
|
72
72
|
# assets_paths(:css)
|
|
@@ -95,15 +95,14 @@ class Roda
|
|
|
95
95
|
# If you do not want to force that directory structure when using
|
|
96
96
|
# asset groups, you can use the <tt>group_subdirs: false</tt> option.
|
|
97
97
|
#
|
|
98
|
-
# In your view code use an array argument in your call to assets
|
|
98
|
+
# In your view code use an array argument in your call to +assets+:
|
|
99
99
|
#
|
|
100
100
|
# <%= assets([:css, :frontend]) %>
|
|
101
101
|
#
|
|
102
102
|
# === Nesting
|
|
103
103
|
#
|
|
104
|
-
# Asset groups also support nesting
|
|
105
|
-
#
|
|
106
|
-
# the plugin:
|
|
104
|
+
# Asset groups also support nesting. You can use a nested hash when
|
|
105
|
+
# loading the plugin:
|
|
107
106
|
#
|
|
108
107
|
# plugin :assets,
|
|
109
108
|
# css: {frontend: {dashboard: 'some_frontend_file.scss'}}
|
|
@@ -115,13 +114,13 @@ class Roda
|
|
|
115
114
|
# == Caching
|
|
116
115
|
#
|
|
117
116
|
# The assets plugin uses the caching plugin internally, and will set the
|
|
118
|
-
# Last-Modified header to the modified timestamp of the asset source file
|
|
117
|
+
# <tt>Last-Modified</tt> header to the modified timestamp of the asset source file
|
|
119
118
|
# when rendering the asset.
|
|
120
119
|
#
|
|
121
120
|
# If you have assets that include other asset files, such as using @import
|
|
122
121
|
# in a sass file, you need to specify the dependencies for your assets so
|
|
123
122
|
# that the assets plugin will correctly pick up changes. You can do this
|
|
124
|
-
# using the
|
|
123
|
+
# using the +:dependencies+ option to the plugin, which takes a hash where
|
|
125
124
|
# the keys are paths to asset files, and values are arrays of paths to
|
|
126
125
|
# dependencies of those asset files:
|
|
127
126
|
#
|
|
@@ -131,26 +130,22 @@ class Roda
|
|
|
131
130
|
# == Asset Compilation
|
|
132
131
|
#
|
|
133
132
|
# In production, you are generally going to want to compile your assets
|
|
134
|
-
# into a single file, with you can do by calling compile_assets after
|
|
133
|
+
# into a single file, with you can do by calling +compile_assets+ after
|
|
135
134
|
# loading the plugin:
|
|
136
135
|
#
|
|
137
136
|
# plugin :assets, css: 'some_file.scss', js: 'some_file.coffee'
|
|
138
137
|
# compile_assets
|
|
139
138
|
#
|
|
140
|
-
# After calling compile_assets
|
|
139
|
+
# After calling +compile_assets+, calls to +assets+ in your views will default
|
|
141
140
|
# to a using a single link each to your CSS and javascript compiled asset
|
|
142
141
|
# files. By default the compiled files are written to the public directory,
|
|
143
142
|
# so that they can be served by the webserver.
|
|
144
143
|
#
|
|
145
144
|
# === Asset Compression
|
|
146
145
|
#
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
# closure-compiler, uglifier, and minjs and use the first one that works.
|
|
151
|
-
# If no compressors are available, the assets will just be concatenated
|
|
152
|
-
# together and not compressed during compilation. You can use the
|
|
153
|
-
# :css_compressor and :js_compressor options to specify the compressor to use.
|
|
146
|
+
# You can use the +:css_compressor+ and +:js_compressor+ options to specify a
|
|
147
|
+
# compressor callable. The callable will be called with the uncompressed source,
|
|
148
|
+
# and should return the compressed source.
|
|
154
149
|
#
|
|
155
150
|
# It is also possible to use the built-in compression options in the CSS or JS
|
|
156
151
|
# compiler, assuming the compiler supports such options. For example, with
|
|
@@ -159,9 +154,7 @@ class Roda
|
|
|
159
154
|
# plugin :assets,
|
|
160
155
|
# css_opts: {style: :compressed}
|
|
161
156
|
#
|
|
162
|
-
#
|
|
163
|
-
# need to specify the compressor to use using a Proc or Method. Here are values
|
|
164
|
-
# that you can use.
|
|
157
|
+
# Here are examples you can use for a few compressor libraries:
|
|
165
158
|
#
|
|
166
159
|
# ==== yui
|
|
167
160
|
#
|
|
@@ -222,19 +215,19 @@ class Roda
|
|
|
222
215
|
# end
|
|
223
216
|
#
|
|
224
217
|
# If you do have the application serve the compiled assets, it will use the
|
|
225
|
-
# Last-Modified header to make sure that clients do not redownload compiled
|
|
218
|
+
# <tt>Last-Modified</tt> header to make sure that clients do not redownload compiled
|
|
226
219
|
# assets that haven't changed.
|
|
227
220
|
#
|
|
228
221
|
# === Asset Precompilation
|
|
229
222
|
#
|
|
230
223
|
# If you want to precompile your assets, so they do not need to be compiled
|
|
231
|
-
# every time you boot the application, you can provide a
|
|
224
|
+
# every time you boot the application, you can provide a +:precompiled+ option
|
|
232
225
|
# when loading the plugin. The value of this option should be the filename
|
|
233
226
|
# where the compiled asset metadata is stored.
|
|
234
227
|
#
|
|
235
228
|
# If the compiled asset metadata file does not exist when the assets plugin
|
|
236
229
|
# is loaded, the plugin will run in non-compiled mode. However, when you call
|
|
237
|
-
# compile_assets
|
|
230
|
+
# +compile_assets+, it will write the compiled asset metadata file after
|
|
238
231
|
# compiling the assets.
|
|
239
232
|
#
|
|
240
233
|
# If the compiled asset metadata file already exists when the assets plugin
|
|
@@ -245,7 +238,7 @@ class Roda
|
|
|
245
238
|
# ==== On Heroku
|
|
246
239
|
#
|
|
247
240
|
# Heroku supports precompiling the assets when using Roda. You just need to
|
|
248
|
-
# add an assets:precompile task,
|
|
241
|
+
# add an assets:precompile task, such as:
|
|
249
242
|
#
|
|
250
243
|
# namespace :assets do
|
|
251
244
|
# desc "Precompile the assets"
|
|
@@ -283,71 +276,67 @@ class Roda
|
|
|
283
276
|
# path, you have the following options:
|
|
284
277
|
#
|
|
285
278
|
# * Copy, hard link, or symlink the external assets files into the assets path.
|
|
286
|
-
# * Use
|
|
287
|
-
#
|
|
288
|
-
#
|
|
289
|
-
#
|
|
290
|
-
#
|
|
291
|
-
#
|
|
292
|
-
#
|
|
293
|
-
#
|
|
294
|
-
#
|
|
295
|
-
#
|
|
296
|
-
# :
|
|
279
|
+
# * Use indirection, such as an erb template that loads the external asset file,
|
|
280
|
+
# so that a file inside the assets path can reference files outside the assets path.
|
|
281
|
+
#
|
|
282
|
+
# == Plugin Keyword Arguments
|
|
283
|
+
#
|
|
284
|
+
# +add_suffix+ :: Whether to append a .css or .js extension to asset routes in non-compiled mode
|
|
285
|
+
# (default: false)
|
|
286
|
+
# +compiled_asset_host+ :: The asset host to use for compiled assets. Should include the protocol
|
|
287
|
+
# as well as the host (e.g. "https://cdn.example.com", "//cdn.example.com")
|
|
288
|
+
# +compiled_css_dir+ :: Directory name in which to store the compiled css file,
|
|
289
|
+
# inside :compiled_path (default: nil)
|
|
290
|
+
# +compiled_css_route+ :: Route under :prefix for compiled css assets (default: :compiled_css_dir)
|
|
291
|
+
# +compiled_js_dir+ :: Directory name in which to store the compiled javascript file,
|
|
297
292
|
# inside :compiled_path (default: nil)
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
#
|
|
302
|
-
#
|
|
303
|
-
#
|
|
304
|
-
#
|
|
305
|
-
#
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
#
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
#
|
|
313
|
-
#
|
|
314
|
-
#
|
|
315
|
-
#
|
|
316
|
-
#
|
|
317
|
-
#
|
|
318
|
-
#
|
|
319
|
-
#
|
|
320
|
-
#
|
|
321
|
-
#
|
|
322
|
-
# not fail if any of them is not available)
|
|
323
|
-
# :js_dir :: Directory name containing your javascript source, inside :path (default: 'js')
|
|
324
|
-
# :js_headers :: A hash of additional headers for your rendered javascript files
|
|
325
|
-
# :js_opts :: Template options to pass to the render plugin (via :template_opts) when rendering javascript assets
|
|
326
|
-
# :js_route :: Route under :prefix for javascript assets (default: :js_dir)
|
|
327
|
-
# :path :: Path to your asset source directory (default: 'assets'). Relative
|
|
293
|
+
# +compiled_js_route+ :: Route under :prefix for compiled javscript assets (default: :compiled_js_dir)
|
|
294
|
+
# +compiled_name+ :: Compiled file name prefix (default: 'app')
|
|
295
|
+
# +compiled_path+ :: Path inside public folder in which compiled files are stored (default: :prefix)
|
|
296
|
+
# +css+ :: The files contain the CSS assets, or a hash to use asset groups.
|
|
297
|
+
# +css_compressor+ :: Compressor callable to use for compressing CSS
|
|
298
|
+
# +css_dir+ :: Directory name containing your css source, inside :path (default: 'css')
|
|
299
|
+
# +css_headers+ :: A hash of additional headers for your rendered css files
|
|
300
|
+
# +css_opts+ :: Template options to pass to the render plugin (via :template_opts) when rendering css assets
|
|
301
|
+
# +css_route+ :: Route under :prefix for css assets (default: :css_dir)
|
|
302
|
+
# +dependencies+ :: A hash of dependencies for your asset files. Keys should be paths to asset files,
|
|
303
|
+
# values should be arrays of paths your asset files depends on. This is used to
|
|
304
|
+
# detect changes in your asset files.
|
|
305
|
+
# +early_hints+ :: Automatically send early hints for all assets. Requires the early_hints plugin.
|
|
306
|
+
# +group_subdirs+ :: Whether a hash used in :css and :js options requires the assets for the
|
|
307
|
+
# related group are contained in a subdirectory with the same name (default: true)
|
|
308
|
+
# +gzip+ :: Store gzipped compiled assets files, and serve those to clients who accept gzip encoding.
|
|
309
|
+
# +headers+ :: A hash of additional headers for both js and css rendered files
|
|
310
|
+
# +js+ :: The files contain the javascript assets, or a hash to use assets groups.
|
|
311
|
+
# +js_compressor+ :: Compressor callable to use for compressing javascript
|
|
312
|
+
# +js_dir+ :: Directory name containing your javascript source, inside :path (default: 'js')
|
|
313
|
+
# +js_headers+ :: A hash of additional headers for your rendered javascript files
|
|
314
|
+
# +js_opts+ :: Template options to pass to the render plugin (via :template_opts) when rendering javascript assets
|
|
315
|
+
# +js_route+ :: Route under :prefix for javascript assets (default: :js_dir)
|
|
316
|
+
# +path+ :: Path to your asset source directory (default: 'assets'). Relative
|
|
328
317
|
# paths will be considered relative to the application's :root option.
|
|
329
|
-
#
|
|
330
|
-
#
|
|
331
|
-
#
|
|
332
|
-
#
|
|
333
|
-
#
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
#
|
|
337
|
-
#
|
|
338
|
-
#
|
|
339
|
-
#
|
|
340
|
-
#
|
|
341
|
-
#
|
|
342
|
-
#
|
|
343
|
-
#
|
|
344
|
-
#
|
|
345
|
-
#
|
|
346
|
-
#
|
|
347
|
-
#
|
|
348
|
-
#
|
|
349
|
-
#
|
|
350
|
-
#
|
|
318
|
+
# +postprocessor+ :: A block which should accept three arguments (asset name, asset type,
|
|
319
|
+
# content). This block can be used to hook into the asset system and
|
|
320
|
+
# make your own modifications before the asset is served. If the asset
|
|
321
|
+
# is to be compiled, the block is called at compile time.
|
|
322
|
+
# +prefix+ :: Prefix for assets path in your URL/routes (default: 'assets')
|
|
323
|
+
# +precompiled+ :: Path to the compiled asset metadata file. If the file exists, will use compiled
|
|
324
|
+
# mode using the metadata in the file. If the file does not exist, will use
|
|
325
|
+
# non-compiled mode, but will write the metadata to the file if compile_assets is called.
|
|
326
|
+
# +public+ :: Path to your public folder, in which compiled files are placed (default: 'public').
|
|
327
|
+
# Relative paths will be considered relative to the application's :root option.
|
|
328
|
+
# +relative_paths+ :: Use relative paths instead of absolute paths when setting up link and script tags for
|
|
329
|
+
# assets.
|
|
330
|
+
# +sri+ :: Enables subresource integrity when setting up references to compiled assets. The value should be
|
|
331
|
+
# :sha256, :sha384, or :sha512 depending on which hash algorithm you want to use. This changes the
|
|
332
|
+
# hash algorithm that Roda will use when naming compiled asset files. The default is :sha256, you
|
|
333
|
+
# can use nil to disable subresource integrity.
|
|
334
|
+
# +timestamp_paths+ :: Include the timestamp of assets in asset paths in non-compiled mode. Doing this can
|
|
335
|
+
# slow down development requests due to additional requests to get last modified times,
|
|
336
|
+
# but it will make sure the paths change in development when there are modifications,
|
|
337
|
+
# which can fix issues when using a caching proxy in non-compiled mode. This can also
|
|
338
|
+
# be specified as a string to use that string to separate the timestamp from the asset.
|
|
339
|
+
# By default, <tt>/</tt> is used as the separator if timestamp paths are enabled.
|
|
351
340
|
module Assets
|
|
352
341
|
DEFAULTS = {
|
|
353
342
|
:compiled_name => 'app'.freeze,
|
|
@@ -364,10 +353,16 @@ class Roda
|
|
|
364
353
|
:compiled_js_dir => nil,
|
|
365
354
|
:sri => :sha256
|
|
366
355
|
}.freeze
|
|
356
|
+
RodaPlugins.deprecate_constant(self, :DEFAULTS)
|
|
367
357
|
|
|
368
358
|
# Internal exception raised when a compressor cannot be found
|
|
369
359
|
CompressorNotFound = Class.new(RodaError)
|
|
370
360
|
|
|
361
|
+
PLUGIN_ARGUMENTS = %i[add_suffix compiled_asset_host compiled_css_dir compiled_css_route compiled_js_dir compiled_js_route compiled_name compiled_path concat_only css css_compressor css_dir css_headers css_opts css_route dependencies early_hints group_subdirs gzip headers js js_compressor js_dir js_headers js_opts js_route path postprocessor prefix precompiled public relative_paths sri timestamp_paths].freeze
|
|
362
|
+
private_constant :PLUGIN_ARGUMENTS
|
|
363
|
+
|
|
364
|
+
Config = Struct.new(*PLUGIN_ARGUMENTS, :compiled, :compiled_css_path, :compiled_css_prefix, :compiled_css_suffix, :compiled_js_path, :compiled_js_prefix, :compiled_js_suffix, :css_path, :css_prefix, :css_suffix, :expanded_dependencies, :js_path, :js_prefix, :js_suffix, :orig_opts)
|
|
365
|
+
|
|
371
366
|
# Load the render, caching, and h plugins, since the assets plugin
|
|
372
367
|
# depends on them.
|
|
373
368
|
def self.load_dependencies(app, opts = OPTS)
|
|
@@ -380,14 +375,20 @@ class Roda
|
|
|
380
375
|
if opts[:relative_paths]
|
|
381
376
|
app.plugin :relative_path
|
|
382
377
|
end
|
|
378
|
+
|
|
379
|
+
if opts[:early_hints]
|
|
380
|
+
app.plugin :early_hints
|
|
381
|
+
end
|
|
383
382
|
end
|
|
384
383
|
|
|
385
384
|
# Setup the options for the plugin. See the Assets module RDoc
|
|
386
385
|
# for a description of the supported options.
|
|
387
|
-
def self.configure(app, opts
|
|
386
|
+
def self.configure(app, opts=OPTS)
|
|
387
|
+
RodaPlugins.check_supported_opts("the assets plugin", opts, PLUGIN_ARGUMENTS)
|
|
388
|
+
|
|
388
389
|
if app.assets_opts
|
|
389
390
|
prev_opts = app.assets_opts[:orig_opts]
|
|
390
|
-
orig_opts =
|
|
391
|
+
orig_opts = prev_opts.merge(opts)
|
|
391
392
|
[:headers, :css_headers, :js_headers, :css_opts, :js_opts, :dependencies].each do |s|
|
|
392
393
|
if prev_opts[s]
|
|
393
394
|
if opts[s]
|
|
@@ -425,17 +426,27 @@ class Roda
|
|
|
425
426
|
opts[:compiled] = app.send(:_precompiled_asset_metadata, opts[:precompiled])
|
|
426
427
|
end
|
|
427
428
|
|
|
428
|
-
if opts[:early_hints]
|
|
429
|
-
app.plugin :early_hints
|
|
430
|
-
end
|
|
431
|
-
|
|
432
429
|
if opts[:timestamp_paths] && !opts[:timestamp_paths].is_a?(String)
|
|
433
430
|
opts[:timestamp_paths] = '/'
|
|
434
431
|
end
|
|
435
432
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
433
|
+
{
|
|
434
|
+
:compiled_name => 'app'.freeze,
|
|
435
|
+
:js_dir => 'js'.freeze,
|
|
436
|
+
:css_dir => 'css'.freeze,
|
|
437
|
+
:prefix => 'assets'.freeze,
|
|
438
|
+
:concat_only => false,
|
|
439
|
+
:compiled => false,
|
|
440
|
+
:add_suffix => false,
|
|
441
|
+
:early_hints => false,
|
|
442
|
+
:timestamp_paths => false,
|
|
443
|
+
:group_subdirs => true,
|
|
444
|
+
:compiled_css_dir => nil,
|
|
445
|
+
:compiled_js_dir => nil,
|
|
446
|
+
:sri => :sha256
|
|
447
|
+
}.each do |k, v|
|
|
448
|
+
opts[k] = v unless opts.has_key?(k)
|
|
449
|
+
end
|
|
439
450
|
|
|
440
451
|
[
|
|
441
452
|
[:compiled_path, :prefix],
|
|
@@ -496,10 +507,12 @@ class Roda
|
|
|
496
507
|
end
|
|
497
508
|
|
|
498
509
|
opts.freeze
|
|
510
|
+
app.opts[:assets_config] = Config.new(*opts.values_at(*Config.members)).freeze
|
|
499
511
|
end
|
|
500
512
|
|
|
501
513
|
module ClassMethods
|
|
502
514
|
RodaPlugins.opt_attr_reader(self, :assets, name: :assets_opts)
|
|
515
|
+
RodaPlugins.opt_attr_reader(self, :assets_config)
|
|
503
516
|
|
|
504
517
|
# Compile options for the given asset type. If no asset_type
|
|
505
518
|
# is given, compile both the :css and :js asset types. You
|
|
@@ -508,9 +521,7 @@ class Roda
|
|
|
508
521
|
def compile_assets(type=nil)
|
|
509
522
|
require 'fileutils'
|
|
510
523
|
|
|
511
|
-
unless
|
|
512
|
-
opts[:assets] = assets_opts.merge(:compiled => _compiled_assets_initial_hash).freeze
|
|
513
|
-
end
|
|
524
|
+
_update_compiled_assets(_compiled_assets_initial_hash) unless assets_config.compiled
|
|
514
525
|
|
|
515
526
|
if type == nil
|
|
516
527
|
_compile_assets(:css)
|
|
@@ -519,18 +530,23 @@ class Roda
|
|
|
519
530
|
_compile_assets(type)
|
|
520
531
|
end
|
|
521
532
|
|
|
522
|
-
if precompile_file =
|
|
533
|
+
if precompile_file = assets_config.precompiled
|
|
523
534
|
::FileUtils.mkdir_p(File.dirname(precompile_file))
|
|
524
535
|
tmp_file = "#{precompile_file}.tmp"
|
|
525
|
-
::File.open(tmp_file, 'wb'){|f| f.write(json_serializer.call(
|
|
536
|
+
::File.open(tmp_file, 'wb'){|f| f.write(json_serializer.call(assets_config.compiled))}
|
|
526
537
|
::File.rename(tmp_file, precompile_file)
|
|
527
538
|
end
|
|
528
539
|
|
|
529
|
-
|
|
540
|
+
assets_config.compiled
|
|
530
541
|
end
|
|
531
542
|
|
|
532
543
|
private
|
|
533
544
|
|
|
545
|
+
def _update_compiled_assets(compiled)
|
|
546
|
+
opts[:assets] = assets_opts.merge(:compiled => compiled).freeze
|
|
547
|
+
opts[:assets_config] = Config.new(*assets_opts.values_at(*Config.members)).freeze
|
|
548
|
+
end
|
|
549
|
+
|
|
534
550
|
# The initial hash to use to store compiled asset metadata.
|
|
535
551
|
def _compiled_assets_initial_hash
|
|
536
552
|
{}
|
|
@@ -541,7 +557,7 @@ class Roda
|
|
|
541
557
|
def _compile_assets(type)
|
|
542
558
|
type, *dirs = type if type.is_a?(Array)
|
|
543
559
|
dirs ||= []
|
|
544
|
-
files =
|
|
560
|
+
files = assets_config[type]
|
|
545
561
|
dirs.each{|d| files = files[d]}
|
|
546
562
|
|
|
547
563
|
case files
|
|
@@ -563,17 +579,17 @@ class Roda
|
|
|
563
579
|
# are files in an asset group.
|
|
564
580
|
def compile_assets_files(files, type, dirs)
|
|
565
581
|
dirs = nil if dirs && dirs.empty?
|
|
566
|
-
o =
|
|
582
|
+
o = assets_config
|
|
567
583
|
app = allocate
|
|
568
584
|
|
|
569
585
|
content = files.map do |file|
|
|
570
|
-
file = "#{dirs.join('/')}/#{file}" if dirs && o
|
|
586
|
+
file = "#{dirs.join('/')}/#{file}" if dirs && o.group_subdirs
|
|
571
587
|
file = "#{o[:"#{type}_path"]}#{file}"
|
|
572
588
|
app.read_asset_file(file, type)
|
|
573
589
|
end.join("\n")
|
|
574
590
|
|
|
575
591
|
# RODA4: Remove :concat_only option
|
|
576
|
-
unless o
|
|
592
|
+
unless o.concat_only
|
|
577
593
|
content = compress_asset(content, type)
|
|
578
594
|
end
|
|
579
595
|
|
|
@@ -597,7 +613,7 @@ class Roda
|
|
|
597
613
|
# Compress the given content for the given type by using the
|
|
598
614
|
# configured compressor, or trying the supported compressors.
|
|
599
615
|
def compress_asset(content, type)
|
|
600
|
-
case compressor =
|
|
616
|
+
case compressor = assets_config[:"#{type}_compressor"]
|
|
601
617
|
when :none
|
|
602
618
|
return content
|
|
603
619
|
when nil
|
|
@@ -682,7 +698,7 @@ class Roda
|
|
|
682
698
|
# a different digest type or to return a static string if you don't
|
|
683
699
|
# want to use a unique value.
|
|
684
700
|
def asset_digest(content)
|
|
685
|
-
algo =
|
|
701
|
+
algo = assets_config.sri || :sha256
|
|
686
702
|
digest = begin
|
|
687
703
|
require 'openssl'
|
|
688
704
|
::OpenSSL::Digest
|
|
@@ -700,7 +716,7 @@ class Roda
|
|
|
700
716
|
# Return an array of paths for the given asset type and optionally
|
|
701
717
|
# asset group. See the assets function documentation for details.
|
|
702
718
|
def assets_paths(type)
|
|
703
|
-
o = self.class.
|
|
719
|
+
o = self.class.assets_config
|
|
704
720
|
if type.is_a?(Array)
|
|
705
721
|
ltype, *dirs = type
|
|
706
722
|
else
|
|
@@ -709,23 +725,29 @@ class Roda
|
|
|
709
725
|
stype = ltype.to_s
|
|
710
726
|
|
|
711
727
|
url_prefix = request.script_name if self.class.add_script_name?
|
|
712
|
-
relative_paths = o
|
|
728
|
+
relative_paths = o.relative_paths
|
|
713
729
|
|
|
714
|
-
paths = if o
|
|
715
|
-
relative_paths = false if o
|
|
730
|
+
paths = if o.compiled
|
|
731
|
+
relative_paths = false if o.compiled_asset_host
|
|
716
732
|
if ukey = _compiled_assets_hash(type, true)
|
|
717
|
-
["#{o
|
|
733
|
+
["#{o.compiled_asset_host}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}"]
|
|
718
734
|
else
|
|
719
735
|
[]
|
|
720
736
|
end
|
|
721
737
|
else
|
|
722
|
-
|
|
738
|
+
case ltype
|
|
739
|
+
when :css, :js
|
|
740
|
+
asset_dir = o[ltype]
|
|
741
|
+
else
|
|
742
|
+
RodaPlugins.warn("Invalid asset type given to assets_paths, such be :css or :js: #{ltype}", uplevel: 1)
|
|
743
|
+
end
|
|
744
|
+
|
|
723
745
|
if dirs && !dirs.empty?
|
|
724
746
|
dirs.each{|f| asset_dir = asset_dir[f]}
|
|
725
|
-
prefix = "#{dirs.join('/')}/" if o
|
|
747
|
+
prefix = "#{dirs.join('/')}/" if o.group_subdirs
|
|
726
748
|
end
|
|
727
749
|
Array(asset_dir).map do |f|
|
|
728
|
-
if ts = o
|
|
750
|
+
if ts = o.timestamp_paths
|
|
729
751
|
mtime = asset_last_modified(File.join(o[:"#{stype}_path"], *[prefix, f].compact))
|
|
730
752
|
mtime = "#{sprintf("%i%06i", mtime.to_i, mtime.usec)}#{ts}"
|
|
731
753
|
end
|
|
@@ -743,11 +765,11 @@ class Roda
|
|
|
743
765
|
end
|
|
744
766
|
|
|
745
767
|
# Return a string containing html tags for the given asset type.
|
|
746
|
-
# This will use a script tag for the
|
|
747
|
-
# the
|
|
768
|
+
# This will use a script tag for the +:js+ type and a link tag for
|
|
769
|
+
# the +:css+ type.
|
|
748
770
|
#
|
|
749
771
|
# To return the tags for a specific asset group, use an array for
|
|
750
|
-
# the type, such as [:css, :frontend]
|
|
772
|
+
# the type, such as <tt>[:css, :frontend]</tt>.
|
|
751
773
|
#
|
|
752
774
|
# You can specify custom attributes for the tag by passing a hash
|
|
753
775
|
# as the attrs argument.
|
|
@@ -758,8 +780,8 @@ class Roda
|
|
|
758
780
|
def assets(type, attrs = OPTS)
|
|
759
781
|
ltype = type.is_a?(Array) ? type[0] : type
|
|
760
782
|
|
|
761
|
-
o = self.class.
|
|
762
|
-
if o
|
|
783
|
+
o = self.class.assets_config
|
|
784
|
+
if o.compiled && (algo = o.sri) && (hash = _compiled_assets_hash(type))
|
|
763
785
|
attrs = Hash[attrs]
|
|
764
786
|
attrs[:integrity] = "#{algo}-#{h([[hash].pack('H*')].pack('m').tr("\n", ''))}"
|
|
765
787
|
end
|
|
@@ -775,13 +797,13 @@ class Roda
|
|
|
775
797
|
end
|
|
776
798
|
|
|
777
799
|
paths = assets_paths(type)
|
|
778
|
-
if o
|
|
800
|
+
if o.early_hints
|
|
779
801
|
early_hint_as = ltype == :js ? 'script' : 'style'
|
|
780
802
|
early_hints = paths.map{|p| "<#{p}>; rel=preload; as=#{early_hint_as}"}
|
|
781
803
|
early_hints = early_hints.join("\n") if Rack.release < '3'
|
|
782
804
|
send_early_hints(RodaResponseHeaders::LINK=>early_hints)
|
|
783
805
|
end
|
|
784
|
-
paths.map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join("\n")
|
|
806
|
+
paths.map!{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join("\n")
|
|
785
807
|
end
|
|
786
808
|
|
|
787
809
|
# Render the asset with the given filename. When assets are compiled,
|
|
@@ -792,11 +814,11 @@ class Roda
|
|
|
792
814
|
# In both cases, if the file has not been modified since the last request,
|
|
793
815
|
# this will return a 304 response.
|
|
794
816
|
def render_asset(file, type)
|
|
795
|
-
o = self.class.
|
|
796
|
-
if o
|
|
817
|
+
o = self.class.assets_config
|
|
818
|
+
if o.compiled
|
|
797
819
|
file = "#{o[:"compiled_#{type}_path"]}#{file}"
|
|
798
820
|
|
|
799
|
-
if o
|
|
821
|
+
if o.gzip && env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/
|
|
800
822
|
@_response[RodaResponseHeaders::CONTENT_ENCODING] = 'gzip'
|
|
801
823
|
file += '.gz'
|
|
802
824
|
end
|
|
@@ -814,21 +836,21 @@ class Roda
|
|
|
814
836
|
# Otherwise, render the file using the render plugin. +file+ should be
|
|
815
837
|
# the relative path to the file from the current directory.
|
|
816
838
|
def read_asset_file(file, type)
|
|
817
|
-
o = self.class.
|
|
839
|
+
o = self.class.assets_config
|
|
818
840
|
|
|
819
841
|
content = if file.end_with?(".#{type}")
|
|
820
842
|
::File.read(file)
|
|
821
843
|
else
|
|
822
|
-
render_asset_file(file, :template_opts=>o[:"#{type}_opts"], :dependencies=>o
|
|
844
|
+
render_asset_file(file, :template_opts=>o[:"#{type}_opts"], :dependencies=>o.expanded_dependencies[file])
|
|
823
845
|
end
|
|
824
846
|
|
|
825
|
-
o
|
|
847
|
+
o.postprocessor ? o.postprocessor.call(file, type, content) : content
|
|
826
848
|
end
|
|
827
849
|
|
|
828
850
|
private
|
|
829
851
|
|
|
830
852
|
def _compiled_assets_hash(type, return_ukey=false)
|
|
831
|
-
compiled = self.class.
|
|
853
|
+
compiled = self.class.assets_config.compiled
|
|
832
854
|
type, *dirs = type if type.is_a?(Array)
|
|
833
855
|
stype = type.to_s
|
|
834
856
|
|
|
@@ -849,8 +871,8 @@ class Roda
|
|
|
849
871
|
# other files, check the modification times of all dependencies and
|
|
850
872
|
# return the maximum.
|
|
851
873
|
def asset_last_modified(file)
|
|
852
|
-
if deps = self.class.
|
|
853
|
-
|
|
874
|
+
if deps = self.class.assets_config.expanded_dependencies[file]
|
|
875
|
+
[file, *deps].map!{|f| ::File.stat(f).mtime}.max
|
|
854
876
|
else
|
|
855
877
|
::File.stat(file).mtime
|
|
856
878
|
end
|
|
@@ -861,7 +883,7 @@ class Roda
|
|
|
861
883
|
# type-specific headers.
|
|
862
884
|
def check_asset_request(file, type, mtime)
|
|
863
885
|
@_request.last_modified(mtime)
|
|
864
|
-
@_response.headers.merge!(self.class.
|
|
886
|
+
@_response.headers.merge!(self.class.assets_config[:"#{type}_headers"])
|
|
865
887
|
end
|
|
866
888
|
|
|
867
889
|
# Render the given asset file using the render plugin, with the given options.
|
|
@@ -875,15 +897,24 @@ class Roda
|
|
|
875
897
|
# An array of asset type strings and regexps for that type, for all asset types
|
|
876
898
|
# handled.
|
|
877
899
|
def assets_matchers
|
|
878
|
-
@assets_matchers ||=
|
|
900
|
+
@assets_matchers ||= _build_assets_matchers
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
def freeze
|
|
904
|
+
assets_matchers
|
|
905
|
+
super
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
private
|
|
909
|
+
|
|
910
|
+
def _build_assets_matchers
|
|
911
|
+
[:css, :js].map! do |t|
|
|
879
912
|
if regexp = assets_regexp(t)
|
|
880
913
|
[t, regexp].freeze
|
|
881
914
|
end
|
|
882
915
|
end.compact.freeze
|
|
883
916
|
end
|
|
884
917
|
|
|
885
|
-
private
|
|
886
|
-
|
|
887
918
|
# A string for the asset filename for the asset type, key, and digest.
|
|
888
919
|
def _asset_regexp(type, key, digest)
|
|
889
920
|
"#{key.sub(/\A#{type}/, '')}.#{digest}.#{type}"
|
|
@@ -892,8 +923,8 @@ class Roda
|
|
|
892
923
|
# The regexp matcher to use for the given type. This handles any asset groups
|
|
893
924
|
# for the asset types.
|
|
894
925
|
def assets_regexp(type)
|
|
895
|
-
o = roda_class.
|
|
896
|
-
if compiled = o
|
|
926
|
+
o = roda_class.assets_config
|
|
927
|
+
if compiled = o.compiled
|
|
897
928
|
assets = compiled.
|
|
898
929
|
select{|k,_| k =~ /\A#{type}/}.
|
|
899
930
|
map{|k, md| _asset_regexp(type, k, md)}
|
|
@@ -902,7 +933,7 @@ class Roda
|
|
|
902
933
|
else
|
|
903
934
|
return unless assets = o[type]
|
|
904
935
|
assets = unnest_assets_hash(assets)
|
|
905
|
-
ts = o
|
|
936
|
+
ts = o.timestamp_paths
|
|
906
937
|
/#{o[:"#{type}_prefix"]}#{"\\d+#{ts}" if ts}(#{Regexp.union(assets.uniq)})#{o[:"#{type}_suffix"]}/
|
|
907
938
|
end
|
|
908
939
|
end
|
|
@@ -914,7 +945,7 @@ class Roda
|
|
|
914
945
|
when Hash
|
|
915
946
|
h.flat_map do |k,v|
|
|
916
947
|
assets = unnest_assets_hash(v)
|
|
917
|
-
assets = assets.map{|x| "#{k}/#{x}"} if roda_class.
|
|
948
|
+
assets = assets.map{|x| "#{k}/#{x}"} if roda_class.assets_config.group_subdirs
|
|
918
949
|
assets
|
|
919
950
|
end
|
|
920
951
|
else
|