require'stringio'moduleSass# An abstract base class for backends for the Sass cache.# Any key-value store can act as such a backend;# it just needs to implement the# \{#_store} and \{#_retrieve} methods.## To use a cache store with Sass,# use the {file:SASS_REFERENCE.md#cache_store-option `:cache_store` option}.classCacheStore# Store cached contents for later retrieval# Must be implemented by all CacheStore subclasses## Note: cache contents contain binary data.## @param key [String] The key to store the contents under# @param version [String] The current sass version.# Cached contents must not be retrieved across different versions of sass.# @param sha [String] The sha of the sass source.# Cached contents must not be retrieved if the sha has changed.# @param contents [String] The contents to store.def_store(key,version,sha,contents)raise"#{self.class} must implement #_store."end# Retrieved cached contents.# Must be implemented by all subclasses.# # Note: if the key exists but the sha or version have changed,# then the key may be deleted by the cache store, if it wants to do so.## @param key [String] The key to retrieve# @param version [String] The current sass version.# Cached contents must not be retrieved across different versions of sass.# @param sha [String] The sha of the sass source.# Cached contents must not be retrieved if the sha has changed.# @return [String] The contents that were previously stored.# @return [NilClass] when the cache key is not found or the version or sha have changed.def_retrieve(key,version,sha)raise"#{self.class} must implement #_retrieve."end# Store a {Sass::Tree::RootNode}.## @param key [String] The key to store it under.# @param sha [String] The checksum for the contents that are being stored.# @param obj [Object] The object to cache.defstore(key,sha,root)_store(key,Sass::VERSION,sha,Sass::Util.dump(root))end# Retrieve a {Sass::Tree::RootNode}.## @param key [String] The key the root element was stored under.# @param sha [String] The checksum of the root element's content.# @return [Object] The cached object.defretrieve(key,sha)contents=_retrieve(key,Sass::VERSION,sha)Sass::Util.load(contents)ifcontentsrescueEOFError,TypeError,ArgumentError=>eraiseSass::Util.sass_warn"Warning. Error encountered while reading cache #{path_to(key)}: #{e}"end# Return the key for the sass file.## The `(sass_dirname, sass_basename)` pair# should uniquely identify the Sass document,# but otherwise there are no restrictions on their content.## @param sass_dirname [String]# The fully-expanded location of the Sass file.# This corresponds to the directory name on a filesystem.# @param sass_basename [String] The name of the Sass file that is being referenced.# This corresponds to the basename on a filesystem.defkey(sass_dirname,sass_basename)dir=Digest::SHA1.hexdigest(sass_dirname)filename="#{sass_basename}c""#{dir}/#{filename}"endend# A backend for the Sass cache using the filesystem.classFileCacheStore<CacheStore# The directory where the cached files will be stored.## @return [String]attr_accessor:cache_location# Create a new FileCacheStore.## @param cache_location [String] see \{#cache\_location}definitialize(cache_location)@cache_location=cache_locationend# @see {CacheStore#\_retrieve\_}def_retrieve(key,version,sha)returnunlessFile.readable?(path_to(key))contents=nilFile.open(path_to(key),"rb")do|f|iff.readline("\n").strip==version&&f.readline("\n").strip==shareturnf.readendendFile.unlinkpath_to(key)nilrescueEOFError,TypeError,ArgumentError=>eSass::Util.sass_warn"Warning. Error encountered while reading cache #{path_to(key)}: #{e}"end# @see {CacheStore#\_store\_}def_store(key,version,sha,contents)returnunlessFile.writable?(File.dirname(@cache_location))returnifFile.exists?(@cache_location)&&!File.writable?(@cache_location)compiled_filename=path_to(key)returnifFile.exists?(File.dirname(compiled_filename))&&!File.writable?(File.dirname(compiled_filename))returnifFile.exists?(compiled_filename)&&!File.writable?(compiled_filename)FileUtils.mkdir_p(File.dirname(compiled_filename))File.open(compiled_filename,"wb")do|f|f.puts(version)f.puts(sha)f.write(contents)endendprivate# Returns the path to a file for the given key.## @param key [String]# @return [String] The path to the cache file.defpath_to(key)File.join(cache_location,key)endend# A backend for the Sass cache using in-process memory.classInMemoryCacheStore<CacheStore# Since the {InMemoryCacheStore} is stored in the Sass tree's options hash,# when the options get serialized as part of serializing the tree,# you get crazy exponential growth in the size of the cached objects# unless you don't dump the cache.## @privatedef_dump(depth)""end# If we deserialize this class, just make a new empty one.## @privatedefself._load(repr)InMemoryCacheStore.newend# Create a new, empty cache store.definitialize@contents={}end# @see CacheStore#_retrievedef_retrieve(key,version,sha)if@contents.has_key?(key)returnunless@contents[key][:version]==versionreturnunless@contents[key][:sha]==shareturn@contents[key][:contents]endend# @see CacheStore#_storedef_store(key,version,sha,contents)@contents[key]={:version=>version,:sha=>sha,:contents=>contents}end# Destructively clear the cache.defreset!@contents={}endend# Doesn't store anything, but records what things it should have stored.# This doesn't currently have any use except for testing and debugging.## @privateclassNullCacheStore<CacheStoredefinitialize@keys={}enddef_retrieve(key,version,sha)nilenddef_store(key,version,sha,contents)@keys[key]=trueenddefwas_set?(key)@keys[key]endendend