master/lib/guard/listener.rb

152 lines
3.8 KiB
Ruby
Raw Normal View History

require 'rbconfig'
2011-05-24 01:36:51 +00:00
require 'digest/sha1'
2010-10-03 21:00:33 +00:00
module Guard
autoload :Darwin, 'guard/listeners/darwin'
autoload :Linux, 'guard/listeners/linux'
autoload :Windows, 'guard/listeners/windows'
autoload :Polling, 'guard/listeners/polling'
2010-10-03 21:00:33 +00:00
class Listener
2011-09-01 09:19:20 +00:00
DefaultIgnorePaths = %w[. .. .bundle .git log tmp vendor]
2011-09-01 09:19:20 +00:00
attr_reader :directory, :ignore_paths
def self.select_and_init(*a)
if mac? && Darwin.usable?
Darwin.new(*a)
elsif linux? && Linux.usable?
Linux.new(*a)
elsif windows? && Windows.usable?
Windows.new(*a)
else
UI.info "Using polling (Please help us to support your system better than that.)"
Polling.new(*a)
2010-10-03 21:00:33 +00:00
end
end
def initialize(directory=Dir.pwd, options={})
@directory = directory.to_s
@sha1_checksums_hash = {}
@relativize_paths = options.fetch(:relativize_paths, true)
2011-09-01 09:19:20 +00:00
@ignore_paths = options.fetch(:ignore_paths, DefaultIgnorePaths)
update_last_event
2010-10-03 21:00:33 +00:00
end
def start
watch(@directory)
end
def stop
end
def on_change(&callback)
@callback = callback
end
def update_last_event
@last_event = Time.now
end
def modified_files(dirs, options={})
files = potentially_modified_files(dirs, options).select { |path| file_modified?(path) }
update_last_event
relativize_paths(files)
2010-10-03 21:00:33 +00:00
end
def worker
raise NotImplementedError, "should respond to #watch"
end
# register a directory to watch. must be implemented by the subclasses
def watch(directory)
raise NotImplementedError, "do whatever you want here, given the directory as only argument"
end
def all_files
potentially_modified_files([@directory], :all => true)
2010-10-03 21:00:33 +00:00
end
# scopes all given paths to the current #directory
def relativize_paths(paths)
return paths unless relativize_paths?
paths.map do |path|
path.gsub(%r{^#{@directory}/}, '')
end
end
def relativize_paths?
!!@relativize_paths
2011-05-15 17:00:15 +00:00
end
private
def potentially_modified_files(dirs, options={})
2011-09-01 09:19:20 +00:00
paths = exclude_ignored_paths_from_dirs(dirs)
if options[:all]
paths.inject([]) do |array, path|
if File.file?(path)
array << path
else
array += Dir.glob("#{path}/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
end
array
end
else
paths.select { |path| File.file?(path) }
end
2010-10-03 21:00:33 +00:00
end
2011-09-01 09:19:20 +00:00
def exclude_ignored_paths_from_dirs(dirs)
paths = Dir.glob(dirs.map { |d| "#{d.sub(%r{/+$}, '')}/*" }, File::FNM_DOTMATCH).reject do |path|
@ignore_paths.include?(File.basename(path))
end
end
# Depending on the filesystem, mtime is probably only precise to the second, so round
# both values down to the second for the comparison.
def file_modified?(path)
if File.mtime(path).to_i == @last_event.to_i
file_content_modified?(path, sha1_checksum(path))
elsif File.mtime(path).to_i > @last_event.to_i
set_sha1_checksums_hash(path, sha1_checksum(path))
true
end
rescue
false
2010-10-03 21:00:33 +00:00
end
def file_content_modified?(path, sha1_checksum)
if @sha1_checksums_hash[path] != sha1_checksum
set_sha1_checksums_hash(path, sha1_checksum)
true
else
false
end
end
def set_sha1_checksums_hash(path, sha1_checksum)
@sha1_checksums_hash[path] = sha1_checksum
end
def sha1_checksum(path)
Digest::SHA1.file(path).to_s
end
def self.mac?
RbConfig::CONFIG['target_os'] =~ /darwin/i
end
def self.linux?
RbConfig::CONFIG['target_os'] =~ /linux/i
2010-10-03 21:00:33 +00:00
end
def self.windows?
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
end
2010-10-03 21:00:33 +00:00
end
end