Change FSSM to a gem dependency instead of vendoring it. Closes GH-343.
This commit is contained in:
parent
be8e5f5313
commit
7b0bb7ad2b
3
Gemfile
3
Gemfile
@ -1,5 +1,7 @@
|
|||||||
source :rubygems
|
source :rubygems
|
||||||
|
|
||||||
|
gemspec
|
||||||
|
|
||||||
gem "compass", :path => "."
|
gem "compass", :path => "."
|
||||||
gem "cucumber", "~> 0.9.2"
|
gem "cucumber", "~> 0.9.2"
|
||||||
gem "rspec", "~>2.0.0"
|
gem "rspec", "~>2.0.0"
|
||||||
@ -11,6 +13,7 @@ gem "haml", "~> 3.1.0.alpha"
|
|||||||
gem "rcov"
|
gem "rcov"
|
||||||
gem "rubyzip"
|
gem "rubyzip"
|
||||||
gem "livereload"
|
gem "livereload"
|
||||||
|
gem "rb-fsevent"
|
||||||
|
|
||||||
gem "ruby-prof" unless RUBY_PLATFORM == "java"
|
gem "ruby-prof" unless RUBY_PLATFORM == "java"
|
||||||
|
|
||||||
|
@ -7,8 +7,9 @@ GIT
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
compass (0.11.beta.7.3e013b8)
|
compass (0.11.beta.7.37998a4)
|
||||||
chunky_png (~> 1.1.0)
|
chunky_png (~> 1.1.0)
|
||||||
|
fssm (~> 0.2)
|
||||||
sass (>= 3.1.0.alpha.249)
|
sass (>= 3.1.0.alpha.249)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
@ -65,6 +66,7 @@ GEM
|
|||||||
abstract (>= 1.0.0)
|
abstract (>= 1.0.0)
|
||||||
eventmachine (0.12.10)
|
eventmachine (0.12.10)
|
||||||
eventmachine (0.12.10-java)
|
eventmachine (0.12.10-java)
|
||||||
|
fssm (0.2.6.1)
|
||||||
gherkin (2.2.9)
|
gherkin (2.2.9)
|
||||||
json (~> 1.4.6)
|
json (~> 1.4.6)
|
||||||
term-ansicolor (~> 1.0.5)
|
term-ansicolor (~> 1.0.5)
|
||||||
@ -106,6 +108,7 @@ GEM
|
|||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (~> 0.14.4)
|
thor (~> 0.14.4)
|
||||||
rake (0.8.7)
|
rake (0.8.7)
|
||||||
|
rb-fsevent (0.4.0)
|
||||||
rcov (0.9.9)
|
rcov (0.9.9)
|
||||||
rcov (0.9.9-java)
|
rcov (0.9.9-java)
|
||||||
rspec (2.0.1)
|
rspec (2.0.1)
|
||||||
@ -144,6 +147,7 @@ DEPENDENCIES
|
|||||||
livereload
|
livereload
|
||||||
mocha
|
mocha
|
||||||
rails (~> 3.0.0.rc)
|
rails (~> 3.0.0.rc)
|
||||||
|
rb-fsevent
|
||||||
rcov
|
rcov
|
||||||
rspec (~> 2.0.0)
|
rspec (~> 2.0.0)
|
||||||
ruby-prof
|
ruby-prof
|
||||||
|
@ -17,6 +17,7 @@ Gem::Specification.new do |gemspec|
|
|||||||
|
|
||||||
gemspec.add_dependency 'sass', '>= 3.1.0.alpha.249'
|
gemspec.add_dependency 'sass', '>= 3.1.0.alpha.249'
|
||||||
gemspec.add_dependency 'chunky_png', '~> 1.1.0'
|
gemspec.add_dependency 'chunky_png', '~> 1.1.0'
|
||||||
|
gemspec.add_dependency 'fssm', '~> 0.2'
|
||||||
|
|
||||||
gemspec.files = %w(README.markdown LICENSE.markdown VERSION.yml Rakefile)
|
gemspec.files = %w(README.markdown LICENSE.markdown VERSION.yml Rakefile)
|
||||||
gemspec.files += Dir.glob("bin/*")
|
gemspec.files += Dir.glob("bin/*")
|
||||||
|
@ -39,12 +39,7 @@ module Compass
|
|||||||
check_for_sass_files!(new_compiler_instance)
|
check_for_sass_files!(new_compiler_instance)
|
||||||
recompile
|
recompile
|
||||||
|
|
||||||
begin
|
require 'fssm'
|
||||||
require 'fssm'
|
|
||||||
rescue LoadError
|
|
||||||
$: << File.join(Compass.lib_directory, 'vendor', 'fssm')
|
|
||||||
retry
|
|
||||||
end
|
|
||||||
|
|
||||||
if options[:poll]
|
if options[:poll]
|
||||||
require "fssm/backends/polling"
|
require "fssm/backends/polling"
|
||||||
|
33
lib/vendor/fssm/fssm.rb
vendored
33
lib/vendor/fssm/fssm.rb
vendored
@ -1,33 +0,0 @@
|
|||||||
dir = File.dirname(__FILE__)
|
|
||||||
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
|
|
||||||
|
|
||||||
module FSSM
|
|
||||||
FileNotFoundError = Class.new(StandardError)
|
|
||||||
CallbackError = Class.new(StandardError)
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def dbg(msg=nil)
|
|
||||||
STDERR.puts(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
def monitor(*args, &block)
|
|
||||||
monitor = FSSM::Monitor.new
|
|
||||||
FSSM::Support.use_block(args.empty? ? monitor : monitor.path(*args), block)
|
|
||||||
|
|
||||||
monitor.run
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'thread'
|
|
||||||
|
|
||||||
require 'fssm/pathname'
|
|
||||||
require 'fssm/support'
|
|
||||||
require 'fssm/tree'
|
|
||||||
require 'fssm/path'
|
|
||||||
require 'fssm/state/directory'
|
|
||||||
require 'fssm/state/file'
|
|
||||||
require 'fssm/monitor'
|
|
||||||
|
|
||||||
require "fssm/backends/#{FSSM::Support.backend.downcase}"
|
|
||||||
FSSM::Backends::Default = FSSM::Backends.const_get(FSSM::Support.backend)
|
|
36
lib/vendor/fssm/fssm/backends/fsevents.rb
vendored
36
lib/vendor/fssm/fssm/backends/fsevents.rb
vendored
@ -1,36 +0,0 @@
|
|||||||
require File.join(File.dirname(__FILE__), 'rubycocoa/fsevents')
|
|
||||||
|
|
||||||
module FSSM::Backends
|
|
||||||
class FSEvents
|
|
||||||
def initialize
|
|
||||||
@handlers = {}
|
|
||||||
@fsevents = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_handler(handler, preload=true)
|
|
||||||
@handlers[handler.path.to_s] = handler
|
|
||||||
|
|
||||||
fsevent = Rucola::FSEvents.new(handler.path.to_s, {:latency => 0.5}) do |events|
|
|
||||||
events.each do |event|
|
|
||||||
handler.refresh(event.path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
fsevent.create_stream
|
|
||||||
handler.refresh(nil, true) if preload
|
|
||||||
fsevent.start
|
|
||||||
@fsevents << fsevent
|
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
begin
|
|
||||||
OSX.CFRunLoopRun
|
|
||||||
rescue Interrupt
|
|
||||||
@fsevents.each do |fsev|
|
|
||||||
fsev.stop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
26
lib/vendor/fssm/fssm/backends/inotify.rb
vendored
26
lib/vendor/fssm/fssm/backends/inotify.rb
vendored
@ -1,26 +0,0 @@
|
|||||||
module FSSM::Backends
|
|
||||||
class Inotify
|
|
||||||
def initialize
|
|
||||||
@notifier = INotify::Notifier.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_handler(handler, preload=true)
|
|
||||||
@notifier.watch(handler.path.to_s, :recursive, :attrib, :modify, :create,
|
|
||||||
:delete, :delete_self, :moved_from, :moved_to, :move_self) do |event|
|
|
||||||
path = FSSM::Pathname.for(event.absolute_name)
|
|
||||||
path = path.dirname unless event.name == "" # Event on root directory
|
|
||||||
handler.refresh(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
handler.refresh(nil, true) if preload
|
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
begin
|
|
||||||
@notifier.run
|
|
||||||
rescue Interrupt
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
25
lib/vendor/fssm/fssm/backends/polling.rb
vendored
25
lib/vendor/fssm/fssm/backends/polling.rb
vendored
@ -1,25 +0,0 @@
|
|||||||
module FSSM::Backends
|
|
||||||
class Polling
|
|
||||||
def initialize(options={})
|
|
||||||
@handlers = []
|
|
||||||
@latency = options[:latency] || 1.5
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_handler(handler, preload=true)
|
|
||||||
handler.refresh(nil, true) if preload
|
|
||||||
@handlers << handler
|
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
begin
|
|
||||||
loop do
|
|
||||||
start = Time.now.to_f
|
|
||||||
@handlers.each {|handler| handler.refresh}
|
|
||||||
nap_time = @latency - (Time.now.to_f - start)
|
|
||||||
sleep nap_time if nap_time > 0
|
|
||||||
end
|
|
||||||
rescue Interrupt
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
131
lib/vendor/fssm/fssm/backends/rubycocoa/fsevents.rb
vendored
131
lib/vendor/fssm/fssm/backends/rubycocoa/fsevents.rb
vendored
@ -1,131 +0,0 @@
|
|||||||
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
|
|
||||||
|
|
||||||
module Rucola
|
|
||||||
class FSEvents
|
|
||||||
class FSEvent
|
|
||||||
attr_reader :fsevents_object
|
|
||||||
attr_reader :id
|
|
||||||
attr_reader :path
|
|
||||||
|
|
||||||
def initialize(fsevents_object, id, path)
|
|
||||||
@fsevents_object, @id, @path = fsevents_object, id, path
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns an array of the files/dirs in the path that the event occurred in.
|
|
||||||
# The files are sorted by the modification time, the first entry is the last modified file.
|
|
||||||
def files
|
|
||||||
Dir.glob("#{File.expand_path(path)}/*").sort_by {|f| File.mtime(f) }.reverse
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the last modified file in the path that the event occurred in.
|
|
||||||
def last_modified_file
|
|
||||||
files.first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class StreamError < StandardError;
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :paths
|
|
||||||
attr_reader :stream
|
|
||||||
|
|
||||||
attr_accessor :allocator
|
|
||||||
attr_accessor :context
|
|
||||||
attr_accessor :since
|
|
||||||
attr_accessor :latency
|
|
||||||
attr_accessor :flags
|
|
||||||
|
|
||||||
# Initializes a new FSEvents `watchdog` object and starts watching the directories you specify for events. The
|
|
||||||
# block is used as a handler for events, which are passed as the block's argument. This method is the easiest
|
|
||||||
# way to start watching some directories if you don't care about the details of setting up the event stream.
|
|
||||||
#
|
|
||||||
# Rucola::FSEvents.start_watching('/tmp') do |events|
|
|
||||||
# events.each { |event| log.debug("#{event.files.inspect} were changed.") }
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Rucola::FSEvents.start_watching('/var/log/system.log', '/var/log/secure.log', :since => last_id, :latency => 5) do
|
|
||||||
# Growl.notify("Something was added to your log files!")
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Note that the method also returns the FSEvents object. This enables you to control the event stream if you want to.
|
|
||||||
#
|
|
||||||
# fsevents = Rucola::FSEvents.start_watching('/Volumes') do |events|
|
|
||||||
# events.each { |event| Growl.notify("Volume changes: #{event.files.to_sentence}") }
|
|
||||||
# end
|
|
||||||
# fsevents.stop
|
|
||||||
def self.start_watching(*params, &block)
|
|
||||||
fsevents = new(*params, &block)
|
|
||||||
fsevents.create_stream
|
|
||||||
fsevents.start
|
|
||||||
fsevents
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new FSEvents `watchdog` object. You can specify a list of paths to watch and options to control the
|
|
||||||
# behaviour of the watchdog. The block you pass serves as a callback when an event is generated on one of the
|
|
||||||
# specified paths.
|
|
||||||
#
|
|
||||||
# fsevents = FSEvents.new('/etc/passwd') { Mailer.send_mail("Someone touched the password file!") }
|
|
||||||
# fsevents.create_stream
|
|
||||||
# fsevents.start
|
|
||||||
#
|
|
||||||
# fsevents = FSEvents.new('/home/upload', :since => UploadWatcher.last_event_id) do |events|
|
|
||||||
# events.each do |event|
|
|
||||||
# UploadWatcher.last_event_id = event.id
|
|
||||||
# event.files.each do |file|
|
|
||||||
# UploadWatcher.logfile.append("#{file} was changed")
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# *:since: The service will report events that have happened after the supplied event ID. Never use 0 because that
|
|
||||||
# will cause every fsevent since the "beginning of time" to be reported. Use OSX::KFSEventStreamEventIdSinceNow
|
|
||||||
# if you want to receive events that have happened after this call. (Default: OSX::KFSEventStreamEventIdSinceNow).
|
|
||||||
# You can find the ID's passed with :since in the events passed to your block.
|
|
||||||
# *:latency: Number of seconds to wait until an FSEvent is reported, this allows the service to bundle events. (Default: 0.0)
|
|
||||||
#
|
|
||||||
# Please refer to the Cocoa documentation for the rest of the options.
|
|
||||||
def initialize(*params, &block)
|
|
||||||
raise ArgumentError, 'No callback block was specified.' unless block_given?
|
|
||||||
|
|
||||||
options = params.last.kind_of?(Hash) ? params.pop : {}
|
|
||||||
@paths = params.flatten
|
|
||||||
|
|
||||||
paths.each { |path| raise ArgumentError, "The specified path (#{path}) does not exist." unless File.exist?(path) }
|
|
||||||
|
|
||||||
@allocator = options[:allocator] || OSX::KCFAllocatorDefault
|
|
||||||
@context = options[:context] || nil
|
|
||||||
@since = options[:since] || OSX::KFSEventStreamEventIdSinceNow
|
|
||||||
@latency = options[:latency] || 0.0
|
|
||||||
@flags = options[:flags] || 0
|
|
||||||
@stream = options[:stream] || nil
|
|
||||||
|
|
||||||
@user_callback = block
|
|
||||||
@callback = Proc.new do |stream, client_callback_info, number_of_events, paths_pointer, event_flags, event_ids|
|
|
||||||
paths_pointer.regard_as('*')
|
|
||||||
events = []
|
|
||||||
number_of_events.times {|i| events << Rucola::FSEvents::FSEvent.new(self, event_ids[i], paths_pointer[i]) }
|
|
||||||
@user_callback.call(events)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create the stream.
|
|
||||||
# Raises a Rucola::FSEvents::StreamError if the stream could not be created.
|
|
||||||
def create_stream
|
|
||||||
@stream = OSX.FSEventStreamCreate(@allocator, @callback, @context, @paths, @since, @latency, @flags)
|
|
||||||
raise(StreamError, 'Unable to create FSEvents stream.') unless @stream
|
|
||||||
OSX.FSEventStreamScheduleWithRunLoop(@stream, OSX.CFRunLoopGetCurrent, OSX::KCFRunLoopDefaultMode)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Start the stream.
|
|
||||||
# Raises a Rucola::FSEvents::StreamError if the stream could not be started.
|
|
||||||
def start
|
|
||||||
raise(StreamError, 'Unable to start FSEvents stream.') unless OSX.FSEventStreamStart(@stream)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Stop the stream.
|
|
||||||
# You can resume it by calling `start` again.
|
|
||||||
def stop
|
|
||||||
OSX.FSEventStreamStop(@stream)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
26
lib/vendor/fssm/fssm/monitor.rb
vendored
26
lib/vendor/fssm/fssm/monitor.rb
vendored
@ -1,26 +0,0 @@
|
|||||||
class FSSM::Monitor
|
|
||||||
def initialize(options={})
|
|
||||||
@options = options
|
|
||||||
@backend = FSSM::Backends::Default.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def path(*args, &block)
|
|
||||||
path = FSSM::Path.new(*args)
|
|
||||||
FSSM::Support.use_block(path, block)
|
|
||||||
|
|
||||||
@backend.add_handler(FSSM::State::Directory.new(path))
|
|
||||||
path
|
|
||||||
end
|
|
||||||
|
|
||||||
def file(*args, &block)
|
|
||||||
path = FSSM::Path.new(*args)
|
|
||||||
FSSM::Support.use_block(path, block)
|
|
||||||
|
|
||||||
@backend.add_handler(FSSM::State::File.new(path))
|
|
||||||
path
|
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
@backend.run
|
|
||||||
end
|
|
||||||
end
|
|
91
lib/vendor/fssm/fssm/path.rb
vendored
91
lib/vendor/fssm/fssm/path.rb
vendored
@ -1,91 +0,0 @@
|
|||||||
class FSSM::Path
|
|
||||||
def initialize(path=nil, glob=nil, &block)
|
|
||||||
set_path(path || '.')
|
|
||||||
set_glob(glob || '**/*')
|
|
||||||
init_callbacks
|
|
||||||
|
|
||||||
if block_given?
|
|
||||||
if block.arity == 1
|
|
||||||
block.call(self)
|
|
||||||
else
|
|
||||||
self.instance_eval(&block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
@path.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_pathname
|
|
||||||
@path
|
|
||||||
end
|
|
||||||
|
|
||||||
def glob(value=nil)
|
|
||||||
return @glob if value.nil?
|
|
||||||
set_glob(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create(callback_or_path=nil, &block)
|
|
||||||
callback_action(:create, (block_given? ? block : callback_or_path))
|
|
||||||
end
|
|
||||||
|
|
||||||
def update(callback_or_path=nil, &block)
|
|
||||||
callback_action(:update, (block_given? ? block : callback_or_path))
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(callback_or_path=nil, &block)
|
|
||||||
callback_action(:delete, (block_given? ? block : callback_or_path))
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def init_callbacks
|
|
||||||
do_nothing = lambda {|base, relative|}
|
|
||||||
@callbacks = Hash.new(do_nothing)
|
|
||||||
end
|
|
||||||
|
|
||||||
def callback_action(type, arg=nil)
|
|
||||||
if arg.is_a?(Proc)
|
|
||||||
set_callback(type, arg)
|
|
||||||
elsif arg.nil?
|
|
||||||
get_callback(type)
|
|
||||||
else
|
|
||||||
run_callback(type, arg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_callback(type, arg)
|
|
||||||
raise ArgumentError, "Proc expected" unless arg.is_a?(Proc)
|
|
||||||
@callbacks[type] = arg
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_callback(type)
|
|
||||||
@callbacks[type]
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_callback(type, arg)
|
|
||||||
base, relative = split_path(arg)
|
|
||||||
|
|
||||||
begin
|
|
||||||
@callbacks[type].call(base, relative)
|
|
||||||
rescue Exception => e
|
|
||||||
raise FSSM::CallbackError, "#{type} - #{base.join(relative)}: #{e.message}", e.backtrace
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def split_path(path)
|
|
||||||
path = FSSM::Pathname.for(path)
|
|
||||||
[@path, (path.relative? ? path : path.relative_path_from(@path))]
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_path(path)
|
|
||||||
path = FSSM::Pathname.for(path)
|
|
||||||
raise FSSM::FileNotFoundError, "No such file or directory - #{path}" unless path.exist?
|
|
||||||
@path = path.expand_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_glob(glob)
|
|
||||||
@glob = glob.is_a?(Array) ? glob : [glob]
|
|
||||||
end
|
|
||||||
end
|
|
502
lib/vendor/fssm/fssm/pathname.rb
vendored
502
lib/vendor/fssm/fssm/pathname.rb
vendored
@ -1,502 +0,0 @@
|
|||||||
require 'fileutils'
|
|
||||||
require 'find'
|
|
||||||
|
|
||||||
module FSSM
|
|
||||||
class Pathname < String
|
|
||||||
SYMLOOP_MAX = 8
|
|
||||||
|
|
||||||
ROOT = '/'.freeze
|
|
||||||
DOT = '.'.freeze
|
|
||||||
DOT_DOT = '..'.freeze
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def for(path)
|
|
||||||
path.is_a?(::FSSM::Pathname) ? path : new("#{path}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(path)
|
|
||||||
raise ArgumentError, "path cannot contain ASCII NULLs" if path =~ %r{\0}
|
|
||||||
super(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
def <=>(other)
|
|
||||||
self.tr('/', "\0").to_s <=> other.to_str.tr('/', "\0")
|
|
||||||
rescue NoMethodError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(other)
|
|
||||||
left = self.cleanpath.tr('/', "\0").to_s
|
|
||||||
right = self.class.for(other).cleanpath.tr('/', "\0").to_s
|
|
||||||
|
|
||||||
left == right
|
|
||||||
rescue NoMethodError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def +(path)
|
|
||||||
dup << path
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(path)
|
|
||||||
replace( join(path).cleanpath! )
|
|
||||||
end
|
|
||||||
|
|
||||||
def absolute?
|
|
||||||
self[0, 1].to_s == ROOT
|
|
||||||
end
|
|
||||||
|
|
||||||
def ascend
|
|
||||||
parts = to_a
|
|
||||||
parts.length.downto(1) do |i|
|
|
||||||
yield self.class.join(parts[0, i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def children
|
|
||||||
entries[2..-1]
|
|
||||||
end
|
|
||||||
|
|
||||||
def cleanpath!
|
|
||||||
parts = to_a
|
|
||||||
final = []
|
|
||||||
|
|
||||||
parts.each do |part|
|
|
||||||
case part
|
|
||||||
when DOT then
|
|
||||||
next
|
|
||||||
when DOT_DOT then
|
|
||||||
case final.last
|
|
||||||
when ROOT then
|
|
||||||
next
|
|
||||||
when DOT_DOT then
|
|
||||||
final.push(DOT_DOT)
|
|
||||||
when nil then
|
|
||||||
final.push(DOT_DOT)
|
|
||||||
else
|
|
||||||
final.pop
|
|
||||||
end
|
|
||||||
else
|
|
||||||
final.push(part)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
replace(final.empty? ? DOT : self.class.join(*final))
|
|
||||||
end
|
|
||||||
|
|
||||||
def cleanpath
|
|
||||||
dup.cleanpath!
|
|
||||||
end
|
|
||||||
|
|
||||||
def descend
|
|
||||||
parts = to_a
|
|
||||||
1.upto(parts.length) { |i| yield self.class.join(parts[0, i]) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def dot?
|
|
||||||
self == DOT
|
|
||||||
end
|
|
||||||
|
|
||||||
def dot_dot?
|
|
||||||
self == DOT_DOT
|
|
||||||
end
|
|
||||||
|
|
||||||
def each_filename(&blk)
|
|
||||||
to_a.each(&blk)
|
|
||||||
end
|
|
||||||
|
|
||||||
def mountpoint?
|
|
||||||
stat1 = self.lstat
|
|
||||||
stat2 = self.parent.lstat
|
|
||||||
|
|
||||||
stat1.dev != stat2.dev || stat1.ino == stat2.ino
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def parent
|
|
||||||
self + '..'
|
|
||||||
end
|
|
||||||
|
|
||||||
def realpath
|
|
||||||
path = self
|
|
||||||
|
|
||||||
SYMLOOP_MAX.times do
|
|
||||||
link = path.readlink
|
|
||||||
link = path.dirname + link if link.relative?
|
|
||||||
path = link
|
|
||||||
end
|
|
||||||
|
|
||||||
raise Errno::ELOOP, self
|
|
||||||
rescue Errno::EINVAL
|
|
||||||
path.expand_path
|
|
||||||
end
|
|
||||||
|
|
||||||
def relative?
|
|
||||||
!absolute?
|
|
||||||
end
|
|
||||||
|
|
||||||
def relative_path_from(base)
|
|
||||||
base = self.class.for(base)
|
|
||||||
|
|
||||||
raise ArgumentError, 'no relative path between a relative and absolute' if self.absolute? != base.absolute?
|
|
||||||
|
|
||||||
return self if base.dot?
|
|
||||||
return self.class.new(DOT) if self == base
|
|
||||||
|
|
||||||
base = base.cleanpath.to_a
|
|
||||||
dest = self.cleanpath.to_a
|
|
||||||
|
|
||||||
while !dest.empty? && !base.empty? && dest[0] == base[0]
|
|
||||||
base.shift
|
|
||||||
dest.shift
|
|
||||||
end
|
|
||||||
|
|
||||||
base.shift if base[0] == DOT
|
|
||||||
dest.shift if dest[0] == DOT
|
|
||||||
|
|
||||||
raise ArgumentError, "base directory may not contain '#{DOT_DOT}'" if base.include?(DOT_DOT)
|
|
||||||
|
|
||||||
path = base.fill(DOT_DOT) + dest
|
|
||||||
path = self.class.join(*path)
|
|
||||||
path = self.class.new(DOT) if path.empty?
|
|
||||||
|
|
||||||
path
|
|
||||||
end
|
|
||||||
|
|
||||||
def root?
|
|
||||||
!!(self =~ %r{^#{ROOT}+$})
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_a
|
|
||||||
array = to_s.split(File::SEPARATOR)
|
|
||||||
array.delete('')
|
|
||||||
array.insert(0, ROOT) if absolute?
|
|
||||||
array
|
|
||||||
end
|
|
||||||
|
|
||||||
alias segments to_a
|
|
||||||
|
|
||||||
def to_path
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"#{self}"
|
|
||||||
end
|
|
||||||
|
|
||||||
alias to_str to_s
|
|
||||||
|
|
||||||
def unlink
|
|
||||||
Dir.unlink(self)
|
|
||||||
true
|
|
||||||
rescue Errno::ENOTDIR
|
|
||||||
File.unlink(self)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
def self.[](pattern)
|
|
||||||
Dir[pattern].map! {|d| FSSM::Pathname.new(d) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.pwd
|
|
||||||
FSSM::Pathname.new(Dir.pwd)
|
|
||||||
end
|
|
||||||
|
|
||||||
def entries
|
|
||||||
Dir.entries(self).map! {|e| FSSM::Pathname.new(e) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def mkdir(mode = 0777)
|
|
||||||
Dir.mkdir(self, mode)
|
|
||||||
end
|
|
||||||
|
|
||||||
def opendir(&blk)
|
|
||||||
Dir.open(self, &blk)
|
|
||||||
end
|
|
||||||
|
|
||||||
def rmdir
|
|
||||||
Dir.rmdir(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.glob(pattern, flags = 0)
|
|
||||||
dirs = Dir.glob(pattern, flags)
|
|
||||||
dirs.map! {|path| FSSM::Pathname.new(path) }
|
|
||||||
|
|
||||||
if block_given?
|
|
||||||
dirs.each {|dir| yield dir }
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
dirs
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def glob(pattern, flags = 0, &block)
|
|
||||||
patterns = [pattern].flatten
|
|
||||||
patterns.map! {|p| self.class.glob(self.to_s + p, flags, &block) }
|
|
||||||
patterns.flatten
|
|
||||||
end
|
|
||||||
|
|
||||||
def chdir
|
|
||||||
blk = lambda { yield self } if block_given?
|
|
||||||
Dir.chdir(self, &blk)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
def blockdev?
|
|
||||||
FileTest.blockdev?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def chardev?
|
|
||||||
FileTest.chardev?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def directory?
|
|
||||||
FileTest.directory?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def executable?
|
|
||||||
FileTest.executable?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def executable_real?
|
|
||||||
FileTest.executable_real?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def exists?
|
|
||||||
FileTest.exists?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def file?
|
|
||||||
FileTest.file?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def grpowned?
|
|
||||||
FileTest.grpowned?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def owned?
|
|
||||||
FileTest.owned?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pipe?
|
|
||||||
FileTest.pipe?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def readable?
|
|
||||||
FileTest.readable?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def readable_real?
|
|
||||||
FileTest.readable_real?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def setgid?
|
|
||||||
FileTest.setgit?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def setuid?
|
|
||||||
FileTest.setuid?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def socket?
|
|
||||||
FileTest.socket?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def sticky?
|
|
||||||
FileTest.sticky?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def symlink?
|
|
||||||
FileTest.symlink?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def world_readable?
|
|
||||||
FileTest.world_readable?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def world_writable?
|
|
||||||
FileTest.world_writable?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def writable?
|
|
||||||
FileTest.writable?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def writable_real?
|
|
||||||
FileTest.writable_real?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def zero?
|
|
||||||
FileTest.zero?(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
def atime
|
|
||||||
File.atime(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ctime
|
|
||||||
File.ctime(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ftype
|
|
||||||
File.ftype(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def lstat
|
|
||||||
File.lstat(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def mtime
|
|
||||||
File.mtime(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def stat
|
|
||||||
File.stat(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def utime(atime, mtime)
|
|
||||||
File.utime(self, atime, mtime)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
def self.join(*parts)
|
|
||||||
last_part = FSSM::Pathname.new(parts.last)
|
|
||||||
return last_part if last_part.absolute?
|
|
||||||
FSSM::Pathname.new(File.join(*parts.reject {|p| p.empty? }))
|
|
||||||
end
|
|
||||||
|
|
||||||
def basename
|
|
||||||
self.class.new(File.basename(self))
|
|
||||||
end
|
|
||||||
|
|
||||||
def chmod(mode)
|
|
||||||
File.chmod(mode, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def chown(owner, group)
|
|
||||||
File.chown(owner, group, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def dirname
|
|
||||||
self.class.new(File.dirname(self))
|
|
||||||
end
|
|
||||||
|
|
||||||
def expand_path(from = nil)
|
|
||||||
self.class.new(File.expand_path(self, from))
|
|
||||||
end
|
|
||||||
|
|
||||||
def extname
|
|
||||||
File.extname(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def fnmatch?(pat, flags = 0)
|
|
||||||
File.fnmatch(pat, self, flags)
|
|
||||||
end
|
|
||||||
|
|
||||||
def join(*parts)
|
|
||||||
self.class.join(self, *parts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def lchmod(mode)
|
|
||||||
File.lchmod(mode, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def lchown(owner, group)
|
|
||||||
File.lchown(owner, group, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def link(to)
|
|
||||||
File.link(self, to)
|
|
||||||
end
|
|
||||||
|
|
||||||
def open(mode = 'r', perm = nil, &blk)
|
|
||||||
File.open(self, mode, perm, &blk)
|
|
||||||
end
|
|
||||||
|
|
||||||
def readlink
|
|
||||||
self.class.new(File.readlink(self))
|
|
||||||
end
|
|
||||||
|
|
||||||
def rename(to)
|
|
||||||
File.rename(self, to)
|
|
||||||
replace(to)
|
|
||||||
end
|
|
||||||
|
|
||||||
def size
|
|
||||||
File.size(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def size?
|
|
||||||
File.size?(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def split
|
|
||||||
File.split(self).map {|part| FSSM::Pathname.new(part) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def symlink(to)
|
|
||||||
File.symlink(self, to)
|
|
||||||
end
|
|
||||||
|
|
||||||
def truncate
|
|
||||||
File.truncate(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
def mkpath
|
|
||||||
self.class.new(FileUtils.mkpath(self))
|
|
||||||
end
|
|
||||||
|
|
||||||
def rmtree
|
|
||||||
self.class.new(FileUtils.rmtree(self).first)
|
|
||||||
end
|
|
||||||
|
|
||||||
def touch
|
|
||||||
self.class.new(FileUtils.touch(self).first)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
def each_line(sep = $/, &blk)
|
|
||||||
IO.foreach(self, sep, &blk)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(len = nil, off = 0)
|
|
||||||
IO.read(self, len, off)
|
|
||||||
end
|
|
||||||
|
|
||||||
def readlines(sep = $/)
|
|
||||||
IO.readlines(self, sep)
|
|
||||||
end
|
|
||||||
|
|
||||||
def sysopen(mode = 'r', perm = nil)
|
|
||||||
IO.sysopen(self, mode, perm)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
def find
|
|
||||||
Find.find(self) {|path| yield FSSM::Pathname.new(path) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pathname
|
|
||||||
class << self
|
|
||||||
alias getwd pwd
|
|
||||||
end
|
|
||||||
|
|
||||||
alias absolute expand_path
|
|
||||||
alias delete unlink
|
|
||||||
alias exist? exists?
|
|
||||||
alias fnmatch fnmatch?
|
|
||||||
end
|
|
||||||
end
|
|
57
lib/vendor/fssm/fssm/state/directory.rb
vendored
57
lib/vendor/fssm/fssm/state/directory.rb
vendored
@ -1,57 +0,0 @@
|
|||||||
module FSSM::State
|
|
||||||
class Directory
|
|
||||||
attr_reader :path
|
|
||||||
|
|
||||||
def initialize(path)
|
|
||||||
@path = path
|
|
||||||
@cache = FSSM::Tree::Cache.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh(base=nil, skip_callbacks=false)
|
|
||||||
previous, current = recache(base || @path.to_pathname)
|
|
||||||
|
|
||||||
unless skip_callbacks
|
|
||||||
deleted(previous, current)
|
|
||||||
created(previous, current)
|
|
||||||
modified(previous, current)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def created(previous, current)
|
|
||||||
(current.keys - previous.keys).each {|created| @path.create(created)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def deleted(previous, current)
|
|
||||||
(previous.keys - current.keys).each {|deleted| @path.delete(deleted)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def modified(previous, current)
|
|
||||||
(current.keys & previous.keys).each do |file|
|
|
||||||
@path.update(file) if (current[file] <=> previous[file]) != 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def recache(base)
|
|
||||||
base = FSSM::Pathname.for(base)
|
|
||||||
previous = @cache.files
|
|
||||||
snapshot(base)
|
|
||||||
current = @cache.files
|
|
||||||
[previous, current]
|
|
||||||
end
|
|
||||||
|
|
||||||
def snapshot(base)
|
|
||||||
base = FSSM::Pathname.for(base)
|
|
||||||
@cache.unset(base)
|
|
||||||
@path.glob.each {|glob| add_glob(base, glob)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_glob(base, glob)
|
|
||||||
FSSM::Pathname.glob(base.join(glob).to_s).each do |fn|
|
|
||||||
@cache.set(fn)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
24
lib/vendor/fssm/fssm/state/file.rb
vendored
24
lib/vendor/fssm/fssm/state/file.rb
vendored
@ -1,24 +0,0 @@
|
|||||||
module FSSM::State
|
|
||||||
class File
|
|
||||||
attr_reader :path
|
|
||||||
|
|
||||||
def initialize(path)
|
|
||||||
@path = path
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh(base=nil, skip_callbacks=false)
|
|
||||||
base ||= @path.to_pathname
|
|
||||||
used_to_exist, @exists = @exists, base.exists?
|
|
||||||
# this handles bad symlinks without failing. why handle bad symlinks at
|
|
||||||
# all? well, we could still be interested in their creation and deletion.
|
|
||||||
old_mtime, @mtime = @mtime, base.symlink? ? Time.at(0) : base.mtime if @exists
|
|
||||||
|
|
||||||
unless skip_callbacks
|
|
||||||
@path.delete(@path.to_s) if used_to_exist && !@exists
|
|
||||||
@path.create(@path.to_s) if !used_to_exist && @exists
|
|
||||||
@path.update(@path.to_s) if used_to_exist && @exists && old_mtime != @mtime
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
63
lib/vendor/fssm/fssm/support.rb
vendored
63
lib/vendor/fssm/fssm/support.rb
vendored
@ -1,63 +0,0 @@
|
|||||||
require 'rbconfig'
|
|
||||||
|
|
||||||
module FSSM::Support
|
|
||||||
class << self
|
|
||||||
def backend
|
|
||||||
@@backend ||= case
|
|
||||||
when mac? && !jruby? && carbon_core?
|
|
||||||
'FSEvents'
|
|
||||||
when linux? && rb_inotify?
|
|
||||||
'Inotify'
|
|
||||||
else
|
|
||||||
'Polling'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def jruby?
|
|
||||||
defined?(JRUBY_VERSION)
|
|
||||||
end
|
|
||||||
|
|
||||||
def mac?
|
|
||||||
Config::CONFIG['target_os'] =~ /darwin/i
|
|
||||||
end
|
|
||||||
|
|
||||||
def linux?
|
|
||||||
Config::CONFIG['target_os'] =~ /linux/i
|
|
||||||
end
|
|
||||||
|
|
||||||
def carbon_core?
|
|
||||||
begin
|
|
||||||
require 'osx/foundation'
|
|
||||||
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
|
|
||||||
true
|
|
||||||
rescue LoadError
|
|
||||||
STDERR.puts("Warning: Unable to load CarbonCore. FSEvents will be unavailable.")
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def rb_inotify?
|
|
||||||
found = begin
|
|
||||||
require 'rb-inotify'
|
|
||||||
if defined?(INotify::VERSION)
|
|
||||||
version = INotify::VERSION
|
|
||||||
version[0] > 0 || version[1] >= 6
|
|
||||||
end
|
|
||||||
rescue LoadError
|
|
||||||
false
|
|
||||||
end
|
|
||||||
STDERR.puts("Warning: Unable to load rb-inotify >= 0.5.1. Inotify will be unavailable.") unless found
|
|
||||||
found
|
|
||||||
end
|
|
||||||
|
|
||||||
def use_block(context, block)
|
|
||||||
return if block.nil?
|
|
||||||
if block.arity == 1
|
|
||||||
block.call(context)
|
|
||||||
else
|
|
||||||
context.instance_eval(&block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
176
lib/vendor/fssm/fssm/tree.rb
vendored
176
lib/vendor/fssm/fssm/tree.rb
vendored
@ -1,176 +0,0 @@
|
|||||||
module FSSM::Tree
|
|
||||||
module NodeBase
|
|
||||||
def initialize
|
|
||||||
@children = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def child(segment)
|
|
||||||
@children["#{segment}"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def child!(segment)
|
|
||||||
(@children["#{segment}"] ||= Node.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_child?(segment)
|
|
||||||
@children.has_key?("#{segment}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_child(segment)
|
|
||||||
@children.delete("#{segment}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_children
|
|
||||||
@children.clear
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module NodeEnumerable
|
|
||||||
include NodeBase
|
|
||||||
include Enumerable
|
|
||||||
|
|
||||||
def each(prefix=nil, &block)
|
|
||||||
@children.each do |segment, node|
|
|
||||||
cprefix = prefix ?
|
|
||||||
FSSM::Pathname.for(prefix).join(segment) :
|
|
||||||
FSSM::Pathname.for(segment)
|
|
||||||
block.call([cprefix, node])
|
|
||||||
node.each(cprefix, &block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module NodeInsertion
|
|
||||||
include NodeBase
|
|
||||||
|
|
||||||
def unset(path)
|
|
||||||
key = key_segments(path)
|
|
||||||
|
|
||||||
if key.empty?
|
|
||||||
remove_children
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
segment = key.pop
|
|
||||||
node = descendant(key)
|
|
||||||
|
|
||||||
return unless node
|
|
||||||
|
|
||||||
node.remove_child(segment)
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def set(path)
|
|
||||||
node = descendant!(path)
|
|
||||||
node.from_path(path).mtime
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def key_segments(key)
|
|
||||||
return key if key.is_a?(Array)
|
|
||||||
FSSM::Pathname.for(key).segments
|
|
||||||
end
|
|
||||||
|
|
||||||
def descendant(path)
|
|
||||||
recurse(path, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def descendant!(path)
|
|
||||||
recurse(path, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def recurse(key, create=false)
|
|
||||||
key = key_segments(key)
|
|
||||||
node = self
|
|
||||||
|
|
||||||
until key.empty?
|
|
||||||
segment = key.shift
|
|
||||||
node = create ? node.child!(segment) : node.child(segment)
|
|
||||||
return nil unless node
|
|
||||||
end
|
|
||||||
|
|
||||||
node
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module CacheDebug
|
|
||||||
def set(path)
|
|
||||||
FSSM.dbg("Cache#set(#{path})")
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def unset(path)
|
|
||||||
FSSM.dbg("Cache#unset(#{path})")
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def ftype(ft)
|
|
||||||
FSSM.dbg("Cache#ftype(#{ft})")
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Node
|
|
||||||
include NodeBase
|
|
||||||
include NodeEnumerable
|
|
||||||
|
|
||||||
attr_accessor :mtime
|
|
||||||
attr_accessor :ftype
|
|
||||||
|
|
||||||
def <=>(other)
|
|
||||||
return unless other.is_a?(::FSSM::Tree::Node)
|
|
||||||
self.mtime <=> other.mtime
|
|
||||||
end
|
|
||||||
|
|
||||||
def from_path(path)
|
|
||||||
path = FSSM::Pathname.for(path)
|
|
||||||
@ftype = path.ftype
|
|
||||||
# this handles bad symlinks without failing. why handle bad symlinks at
|
|
||||||
# all? well, we could still be interested in their creation and deletion.
|
|
||||||
@mtime = path.symlink? ? Time.at(0) : path.mtime
|
|
||||||
self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Cache
|
|
||||||
include NodeBase
|
|
||||||
include NodeEnumerable
|
|
||||||
include NodeInsertion
|
|
||||||
include CacheDebug if $DEBUG
|
|
||||||
|
|
||||||
def set(path)
|
|
||||||
# all paths set from this level need to be absolute
|
|
||||||
# realpath will fail on broken links
|
|
||||||
path = FSSM::Pathname.for(path).expand_path
|
|
||||||
super(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
def files
|
|
||||||
ftype('file')
|
|
||||||
end
|
|
||||||
|
|
||||||
def directories
|
|
||||||
ftype('directory')
|
|
||||||
end
|
|
||||||
|
|
||||||
def links
|
|
||||||
ftype('link')
|
|
||||||
end
|
|
||||||
|
|
||||||
alias symlinks links
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def ftype(ft)
|
|
||||||
inject({}) do |hash, (path, node)|
|
|
||||||
hash["#{path}"] = node.mtime if node.ftype == ft
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
Loading…
Reference in New Issue
Block a user