178 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| module Jekyll
 | |
|   class Regenerator
 | |
|     attr_reader :site, :metadata, :cache
 | |
|     attr_accessor :disabled
 | |
|     private :disabled, :disabled=
 | |
| 
 | |
|     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 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 && !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.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
 | |
| 
 | |
|       unless 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
 | |
|       unless disabled?
 | |
|         File.binwrite(metadata_file, Marshal.dump(metadata))
 | |
|       end
 | |
|     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?
 | |
|       self.disabled = !site.incremental? 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
 |