194 lines
3.2 KiB
Ruby
194 lines
3.2 KiB
Ruby
|
class FSSM::Cache
|
||
|
module Common
|
||
|
include Enumerable
|
||
|
|
||
|
def initialize
|
||
|
@children = Hash.new
|
||
|
end
|
||
|
|
||
|
def each(prefix='./', &block)
|
||
|
@children.each do |segment, node|
|
||
|
cprefix = Pathname.for(prefix.dup).join(segment)
|
||
|
block.call(cprefix, node)
|
||
|
node.each(cprefix, &block)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
protected
|
||
|
|
||
|
def with_lock
|
||
|
@mutex.lock
|
||
|
yield
|
||
|
@mutex.unlock
|
||
|
end
|
||
|
|
||
|
def descendant(path)
|
||
|
recurse_on_key(path, false)
|
||
|
end
|
||
|
|
||
|
def descendant!(path)
|
||
|
recurse_on_key(path, true)
|
||
|
end
|
||
|
|
||
|
def child(segment)
|
||
|
has_child?(segment) ? @children["#{segment}"] : nil
|
||
|
end
|
||
|
|
||
|
def child!(segment)
|
||
|
(@children["#{segment}"] ||= Node.new)
|
||
|
end
|
||
|
|
||
|
def has_child?(segment)
|
||
|
@children.include?("#{segment}")
|
||
|
end
|
||
|
|
||
|
def remove_child(segment)
|
||
|
@children.delete("#{segment}")
|
||
|
end
|
||
|
|
||
|
def remove_children
|
||
|
@children.clear
|
||
|
end
|
||
|
|
||
|
def recurse_on_key(key, create)
|
||
|
key = sanitize_key(key)
|
||
|
node = self
|
||
|
|
||
|
until key.empty?
|
||
|
segment = key.shift
|
||
|
node = create ? node.child!(segment) : node.child(segment)
|
||
|
return nil unless node
|
||
|
end
|
||
|
|
||
|
node
|
||
|
end
|
||
|
|
||
|
def key_for_path(path)
|
||
|
Pathname.for(path).names
|
||
|
end
|
||
|
|
||
|
def relative_path(path)
|
||
|
sanitize_path(path, false)
|
||
|
end
|
||
|
|
||
|
def absolute_path(path)
|
||
|
sanitize_path(path, true)
|
||
|
end
|
||
|
|
||
|
def sanitize_path(path, absolute)
|
||
|
if path.is_a?(Array)
|
||
|
first = absolute ? '/' : path.shift
|
||
|
path = path.inject(Pathname.new("#{first}")) do |pathname, segment|
|
||
|
pathname.join("#{segment}")
|
||
|
end
|
||
|
path
|
||
|
else
|
||
|
path = Pathname.for(path)
|
||
|
absolute ? path.expand_path : path
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class Node
|
||
|
include Common
|
||
|
|
||
|
attr_accessor :mtime
|
||
|
attr_accessor :ftype
|
||
|
|
||
|
def <=>(other)
|
||
|
self.mtime <=> other.mtime
|
||
|
end
|
||
|
|
||
|
def from_path(path)
|
||
|
path = absolute_path(path)
|
||
|
@mtime = path.mtime
|
||
|
@ftype = path.ftype
|
||
|
end
|
||
|
|
||
|
protected
|
||
|
|
||
|
def sanitize_key(key)
|
||
|
key_for_path(relative_path(key))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
include Common
|
||
|
|
||
|
def initialize
|
||
|
@mutex = Mutex.new
|
||
|
super
|
||
|
end
|
||
|
|
||
|
def clear
|
||
|
@mutex.lock
|
||
|
@children.clear
|
||
|
@mutex.unlock
|
||
|
end
|
||
|
|
||
|
def set(path)
|
||
|
unset(path)
|
||
|
node = descendant!(path)
|
||
|
node.from_path(path)
|
||
|
node.mtime
|
||
|
end
|
||
|
|
||
|
def unset(path='/')
|
||
|
key = sanitize_key(path)
|
||
|
|
||
|
if key.empty?
|
||
|
self.clear
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
segment = key.pop
|
||
|
node = descendant(key)
|
||
|
|
||
|
return unless node
|
||
|
|
||
|
@mutex.lock
|
||
|
node.remove_child(segment)
|
||
|
@mutex.unlock
|
||
|
|
||
|
nil
|
||
|
end
|
||
|
|
||
|
def files
|
||
|
ftype('file')
|
||
|
end
|
||
|
|
||
|
def directories
|
||
|
ftype('directory')
|
||
|
end
|
||
|
|
||
|
protected
|
||
|
|
||
|
def each(&block)
|
||
|
prefix='/'
|
||
|
super(prefix, &block)
|
||
|
end
|
||
|
|
||
|
def ftype(ft)
|
||
|
inject({}) do |hash, entry|
|
||
|
path, node = entry
|
||
|
hash["#{path}"] = node.mtime if node.ftype == ft
|
||
|
hash
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def descendant(path)
|
||
|
node = recurse_on_key(path, false)
|
||
|
node
|
||
|
end
|
||
|
|
||
|
def descendant!(path)
|
||
|
@mutex.lock
|
||
|
node = recurse_on_key(path, true)
|
||
|
@mutex.unlock
|
||
|
node
|
||
|
end
|
||
|
|
||
|
def sanitize_key(key)
|
||
|
key_for_path(absolute_path(key))
|
||
|
end
|
||
|
end
|