From 3e8c37b6416a8a68c6f4d5dd4c2c921d3572d561 Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Thu, 2 May 2019 00:13:20 +0530 Subject: [PATCH] Refactor Jekyll::Cache (#7532) Merge pull request 7532 --- features/cache.feature | 9 +++ lib/jekyll/cache.rb | 135 ++++++++++++++++++++++------------------- lib/jekyll/site.rb | 2 +- 3 files changed, 81 insertions(+), 65 deletions(-) diff --git a/features/cache.feature b/features/cache.feature index 99aee245..f6f62af1 100644 --- a/features/cache.feature +++ b/features/cache.feature @@ -26,3 +26,12 @@ Feature: Cache But the .jekyll-cache directory should not exist And the _site directory should exist And I should see "

Hello World

" 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 "

Hello World

" in "_site/index.html" diff --git a/lib/jekyll/cache.rb b/lib/jekyll/cache.rb index fd4bde17..be9a1893 100644 --- a/lib/jekyll/cache.rb +++ b/lib/jekyll/cache.rb @@ -4,9 +4,59 @@ require "digest" 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 # @@ -14,27 +64,10 @@ module Jekyll # # 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 @@ module Jekyll 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 @@ module Jekyll # 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 @@ module Jekyll 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 @@ module Jekyll # 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 @@ module Jekyll 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 @@ module Jekyll # # Returns nothing. def delete_cache_files - FileUtils.rm_rf(path_to) if @@disk_cache_enabled + 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 - 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 @@ module Jekyll 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 diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index fbc03a4a..43a652cc 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -469,7 +469,7 @@ module Jekyll # 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