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

version_info[RW]

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

new(cache_dir, postfix) click to toggle source

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

clean_unused(root_path, key_paths) click to toggle source

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(key_path, options={}) click to toggle source

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(key_path, value_data) click to toggle source

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

cache_file(path) click to toggle source
# File lib/rgen/util/file_cache_map.rb, line 91
def cache_file(path)
  File.dirname(path) + "/"+@cache_dir+"/" + File.basename(path) + @postfix 
end
calc_sha1(data) click to toggle source
# File lib/rgen/util/file_cache_map.rb, line 95
def calc_sha1(data)
  sha1 = Digest::SHA1.new
  sha1.update(data)
  sha1.hexdigest
end
calc_sha1_keydata(path) click to toggle source

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
keyData(path) click to toggle source
# File lib/rgen/util/file_cache_map.rb, line 101
def keyData(path)
  File.read(path)+@version_info.to_s
end