module Roda::RodaPlugins::Assets::InstanceMethods
def _compiled_assets_hash(type, return_ukey=false)
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('.') ckey = "#{stype}.#{key}" if hash = ukey = compiled[ckey] ukey = "#{key}.#{ukey}" end else hash = ukey = compiled[stype] end return_ukey ? ukey : hash end
def asset_last_modified(file)
other files, check the modification times of all dependencies and
Return when the file was last modified. If the file depends on any
def asset_last_modified(file) if deps = self.class.assets_opts[:expanded_dependencies][file] ([file] + Array(deps)).map{|f| ::File.stat(f).mtime}.max else ::File.stat(file).mtime end end
def assets(type, attrs = OPTS)
tag for each asset file. When the assets are compiled, this will
When the assets are not compiled, this will result in a separate
as the attrs argument.
You can specify custom attributes for the tag by passing a hash
the type, such as [:css, :frontend].
To return the tags for a specific asset group, use an array for
the :css type.
This will use a script tag for the :js type and a link tag for
Return a string containing html tags for the given asset type.
def assets(type, attrs = OPTS) ltype = type.is_a?(Array) ? type[0] : 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", ''))}" end attributes = attrs.map{|k,v| "#{k}=\"#{h(v)}\""}.join(' ') if ltype == :js tag_start = "<script#{' type="text/javascript"' unless attrs[:type]} #{attributes} src=\"" tag_end = "\"></script>" else tag_start = "<link rel=\"stylesheet\" #{attributes} href=\"" tag_end = "\" />" end paths = assets_paths(type) if o[:early_hints] early_hint_as = ltype == :js ? 'script' : 'style' send_early_hints('Link'=>paths.map{|p| "<#{p}>; rel=preload; as=#{early_hint_as}"}.join("\n")) end paths.map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join("\n") end
def assets_paths(type)
Return an array of paths for the given asset type and optionally
def assets_paths(type) o = self.class.assets_opts 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] relative_paths = o[:relative_paths] paths = if o[:compiled] relative_paths = false if o[:compiled_asset_host] if ukey = _compiled_assets_hash(type, true) ["#{o[:compiled_asset_host]}#{url_prefix}/#{o[:"compiled_#{stype}_prefix"]}.#{ukey}.#{stype}"] else [] end else asset_dir = o[ltype] if dirs && !dirs.empty? dirs.each{|f| asset_dir = asset_dir[f]} prefix = "#{dirs.join('/')}/" if o[:group_subdirs] end Array(asset_dir).map do |f| if ts = o[:timestamp_paths] mtime = asset_last_modified(File.join(o[:"#{stype}_path"], *[prefix, f].compact)) mtime = "#{sprintf("%i%06i", mtime.to_i, mtime.usec)}#{ts}" end "#{url_prefix}/#{o[:"#{stype}_prefix"]}#{mtime}#{prefix}#{f}#{o[:"#{stype}_suffix"]}" end end if relative_paths paths.map! do |path| "#{relative_prefix}#{path}" end end paths end
def check_asset_request(file, type, mtime)
a 304 response immediately. Otherwise, add the appropriate
If the asset hasn't been modified since the last request, return
def check_asset_request(file, type, mtime) @_request.last_modified(mtime) @_response.headers.merge!(self.class.assets_opts[:"#{type}_headers"]) end
def read_asset_file(file, type)
Otherwise, render the file using the render plugin. +file+ should be
Return the content of the file if it is already of the correct type.
def read_asset_file(file, type) o = self.class.assets_opts content = if file.end_with?(".#{type}") ::File.read(file) else render_asset_file(file, :template_opts=>o[:"#{type}_opts"], :dependencies=>o[:expanded_dependencies][file]) end o[:postprocessor] ? o[:postprocessor].call(file, type, content) : content end
def render_asset(file, type)
In both cases, if the file has not been modified since the last request,
this will render the asset using the render plugin.
When assets are not compiled and the file is not already in the same format,
this returns the contents of the compiled file.
or when the file is already of the given type (no rendering necessary),
Render the asset with the given filename. When assets are compiled,
def render_asset(file, type) o = self.class.assets_opts if o[:compiled] file = "#{o[:"compiled_#{type}_path"]}#{file}" if o[:gzip] && env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/ @_response[RodaResponseHeaders::CONTENT_ENCODING] = 'gzip' file += '.gz' end check_asset_request(file, type, ::File.stat(file).mtime) ::File.read(file) else file = "#{o[:"#{type}_path"]}#{file}" check_asset_request(file, type, asset_last_modified(file)) read_asset_file(file, type) end end
def render_asset_file(file, options)
Render the given asset file using the render plugin, with the given options.
def render_asset_file(file, options) render_template({:path => file}, options) end