jekyll/lib/jekyll/regenerator.rb

175 lines
4.2 KiB
Ruby

module Jekyll
class Regenerator
attr_reader :site, :metadata, :cache
def initialize(site)
@site = site
# Read metadata from file
read_metadata
# Initialize cache to an empty hash
clear_cache
end
# Checks if a renderable object needs to be regenerated
#
# Returns a boolean.
def regenerate?(document)
case document
when Post, Page
document.asset_file? || document.data['regenerate'] ||
source_modified_or_dest_missing?(
site.in_source_dir(document.relative_path), document.destination(@site.dest)
)
when Document
!document.write? || document.data['regenerate'] ||
source_modified_or_dest_missing?(
document.path, document.destination(@site.dest)
)
else
source_path = document.respond_to?(:path) ? document.path : nil
dest_path = document.respond_to?(:destination) ? document.destination(@site.dest) : nil
source_modified_or_dest_missing?(source_path, dest_path)
end
end
# Add a path to the metadata
#
# Returns true, also on failure.
def add(path)
return true unless File.exist?(path)
metadata[path] = {
"mtime" => File.mtime(path),
"deps" => []
}
cache[path] = true
end
# Force a path to regenerate
#
# Returns true.
def force(path)
cache[path] = true
end
# Clear the metadata and cache
#
# Returns nothing
def clear
@metadata = {}
clear_cache
end
# Clear just the cache
#
# Returns nothing
def clear_cache
@cache = {}
end
# Checks if the source has been modified or the
# destination is missing
#
# returns a boolean
def source_modified_or_dest_missing?(source_path, dest_path)
modified?(source_path) || (dest_path and !File.exist?(dest_path))
end
# Checks if a path's (or one of its dependencies)
# mtime has changed
#
# Returns a boolean.
def modified?(path)
return true if disabled?
# objects that don't have a path are always regenerated
return true if path.nil?
# Check for path in cache
if cache.has_key? path
return cache[path]
end
# Check path that exists in metadata
data = metadata[path]
if data
data["deps"].each do |dependency|
if modified?(dependency)
return cache[dependency] = cache[path] = true
end
end
if File.exist?(path) && data["mtime"].eql?(File.mtime(path))
return cache[path] = false
else
return add(path)
end
end
# Path does not exist in metadata, add it
return add(path)
end
# Add a dependency of a path
#
# Returns nothing.
def add_dependency(path, dependency)
return if (metadata[path].nil? || @disabled)
if !metadata[path]["deps"].include? dependency
metadata[path]["deps"] << dependency
add(dependency) unless metadata.include?(dependency)
end
regenerate? dependency
end
# Write the metadata to disk
#
# Returns nothing.
def write_metadata
File.binwrite(metadata_file, Marshal.dump(metadata))
end
# Produce the absolute path of the metadata file
#
# Returns the String path of the file.
def metadata_file
site.in_source_dir('.jekyll-metadata')
end
# Check if metadata has been disabled
#
# Returns a Boolean (true for disabled, false for enabled).
def disabled?
@disabled = site.full_rebuild? if @disabled.nil?
@disabled
end
private
# Read metadata from the metadata file, if no file is found,
# initialize with an empty hash
#
# Returns the read metadata.
def read_metadata
@metadata = if !disabled? && File.file?(metadata_file)
content = File.binread(metadata_file)
begin
Marshal.load(content)
rescue TypeError
SafeYAML.load(content)
rescue ArgumentError => e
Jekyll.logger.warn("Failed to load #{metadata_file}: #{e}")
{}
end
else
{}
end
end
end
end