Class: Sprockets::Cache::FileStore
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | lib/sprockets/cache/file_store.rb |
Overview
Constant Summary
-
DEFAULT_MAX_SIZE =
# File 'lib/sprockets/cache/file_store.rb', line 22
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