Skip to content
Permalink
Browse files

Refactor Jekyll::Cache (#7532)

Merge pull request 7532
  • Loading branch information...
ashmaroli authored and jekyllbot committed May 1, 2019
1 parent 17a5f81 commit 3e8c37b6416a8a68c6f4d5dd4c2c921d3572d561
Showing with 81 additions and 65 deletions.
  1. +9 −0 features/cache.feature
  2. +71 −64 lib/jekyll/cache.rb
  3. +1 −1 lib/jekyll/site.rb
@@ -26,3 +26,12 @@ Feature: Cache
But the .jekyll-cache directory should not exist
And the _site directory should exist
And I should see "<p>Hello World</p>" in "_site/index.html"

Scenario: Disk usage in safe mode
Given I have an "index.md" page that contains "{{ site.title }}"
And I have a configuration file with "title" set to "Hello World"
When I run jekyll build --safe
Then I should get a zero exit status
But the .jekyll-cache directory should not exist
And the _site directory should exist
And I should see "<p>Hello World</p>" in "_site/index.html"
@@ -4,37 +4,70 @@

module Jekyll
class Cache
# rubocop:disable Style/ClassVars
@@caches = {}
@@disk_cache_enabled = true
# class-wide base cache
@base_cache = {}

# class-wide directive to write cache to disk is enabled by default
@disk_cache_enabled = true

class << self
# class-wide cache location
attr_accessor :cache_dir

# class-wide directive to write cache to disk
attr_reader :disk_cache_enabled

# class-wide base cache reader
attr_reader :base_cache

# Disable Marshaling cached items to disk
def disable_disk_cache!
@disk_cache_enabled = false
end

# Clear all caches
def clear
delete_cache_files
base_cache.each_value(&:clear)
end

# Compare the current config to the cached config
# If they are different, clear all caches
#
# Returns nothing.
def clear_if_config_changed(config)
config = config.inspect
cache = Jekyll::Cache.new "Jekyll::Cache"
return if cache.key?("config") && cache["config"] == config

clear
cache = Jekyll::Cache.new "Jekyll::Cache"
cache["config"] = config
nil
end

private

# Delete all cached items from all caches
#
# Returns nothing.
def delete_cache_files
FileUtils.rm_rf(@cache_dir) if disk_cache_enabled
end
end

#

# Get an existing named cache, or create a new one if none exists
#
# name - name of the cache
#
# Returns nothing.
def initialize(name)
@cache = @@caches[name] ||= {}
@cache = Jekyll::Cache.base_cache[name] ||= {}
@name = name.gsub(%r![^\w\s-]!, "-")
end

# Set class-wide base_dir
def self.base_dir=(dir_path)
@@base_dir = dir_path
end

# Disable Marshaling cached items to disk
def self.disable_disk_cache!
@@disk_cache_enabled = false
end
# rubocop:enable Style/ClassVars

# Clear all caches
def self.clear
delete_cache_files
@@caches.each_value(&:clear)
end

# Clear this particular cache
def clear
delete_cache_files
@@ -49,7 +82,7 @@ def [](key)
return @cache[key] if @cache.key?(key)

path = path_to(hash(key))
if @@disk_cache_enabled && File.file?(path) && File.readable?(path)
if disk_cache_enabled? && File.file?(path) && File.readable?(path)
@cache[key] = load(path)
else
raise
@@ -61,7 +94,7 @@ def [](key)
# Returns nothing.
def []=(key, value)
@cache[key] = value
return unless @@disk_cache_enabled
return unless disk_cache_enabled?

path = path_to(hash(key))
value = new Hash(value) if value.is_a?(Hash) && !value.default.nil?
@@ -70,9 +103,8 @@ def []=(key, value)
Jekyll.logger.debug "Cache:", "Cannot dump object #{key}"
end

# If an item already exists in the cache, retrieve it
# Else execute code block, and add the result to the cache, and return that
# result
# If an item already exists in the cache, retrieve it.
# Else execute code block, and add the result to the cache, and return that result.
def getset(key)
self[key]
rescue StandardError
@@ -86,10 +118,7 @@ def getset(key)
# Returns nothing.
def delete(key)
@cache.delete(key)
return unless @@disk_cache_enabled

path = path_to(hash(key))
File.delete(path)
File.delete(path_to(hash(key))) if disk_cache_enabled?
end

# Check if `key` already exists in this cache
@@ -100,40 +129,27 @@ def key?(key)
return true if @cache.key?(key)
# Otherwise, it might be cached on disk
# but we should not consider the disk cache if it is disabled
return false unless @@disk_cache_enabled
return false unless disk_cache_enabled?

path = path_to(hash(key))
File.file?(path) && File.readable?(path)
end

# Compare the current config to the cached config
# If they are different, clear all caches
#
# Returns nothing.
def self.clear_if_config_changed(config)
config = config.inspect
cache = Jekyll::Cache.new "Jekyll::Cache"
return if cache.key?("config") && cache["config"] == config

clear
cache = Jekyll::Cache.new "Jekyll::Cache"
cache["config"] = config
nil
def disk_cache_enabled?
!!Jekyll::Cache.disk_cache_enabled
end

private

# Given a hashed key, return the path to where this item would be saved on
# disk
# Given a hashed key, return the path to where this item would be saved on disk.
def path_to(hash = nil)
@base_dir ||= File.join(@@base_dir, @name)
@base_dir ||= File.join(Jekyll::Cache.cache_dir, @name)
return @base_dir if hash.nil?

File.join(@base_dir, hash[0..1], hash[2..-1]).freeze
end

# Given a key, return a SHA2 hash that can be used for caching this item to
# disk
# Given a key, return a SHA2 hash that can be used for caching this item to disk.
def hash(key)
Digest::SHA2.hexdigest(key).freeze
end
@@ -142,22 +158,14 @@ def hash(key)
#
# Returns nothing.
def delete_cache_files
FileUtils.rm_rf(path_to) if @@disk_cache_enabled
end

# Delete all cached items from all caches
#
# Returns nothing.
def self.delete_cache_files
FileUtils.rm_rf(@@base_dir) if @@disk_cache_enabled
FileUtils.rm_rf(path_to) if disk_cache_enabled?
end
private_class_method :delete_cache_files

# Load `path` from disk and return the result
# Load `path` from disk and return the result.
# This MUST NEVER be called in Safe Mode
# rubocop:disable Security/MarshalLoad
def load(path)
raise unless @@disk_cache_enabled
raise unless disk_cache_enabled?

cached_file = File.open(path, "rb")
value = Marshal.load(cached_file)
@@ -166,15 +174,14 @@ def load(path)
end
# rubocop:enable Security/MarshalLoad

# Given a path and a value, save value to disk at path
# Given a path and a value, save value to disk at path.
# This should NEVER be called in Safe Mode
#
# Returns nothing.
def dump(path, value)
return unless @@disk_cache_enabled
return unless disk_cache_enabled?

dir = File.dirname(path)
FileUtils.mkdir_p(dir)
FileUtils.mkdir_p(File.dirname(path))
File.open(path, "wb") do |cached_file|
Marshal.dump(value, cached_file)
end
@@ -469,7 +469,7 @@ def site_cleaner

# Disable Marshaling cache to disk in Safe Mode
def configure_cache
Jekyll::Cache.base_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
Jekyll::Cache.disable_disk_cache! if safe
end

0 comments on commit 3e8c37b

Please sign in to comment.
You can’t perform that action at this time.