2010-10-17 19:42:40 +00:00
|
|
|
require 'rbconfig'
|
2011-05-24 01:36:51 +00:00
|
|
|
require 'digest/sha1'
|
2010-10-03 21:00:33 +00:00
|
|
|
|
|
|
|
module Guard
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2010-10-17 19:42:40 +00:00
|
|
|
autoload :Darwin, 'guard/listeners/darwin'
|
|
|
|
autoload :Linux, 'guard/listeners/linux'
|
2011-04-30 10:38:57 +00:00
|
|
|
autoload :Windows, 'guard/listeners/windows'
|
2010-10-17 19:42:40 +00:00
|
|
|
autoload :Polling, 'guard/listeners/polling'
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2010-10-03 21:00:33 +00:00
|
|
|
class Listener
|
2011-08-30 19:13:51 +00:00
|
|
|
|
2011-09-01 09:19:20 +00:00
|
|
|
DefaultIgnorePaths = %w[. .. .bundle .git log tmp vendor]
|
2011-09-01 21:24:45 +00:00
|
|
|
attr_accessor :changed_files
|
2011-09-01 19:28:03 +00:00
|
|
|
attr_reader :directory, :ignore_paths, :locked
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-05-15 17:07:20 +00:00
|
|
|
def self.select_and_init(*a)
|
2010-10-17 19:42:40 +00:00
|
|
|
if mac? && Darwin.usable?
|
2011-05-15 17:07:20 +00:00
|
|
|
Darwin.new(*a)
|
2010-10-17 19:42:40 +00:00
|
|
|
elsif linux? && Linux.usable?
|
2011-05-15 17:07:20 +00:00
|
|
|
Linux.new(*a)
|
2011-04-30 10:38:57 +00:00
|
|
|
elsif windows? && Windows.usable?
|
2011-05-15 17:07:20 +00:00
|
|
|
Windows.new(*a)
|
2010-10-17 19:42:40 +00:00
|
|
|
else
|
|
|
|
UI.info "Using polling (Please help us to support your system better than that.)"
|
2011-05-15 17:07:20 +00:00
|
|
|
Polling.new(*a)
|
2010-10-03 21:00:33 +00:00
|
|
|
end
|
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-09-01 19:28:03 +00:00
|
|
|
def initialize(directory = Dir.pwd, options = {})
|
2011-07-20 23:29:05 +00:00
|
|
|
@directory = directory.to_s
|
2011-05-09 07:39:11 +00:00
|
|
|
@sha1_checksums_hash = {}
|
2011-07-20 23:29:05 +00:00
|
|
|
@relativize_paths = options.fetch(:relativize_paths, true)
|
2011-08-30 19:13:51 +00:00
|
|
|
@changed_files = []
|
|
|
|
@locked = false
|
2011-09-01 21:24:45 +00:00
|
|
|
@ignore_paths = DefaultIgnorePaths
|
|
|
|
@ignore_paths |= options[:ignore_paths] if options[:ignore_paths]
|
|
|
|
|
2010-10-17 19:42:40 +00:00
|
|
|
update_last_event
|
2011-08-30 19:13:51 +00:00
|
|
|
start_reactor
|
|
|
|
end
|
|
|
|
|
|
|
|
def start_reactor
|
2011-09-02 14:22:01 +00:00
|
|
|
return if ENV["GUARD_ENV"] == 'test'
|
2011-09-03 12:16:32 +00:00
|
|
|
p "start_reactor"
|
2011-08-30 19:13:51 +00:00
|
|
|
Thread.new do
|
|
|
|
loop do
|
|
|
|
if @changed_files != [] && !@locked
|
|
|
|
changed_files = @changed_files.dup
|
|
|
|
clear_changed_files
|
|
|
|
::Guard.run_on_change(changed_files)
|
|
|
|
else
|
|
|
|
sleep 0.1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2010-10-03 21:00:33 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-05-13 21:06:45 +00:00
|
|
|
def start
|
2011-07-20 23:29:05 +00:00
|
|
|
watch(@directory)
|
2011-05-13 21:06:45 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def stop
|
|
|
|
end
|
|
|
|
|
2011-08-30 19:13:51 +00:00
|
|
|
def lock
|
|
|
|
@locked = true
|
|
|
|
end
|
|
|
|
|
|
|
|
def unlock
|
|
|
|
@locked = false
|
|
|
|
end
|
|
|
|
|
|
|
|
def clear_changed_files
|
|
|
|
@changed_files.clear
|
|
|
|
end
|
|
|
|
|
2011-05-13 21:06:45 +00:00
|
|
|
def on_change(&callback)
|
|
|
|
@callback = callback
|
|
|
|
end
|
|
|
|
|
2011-01-19 22:05:45 +00:00
|
|
|
def update_last_event
|
|
|
|
@last_event = Time.now
|
2010-11-30 20:15:03 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-09-01 19:28:03 +00:00
|
|
|
def modified_files(dirs, options = {})
|
2011-09-02 14:22:01 +00:00
|
|
|
last_event = @last_event.dup
|
2011-07-20 23:25:06 +00:00
|
|
|
update_last_event
|
2011-09-02 14:22:01 +00:00
|
|
|
files = potentially_modified_files(dirs, options).select { |path| file_modified?(path, last_event) }
|
2011-09-03 12:16:32 +00:00
|
|
|
p "modified_files"
|
|
|
|
p files
|
2011-07-20 23:25:06 +00:00
|
|
|
relativize_paths(files)
|
2010-10-03 21:00:33 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-05-13 21:06:45 +00:00
|
|
|
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
|
|
|
|
|
2011-05-15 16:36:23 +00:00
|
|
|
def all_files
|
2011-07-20 23:40:40 +00:00
|
|
|
potentially_modified_files([@directory], :all => true)
|
2010-10-03 21:00:33 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-05-15 16:36:23 +00:00
|
|
|
# scopes all given paths to the current #directory
|
2011-07-20 23:29:05 +00:00
|
|
|
def relativize_paths(paths)
|
|
|
|
return paths unless relativize_paths?
|
2011-05-28 15:15:09 +00:00
|
|
|
paths.map do |path|
|
2011-07-20 23:29:05 +00:00
|
|
|
path.gsub(%r{^#{@directory}/}, '')
|
2011-05-15 16:36:23 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-07-20 23:29:05 +00:00
|
|
|
def relativize_paths?
|
|
|
|
!!@relativize_paths
|
2011-05-15 17:00:15 +00:00
|
|
|
end
|
2011-09-01 21:24:45 +00:00
|
|
|
|
2011-09-01 11:43:02 +00:00
|
|
|
# return children of the passed dirs that are not in the ignore_paths list
|
2011-09-01 11:30:25 +00:00
|
|
|
def exclude_ignored_paths(dirs, ignore_paths = self.ignore_paths)
|
|
|
|
Dir.glob(dirs.map { |d| "#{d.sub(%r{/+$}, '')}/*" }, File::FNM_DOTMATCH).reject do |path|
|
|
|
|
ignore_paths.include?(File.basename(path))
|
|
|
|
end
|
|
|
|
end
|
2011-05-15 17:00:15 +00:00
|
|
|
|
2011-01-19 22:05:45 +00:00
|
|
|
private
|
|
|
|
|
2011-07-20 23:40:40 +00:00
|
|
|
def potentially_modified_files(dirs, options={})
|
2011-09-01 11:30:25 +00:00
|
|
|
paths = exclude_ignored_paths(dirs)
|
2011-09-01 21:24:45 +00:00
|
|
|
|
2011-07-20 23:40:40 +00:00
|
|
|
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-01-19 22:05:45 +00:00
|
|
|
|
2011-05-28 14:47:35 +00:00
|
|
|
# Depending on the filesystem, mtime is probably only precise to the second, so round
|
|
|
|
# both values down to the second for the comparison.
|
2011-09-02 14:22:01 +00:00
|
|
|
def file_modified?(path, last_event)
|
2011-09-03 12:16:32 +00:00
|
|
|
p path
|
|
|
|
p last_event.to_i
|
|
|
|
p File.ctime(path).to_i
|
2011-09-02 14:22:01 +00:00
|
|
|
if File.ctime(path).to_i == last_event.to_i
|
2011-09-03 12:16:32 +00:00
|
|
|
p "File.ctime == last_event"
|
|
|
|
res = file_content_modified?(path, sha1_checksum(path))
|
|
|
|
p res.to_s
|
|
|
|
res
|
2011-09-02 14:22:01 +00:00
|
|
|
elsif File.ctime(path).to_i > last_event.to_i
|
2011-09-03 12:16:32 +00:00
|
|
|
p "File.ctime > last_event"
|
2011-05-28 14:47:35 +00:00
|
|
|
set_sha1_checksums_hash(path, sha1_checksum(path))
|
2011-09-03 12:16:32 +00:00
|
|
|
p "true"
|
2011-05-28 14:47:35 +00:00
|
|
|
true
|
2011-09-03 12:16:32 +00:00
|
|
|
else
|
|
|
|
p "false"
|
|
|
|
false
|
2011-05-28 14:47:35 +00:00
|
|
|
end
|
2010-10-07 18:53:29 +00:00
|
|
|
rescue
|
2011-09-03 12:16:32 +00:00
|
|
|
p "false"
|
2010-10-07 18:53:29 +00:00
|
|
|
false
|
2010-10-03 21:00:33 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-05-28 14:47:35 +00:00
|
|
|
def file_content_modified?(path, sha1_checksum)
|
2011-07-20 23:29:05 +00:00
|
|
|
if @sha1_checksums_hash[path] != sha1_checksum
|
2011-05-28 14:47:35 +00:00
|
|
|
set_sha1_checksums_hash(path, sha1_checksum)
|
2011-05-09 07:39:11 +00:00
|
|
|
true
|
|
|
|
else
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-05-28 14:47:35 +00:00
|
|
|
def set_sha1_checksums_hash(path, sha1_checksum)
|
|
|
|
@sha1_checksums_hash[path] = sha1_checksum
|
|
|
|
end
|
|
|
|
|
|
|
|
def sha1_checksum(path)
|
2011-05-28 14:50:16 +00:00
|
|
|
Digest::SHA1.file(path).to_s
|
2011-05-28 14:47:35 +00:00
|
|
|
end
|
|
|
|
|
2010-10-17 19:42:40 +00:00
|
|
|
def self.mac?
|
2011-06-16 11:14:51 +00:00
|
|
|
RbConfig::CONFIG['target_os'] =~ /darwin/i
|
2010-10-17 19:42:40 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2010-10-17 19:42:40 +00:00
|
|
|
def self.linux?
|
2011-06-16 11:14:51 +00:00
|
|
|
RbConfig::CONFIG['target_os'] =~ /linux/i
|
2010-10-03 21:00:33 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
|
2011-04-30 10:38:57 +00:00
|
|
|
def self.windows?
|
2011-06-16 11:14:51 +00:00
|
|
|
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
2011-04-30 10:38:57 +00:00
|
|
|
end
|
|
|
|
|
2010-10-03 21:00:33 +00:00
|
|
|
end
|
2011-01-19 22:05:45 +00:00
|
|
|
end
|