Class: Sprockets::Cache::FileStore
Relationships & Source Files | |
Inherits: | Object |
Defined in: | lib/sprockets/cache/file_store.rb |
Overview
Constant Summary
-
DEFAULT_MAX_SIZE =
Internal: Default key limit for store.
25 * 1024 * 1024
-
EXCLUDED_DIRS =
# File 'lib/sprockets/cache/file_store.rb', line 23['.', '..'].freeze
-
GITKEEP_FILES =
# File 'lib/sprockets/cache/file_store.rb', line 24['.gitkeep', '.keep'].freeze
Class Method Summary
-
.default_logger
Internal: Default standard error fatal logger.
-
.new(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger) ⇒ FileStore
constructor
Public: Initialize the cache store.
Instance Method Summary
-
#clear(options = nil)
Public: Clear the cache.
-
#get(key)
Public: Retrieve value from cache.
-
#inspect
Public: Pretty inspect.
-
#set(key, value)
Public: Set a key and value in the cache.
- #compute_size(caches) private
-
#find_caches
private
Internal: Get all cache files along with stats.
- #gc! private
- #safe_open(path, &block) private
- #safe_stat(fn) private
- #size private
Constructor Details
.new(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger) ⇒ FileStore
Public: Initialize the cache store.
root - A String path to a directory to persist cached values to. max_size - A Integer of the maximum size the store will hold (in bytes).
(default: 25MB).
logger - The logger to which some info will be printed.
(default logger level is FATAL and won't output anything).
# File 'lib/sprockets/cache/file_store.rb', line 42
def initialize(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger) @root = root @max_size = max_size @gc_size = max_size * 0.75 @logger = logger end
Class Method Details
.default_logger
Internal: Default standard error fatal logger.
Returns a Logger.
# File 'lib/sprockets/cache/file_store.rb', line 29
def self.default_logger logger = Logger.new($stderr) logger.level = Logger::FATAL logger end
Instance Method Details
#clear(options = nil)
Public: Clear the cache
adapted from ActiveSupport::Cache::FileStore#clear
Deletes all items from the cache. In this case it deletes all the entries in the specified file store directory except for .keep
or .gitkeep
. Be careful which directory is specified as @root because everything in that directory will be deleted.
Returns true
# File 'lib/sprockets/cache/file_store.rb', line 139
def clear(=nil) if File.exist?(@root) root_dirs = Dir.entries(@root).reject { |f| (EXCLUDED_DIRS + GITKEEP_FILES).include?(f) } FileUtils.rm_r(root_dirs.collect{ |f| File.join(@root, f) }) end true end
#compute_size(caches) (private)
[ GitHub ]# File 'lib/sprockets/cache/file_store.rb', line 166
def compute_size(caches) caches.inject(0) { |sum, (_, stat)| sum + stat.size } end
#find_caches (private)
Internal: Get all cache files along with stats.
Returns an Array of [String filename, File::Stat] pairs sorted by mtime.
# File 'lib/sprockets/cache/file_store.rb', line 152
def find_caches Dir.glob(File.join(@root, '**/*.cache')).reduce([]) { |stats, filename| stat = safe_stat(filename) # stat maybe nil if file was removed between the time we called # dir.glob and the next stat stats << [filename, stat] if stat stats }.sort_by { |_, stat| stat.mtime.to_i } end
#gc! (private)
[ GitHub ]# File 'lib/sprockets/cache/file_store.rb', line 183
def gc! start_time = Time.now caches = find_caches size = compute_size(caches) delete_caches, keep_caches = caches.partition { |filename, stat| deleted = size > @gc_size size -= stat.size deleted } return if delete_caches.empty? FileUtils.remove(delete_caches.map(&:first), force: true) @size = compute_size(keep_caches) @logger.warn do secs = Time.now.to_f - start_time.to_f "#{self.class}[#{@root}] garbage collected " + "#{delete_caches.size} files (#{(secs * 1000).to_i}ms)" end end
#get(key)
Public: Retrieve value from cache.
This API should not be used directly, but via the Cache
wrapper API.
key - String cache key.
Returns Object or nil or the value is not set.
# File 'lib/sprockets/cache/file_store.rb', line 56
def get(key) path = File.join(@root, "#{key}.cache") value = safe_open(path) do |f| begin EncodingUtils.unmarshaled_deflated(f.read, Zlib::MAX_WBITS) rescue Exception => e @logger.error do "#{self.class}[#{path}] could not be unmarshaled: " + "#{e.class}: #{e.}" end nil end end if value FileUtils.touch(path) value end end
#inspect
Public: Pretty inspect
Returns String.
# File 'lib/sprockets/cache/file_store.rb', line 126
def inspect "#<#{self.class} size=#{size}/#{@max_size}>" end
#safe_open(path, &block) (private)
[ GitHub ]# File 'lib/sprockets/cache/file_store.rb', line 176
def safe_open(path, &block) if File.exist?(path) File.open(path, 'rb', &block) end rescue Errno::ENOENT end
#safe_stat(fn) (private)
[ GitHub ]# File 'lib/sprockets/cache/file_store.rb', line 170
def safe_stat(fn) File.stat(fn) rescue Errno::ENOENT nil end
#set(key, value)
Public: Set a key and value in the cache.
This API should not be used directly, but via the Cache
wrapper API.
key - String cache key. value - Object value.
Returns Object value.
# File 'lib/sprockets/cache/file_store.rb', line 85
def set(key, value) path = File.join(@root, "#{key}.cache") # Ensure directory exists FileUtils.mkdir_p File.dirname(path) # Check if cache exists before writing exists = File.exist?(path) # Serialize value marshaled = Marshal.dump(value) # Compress if larger than 4KB if marshaled.bytesize > 4 * 1024 deflater = Zlib::Deflate.new( Zlib::BEST_COMPRESSION, Zlib::MAX_WBITS, Zlib::MAX_MEM_LEVEL, Zlib::DEFAULT_STRATEGY ) deflater << marshaled raw = deflater.finish else raw = marshaled end # Write data PathUtils.atomic_write(path) do |f| f.write(raw) @size = size + f.size unless exists end # GC if necessary gc! if size > @max_size value end
#size (private)
[ GitHub ]# File 'lib/sprockets/cache/file_store.rb', line 162
def size @size ||= compute_size(find_caches) end