class Pakyow::Assets::Asset

Represents an asset.

Instances are created when booting in all environments, meaning the app is guaranteed access to these objects. Contents are loaded and processed eagerly. This is expected to happen under two scenarios:

1. In development, an asset is loaded and processed when it's requested.
2. In production, when assets are precompiled during a deployment.

Attributes

dependencies[R]
logical_path[R]
mime_suffix[R]
mime_type[R]

Public Class Methods

inherited(asset_class) click to toggle source

@api private

Calls superclass method
# File lib/pakyow/assets/asset.rb, line 52
def inherited(asset_class)
  @__types << asset_class
  super
end
load() click to toggle source

Implemented by subclasses to load any libraries they need.

# File lib/pakyow/assets/asset.rb, line 47
def load
  # intentionally empty
end
new(local_path:, config:, dependencies: [], source_location: "", prefix: "/", related: []) click to toggle source
# File lib/pakyow/assets/asset.rb, line 91
def initialize(local_path:, config:, dependencies: [], source_location: "", prefix: "/", related: [])
  @local_path, @config, @source_location, @dependencies, @related = local_path, config, source_location, dependencies, related

  @logical_path = self.class.update_path_for_emitted_type(
    String.normalize_path(
      File.join(prefix, local_path.sub(source_location, ""))
    )
  )

  @public_path = String.normalize_path(
    File.join(config.prefix, @logical_path)
  )

  @mime_type = case File.extname(@public_path)
  when ".js"
    # Resolves an issue with mini_mime returning `application/ecmascript`
    #
    "application/javascript"
  else
    MiniMime.lookup_by_filename(@public_path)&.content_type.to_s
  end

  @mime_prefix, @mime_suffix = @mime_type.split("/", 2)

  @source_map_enabled = config.source_maps

  @mutex = Mutex.new
end
new_from_path(path, config:, source_location: "", prefix: "/", related: []) click to toggle source
# File lib/pakyow/assets/asset.rb, line 31
def new_from_path(path, config:, source_location: "", prefix: "/", related: [])
  asset_class = @__types.find { |type|
    type.__extensions.include?(File.extname(path))
  } || self

  asset_class.load; asset_class.new(
    local_path: path,
    source_location: source_location,
    config: config,
    prefix: prefix,
    related: related
  )
end
update_path_for_emitted_type(path) click to toggle source

@api private

# File lib/pakyow/assets/asset.rb, line 58
def update_path_for_emitted_type(path)
  if @__emits
    path.sub(File.extname(path), @__emits)
  else
    path
  end
end

Private Class Methods

emits(type) click to toggle source

Defines the emitted asset type (e.g. sass emits css).

# File lib/pakyow/assets/asset.rb, line 84
def emits(type)
  @__emits = ".#{type}"
end
extension(extension) click to toggle source

Registers extension for this asset.

# File lib/pakyow/assets/asset.rb, line 70
def extension(extension)
  extensions(extension)
end
extensions(*extensions) click to toggle source

Registers multiple extensions for this asset.

# File lib/pakyow/assets/asset.rb, line 76
def extensions(*extensions)
  extensions.each do |extension|
    @__extensions << ".#{extension}"
  end
end

Public Instance Methods

bytesize() click to toggle source
# File lib/pakyow/assets/asset.rb, line 148
def bytesize
  ensure_content(&:bytesize)
end
disable_source_map() click to toggle source
# File lib/pakyow/assets/asset.rb, line 195
def disable_source_map
  tap do
    @source_map_enabled = false
  end
end
each(&block) click to toggle source
# File lib/pakyow/assets/asset.rb, line 134
def each(&block)
  ensure_content do |content|
    StringIO.new(post_process(content)).each(&block)
  end
end
fingerprint() click to toggle source
# File lib/pakyow/assets/asset.rb, line 152
def fingerprint
  [@local_path].concat(dependencies).each_with_object(Digest::MD5.new) { |path, digest|
    digest.update(Digest::MD5.file(path).hexdigest)
  }.hexdigest
end
fingerprinted_filename() click to toggle source
# File lib/pakyow/assets/asset.rb, line 158
def fingerprinted_filename
  extension = File.extname(@public_path)
  File.basename(@public_path, extension) + "__" + fingerprint + extension
end
freeze() click to toggle source

Overriding and freezing after content is set lets us eagerly process the content rather than incurring that cost on boot.

Calls superclass method
# File lib/pakyow/assets/asset.rb, line 123
def freeze
  @freezing = true
  unless @freezing
    public_path
  end

  if instance_variable_defined?(:@content) && (!@config.fingerprint || instance_variable_defined?(:@fingerprinted_public_path))
    super
  end
end
public_path() click to toggle source
# File lib/pakyow/assets/asset.rb, line 163
def public_path
  if @config.fingerprint
    unless instance_variable_defined?(:@fingerprinted_public_path)
      @fingerprinted_public_path = File.join(
        File.dirname(@public_path),
        fingerprinted_filename
      )

      freeze
    end

    @fingerprinted_public_path
  else
    @public_path
  end
end
read() click to toggle source
# File lib/pakyow/assets/asset.rb, line 140
def read
  String.new.tap do |asset|
    each do |content|
      asset << content
    end
  end
end
source_map() click to toggle source
# File lib/pakyow/assets/asset.rb, line 184
def source_map
  if source_map?
    SourceMap.new(
      source_map_content,
      file: File.basename(public_path)
    )
  else
    nil
  end
end
source_map?() click to toggle source
# File lib/pakyow/assets/asset.rb, line 180
def source_map?
  respond_to?(:source_map_content)
end

Private Instance Methods

embed_mapping_url(content) click to toggle source
# File lib/pakyow/assets/asset.rb, line 242
def embed_mapping_url(content)
  content + SourceMap.mapping_url(path: public_path, type: @mime_suffix)
end
ensure_content() { |content| ... } click to toggle source
# File lib/pakyow/assets/asset.rb, line 203
def ensure_content
  @mutex.synchronize do
    unless frozen? || instance_variable_defined?(:@content)
      @content = load_content; freeze
    end
  end

  yield @content if block_given?
end
external?() click to toggle source
# File lib/pakyow/assets/asset.rb, line 246
def external?
  File.dirname(@local_path) == @config.externals.path
end
load_content() click to toggle source
# File lib/pakyow/assets/asset.rb, line 213
def load_content
  content = process(File.read(@local_path)).to_s

  if mime_suffix == "css" || mime_suffix == "javascript"
    # Update references to related assets with prefixed path, fingerprints.
    # Do this here rather than in post-processing so that the source maps reflect the changes.
    #
    @related.each do |asset|
      if asset != self && content.include?(asset.logical_path)
        content.gsub!(asset.logical_path, File.join(@config.host, asset.public_path))
      end
    end
  end

  content
end
post_process(content) click to toggle source
# File lib/pakyow/assets/asset.rb, line 234
def post_process(content)
  if @source_map_enabled && source_map?
    embed_mapping_url(content)
  else
    content
  end
end
process(content) click to toggle source
# File lib/pakyow/assets/asset.rb, line 230
def process(content)
  content
end