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)

return the maximum.
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)

result in a single tag to the compiled asset file.
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)

asset group. See the assets function documentation for details.
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)

type-specific headers.
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)

the relative path to the file from the current directory.
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)

this will return a 304 response.
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)

+file+ should be the relative path to the file from the current directory.
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