module Jekyll::DistorteD::StaticState

This module implements the methods our tag needs in order to pretend to be a Jekyll::StaticFile so we don't need to redundantly re-implement a Generator and Jekyll::Cleaner.

Public Instance Methods

destination(dest_root) click to toggle source

Returns the to-be-written path of a single standard StaticFile. The value returned by this method is only the 'main' or 'original' (even if modified somehow) file and does not include the path/filenames of any variations. This method will be called by jekyll/lib/cleaner#new_files to generate the list of files that need to be build or rebuilt for a site. For this reason, this method shouldn't do any kind of checking the real filesystem, since e.g. its URL-based destdir might not exist yet if the Site.dest is completely blank.

# File lib/distorted-jekyll/static_state.rb, line 26
def destination(dest_root)
  File.join(dest_root, @relative_dest, @name)
end
destinations(dest_root) click to toggle source

This method will be called by our monkey-patched Jekyll::Cleaner#new_files in place of the single-destination method usually used. This allows us to tell Jekyll about more than a single file that should be kept when regenerating the site. This makes DistorteD fast!

# File lib/distorted-jekyll/static_state.rb, line 35
def destinations(dest_root)
  changes&.flat_map { |change| change.paths(dest_root) }
end
modified?() click to toggle source

HACK HACK HACK Jekyll does not pass this method a site.dest like it does write() and others, but I want to be able to short-circuit here if all the to-be-generated files already exist.

# File lib/distorted-jekyll/static_state.rb, line 43
def modified?
  # Assume modified for the sake of freshness :)
  modified = true

  site_dest = the_setting_sun(:jekyll, :destination).to_s
  if Dir.exist?(site_dest)
    if Dir.exist?(File.join(site_dest, @relative_dest))
      extant_files = Dir.entries(File.join(site_dest, @relative_dest)).to_set

      # TODO: Make this smarter. It's not enough that all the generated
      # filenames should exist. Try a few more ways to detect subtler
      # "changes to the source file since generation of variations.
      if wanted_files.subset?(extant_files)
        Jekyll.logger.debug(@name, "All variations present: #{wanted_files}")
        modified = false
      else
        Jekyll.logger.debug(@name, "Missing variations: #{wanted_files - extant_files}")
      end

    end  # relative_dest.exists?
  end  # site_dest.exists?
  Jekyll.logger.debug("#{@name} modified?",  modified)
  return modified
end
write(dest_root) click to toggle source

Write the static file to the destination directory (if modified).

dest - The String path to the destination dir.

Returns false if the file was not modified since last time (no-op).

# File lib/distorted-jekyll/static_state.rb, line 84
  def write(dest_root)
    return false if File.exist?(path) && !modified?

    # Create any directories to the depth of the intended destination.
    FileUtils.mkdir_p(File.join(dest_root, @relative_dest))
    # Save every desired variation of this image.
    # This will be a Set of Hashes each describing the name, type,
    # dimensions, attributes, etc of each output variation we want.
    # Full-size outputs will have the special tag `:full`.
    changes&.each { |change|
      if self.respond_to?(change.type.distorted_file_method)
        Jekyll.logger.debug("DistorteD::#{change.type.distorted_file_method}", change.name)
        # WISHLIST: Remove the empty final positional Hash argument once we require a Ruby version
        # that will not perform the implicit Change-to-Hash conversion due to Change's
        # implementation of :to_hash. Ruby 2.7 will complain but still do the conversion,
        # breaking downstream callers that want a Struct they can call arbitrary key methods on.
        # https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
        self.send(change.type.distorted_file_method, dest_root, change, **{})
      elsif extname == ".#{change.type.preferred_extension}"
        Jekyll.logger.debug(@name, <<~RAWCOPY
            No #{change.type.distorted_file_method} method is defined,
            but the intended output type #{change.type.to_s} is the same
            as the input type, so I will fall back to copying the raw file.
          RAWCOPY
        )
        copy_file(change.paths(dest_root).first)
      else
        Jekyll.logger.error(@name, "Missing write method #{change.type.distorted_file_method}")
        raise MediaTypeOutputNotImplementedError.new(change.path(dest_root), type_mars, self.class.name)
      end
    }
  end
write?() click to toggle source

Whether to write the file to the filesystem

Returns true unless the defaults for the destination path from _config.yml contain `published: false`.

# File lib/distorted-jekyll/static_state.rb, line 72
def write?
  publishable = defaults.fetch('published'.freeze, true)
  return publishable unless @collection

  publishable && @collection.write?
end

Private Instance Methods

copy_file(dest_path, *a, **k) click to toggle source
# File lib/distorted-jekyll/static_state.rb, line 119
def copy_file(dest_path, *a, **k)
  if @site.safe || Jekyll.env == "production"
    FileUtils.cp(path, dest_path)
  else
    FileUtils.copy_entry(path, dest_path)
  end
end
extname() click to toggle source

Returns the extname /!\ including the dot /!\

# File lib/distorted-jekyll/static_state.rb, line 130
def extname
  File.extname(@name)
end
mtime() click to toggle source

Returns last modification time for this file.

# File lib/distorted-jekyll/static_state.rb, line 135
def mtime
  (@modified_time ||= File.stat(path).mtime).to_i
end
path() click to toggle source

Returns source file path.

# File lib/distorted-jekyll/static_state.rb, line 140
def path
  @path ||= begin
    # Static file is from a collection inside custom collections directory
    if !@collection.nil? && !@site.config['collections_dir'.freeze].empty?
      File.join(*[@base, @site.config['collections_dir'.freeze], @dir, @name].compact)
    else
      File.join(*[@base, @dir, @name].compact)
    end
  end
end
wanted_files() click to toggle source

Returns a Set of just the String filenames we want for this media. This will be used by `modified?` among others.

# File lib/distorted-jekyll/static_state.rb, line 154
def wanted_files
  # Cooltrainer::Change#names returns an Array[String], so we must concat every Change into one.
  changes.map(&:names).reduce(&:concat).to_set
end