class RGen::Util::FileCacheMap
Implements a cache for storing and loading data associated with arbitrary files. The data is stored in cache files within a subfolder of the folder where the associated files exists. The cache files are protected with a checksum and loaded data will be invalid in case either the associated file are the cache file has changed.
Attributes
optional program version info to be associated with the cache files if the program version changes, cached data will also be invalid
Public Class Methods
cache_dir
is the name of the subfolder where cache files are
created postfix
is an extension appended to the original file
name for creating the name of the cache file
# File lib/rgen/util/file_cache_map.rb, line 22 def initialize(cache_dir, postfix) @postfix = postfix @cache_dir = cache_dir end
Public Instance Methods
remove cache files which are not associated with any file in
key_paths
will only remove files within root_path
# File lib/rgen/util/file_cache_map.rb, line 80 def clean_unused(root_path, key_paths) raise "key paths must be within root path" unless key_paths.all?{|p| p.index(root_path) == 0} used_files = key_paths.collect{|p| cache_file(p)} files = Dir[root_path+"/**/"+@cache_dir+"/*"+@postfix] files.each do |f| FileUtils.rm(f) unless used_files.include?(f) end end
load data associated with file key_path
returns :invalid in
case either the associated file or the cache file has changed
options:
:invalidation_reasons: an array which will receive symbols indicating why the cache is invalid: :no_cachefile, :cachefile_corrupted, :keyfile_changed
# File lib/rgen/util/file_cache_map.rb, line 35 def load_data(key_path, options={}) reasons = options[:invalidation_reasons] || [] cf = cache_file(key_path) result = nil begin File.open(cf, "rb") do |f| header = f.read(41) if !header reasons << :cachefile_corrupted return :invalid end checksum = header[0..39] data = f.read if calc_sha1(data) == checksum if calc_sha1_keydata(key_path) == data[0..39] result = data[41..-1] else reasons << :keyfile_changed result = :invalid end else reasons << :cachefile_corrupted result = :invalid end end rescue Errno::ENOENT reasons << :no_cachefile result = :invalid end result end
store data value_data
associated with file
key_path
# File lib/rgen/util/file_cache_map.rb, line 68 def store_data(key_path, value_data) data = calc_sha1_keydata(key_path) + "\n" + value_data data = calc_sha1(data) + "\n" + data cf = cache_file(key_path) FileUtils.mkdir(File.dirname(cf)) rescue Errno::EEXIST File.open(cf, "wb") do |f| f.write(data) end end
Private Instance Methods
# File lib/rgen/util/file_cache_map.rb, line 91 def cache_file(path) File.dirname(path) + "/"+@cache_dir+"/" + File.basename(path) + @postfix end
# File lib/rgen/util/file_cache_map.rb, line 95 def calc_sha1(data) sha1 = Digest::SHA1.new sha1.update(data) sha1.hexdigest end
this method is much faster than calling keyData
and putting
the result in calc_sha1
reason is probably that there are not
so many big strings being created
# File lib/rgen/util/file_cache_map.rb, line 107 def calc_sha1_keydata(path) begin sha1 = Digest::SHA1.new sha1.file(path) sha1.update(@version_info.to_s) sha1.hexdigest rescue Errno::ENOENT "<missing_key_file>" end end
# File lib/rgen/util/file_cache_map.rb, line 101 def keyData(path) File.read(path)+@version_info.to_s end