lib/roda/plugins/assets.rb in roda-2.18.0 vs lib/roda/plugins/assets.rb in roda-2.19.0

- old
+ new

@@ -317,19 +317,21 @@ CSS_SUFFIX = '.css'.freeze HTTP_ACCEPT_ENCODING = 'HTTP_ACCEPT_ENCODING'.freeze CONTENT_ENCODING = 'Content-Encoding'.freeze GZIP = 'gzip'.freeze DOTGZ = '.gz'.freeze + EMPTY_ATTRS = {}.freeze # Internal exception raised when a compressor cannot be found CompressorNotFound = Class.new(RodaError) - # Load the render and caching plugins plugins, since the assets plugin + # Load the render, caching, and h plugins, since the assets plugin # depends on them. def self.load_dependencies(app, _opts = nil) app.plugin :render app.plugin :caching + app.plugin :h end # Setup the options for the plugin. See the Assets module RDoc # for a description of the supported options. def self.configure(app, opts = {}) @@ -603,39 +605,27 @@ module InstanceMethods # Return an array of paths for the given asset type and optionally # asset group. See the assets function documentation for details. def assets_paths(type) o = self.class.assets_opts - type, *dirs = type if type.is_a?(Array) - stype = type.to_s - ru = Rack::Utils + if type.is_a?(Array) + ltype, *dirs = type + else + ltype = type + end + stype = ltype.to_s url_prefix = request.script_name if self.class.opts[:add_script_name] if compiled = o[:compiled] - asset_host = o[:compiled_asset_host] - if dirs && !dirs.empty? - key = dirs.join(DOT) - ckey = "#{stype}.#{key}" - if hash = ukey = compiled[ckey] - ukey = "#{key}.#{ukey}" - end + if ukey = _compiled_assets_hash(type, true) + ["#{o[:compiled_asset_host]}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}"] else - hash = ukey = compiled[stype] - end - - if ukey - if algo = o[:sri] - integrity = "\" integrity=\"#{algo}-#{ru.escape_html([[hash].pack('H*')].pack('m').tr("\n", EMPTY_STRING))}" - end - - [ "#{asset_host}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}#{integrity}" ] - else [] end else - asset_dir = o[type] + asset_dir = o[ltype] if dirs && !dirs.empty? dirs.each{|f| asset_dir = asset_dir[f]} prefix = "#{dirs.join(SLASH)}/" if o[:group_subdirs] end Array(asset_dir).map{|f| "#{url_prefix}/#{o[:"#{stype}_prefix"]}#{prefix}#{f}#{o[:"#{stype}_suffix"]}"} @@ -650,32 +640,30 @@ # the type, such as [:css, :frontend]. # # When the assets are not compiled, this will result in a separate # tag for each asset file. When the assets are compiled, this will # result in a single tag to the compiled asset file. - def assets(type, attrs = nil) - ru = Rack::Utils - attrs = if attrs - attrs.map{|k,v| "#{k}=\"#{ru.escape_html(v.to_s)}\""}.join(SPACE) - else - EMPTY_STRING - end + def assets(type, attrs = EMPTY_ATTRS) + ltype = type.is_a?(Array) ? type[0] : type - ltype = if type.is_a?(Array) - type[0] - else - type + o = self.class.assets_opts + if o[:compiled] && (algo = o[:sri]) && (hash = _compiled_assets_hash(type)) + attrs = Hash[attrs] + attrs[:integrity] = "#{algo}-#{h([[hash].pack('H*')].pack('m').tr("\n", EMPTY_STRING))}" end + + attrs = attrs.map{|k,v| "#{k}=\"#{h(v)}\""}.join(SPACE) + if ltype == :js tag_start = "<script type=\"text/javascript\" #{attrs} src=\"" tag_end = JS_END else tag_start = "<link rel=\"stylesheet\" #{attrs} href=\"" tag_end = CSS_END end - assets_paths(type).map{|p| "#{tag_start}#{p}#{tag_end}"}.join(NEWLINE) + assets_paths(type).map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join(NEWLINE) end # Render the asset with the given filename. When assets are compiled, # or when the file is already of the given type (no rendering necessary), # this returns the contents of the compiled file. @@ -716,9 +704,27 @@ o[:postprocessor] ? o[:postprocessor].call(file, type, content) : content end private + + def _compiled_assets_hash(type, return_ukey=false) + compiled = self.class.assets_opts[:compiled] + type, *dirs = type if type.is_a?(Array) + stype = type.to_s + + if dirs && !dirs.empty? + key = dirs.join(DOT) + ckey = "#{stype}.#{key}" + if hash = ukey = compiled[ckey] + ukey = "#{key}.#{ukey}" + end + else + hash = ukey = compiled[stype] + end + + return_ukey ? ukey : hash + end # Return when the file was last modified. If the file depends on any # other files, check the modification times of all dependencies and # return the maximum. def asset_last_modified(file)