Replaced Thread (incompatible with inotify) by a whole dir scan after each run_on_changes
This commit is contained in:
parent
48b9c2b824
commit
9772e9d9c8
@ -1,3 +1,8 @@
|
||||
== Jan 19, 2011 [by thibaudgg]
|
||||
|
||||
Features:
|
||||
- The whole directory are now watched after that run_on_change was launched on all guards to detect new files modifications.
|
||||
|
||||
== Dec 17, 2010 [by netzpirat]
|
||||
|
||||
Features:
|
||||
@ -14,7 +19,6 @@ Features:
|
||||
|
||||
Features:
|
||||
- It's now possible to return an enumerable in the 'watch' optional blocks in the Guardfile.
|
||||
- Listener now continue to watch changed files even when guards plugin are running.
|
||||
|
||||
Specs:
|
||||
- Guard::Watcher
|
||||
|
38
lib/guard.rb
38
lib/guard.rb
@ -29,28 +29,28 @@ module Guard
|
||||
if guards.empty?
|
||||
UI.error "No guards found in Guardfile, please add at least one."
|
||||
else
|
||||
listener.on_change do |files|
|
||||
if Watcher.match_files?(guards, files)
|
||||
run { run_on_change_for_all_guards(files) }
|
||||
end
|
||||
end
|
||||
|
||||
UI.info "Guard is now watching at '#{Dir.pwd}'"
|
||||
guards.each { |g| supervised_task(g, :start) }
|
||||
|
||||
Thread.new { listener.start }
|
||||
wait_for_changes_and_launch_guards
|
||||
guards.each { |guard| supervised_task(guard, :start) }
|
||||
listener.start
|
||||
end
|
||||
end
|
||||
|
||||
def wait_for_changes_and_launch_guards
|
||||
loop do
|
||||
if !running? && !listener.changed_files.empty?
|
||||
changed_files = listener.get_and_clear_changed_files
|
||||
if Watcher.match_files?(guards, changed_files)
|
||||
run do
|
||||
def run_on_change_for_all_guards(files)
|
||||
guards.each do |guard|
|
||||
paths = Watcher.match_files(guard, changed_files)
|
||||
paths = Watcher.match_files(guard, files)
|
||||
supervised_task(guard, :run_on_change, paths) unless paths.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sleep 0.2
|
||||
# Reparse the whole directory to catch new files modified during the guards run
|
||||
new_modified_files = listener.modified_files([Dir.pwd + '/'], :all => true)
|
||||
listener.update_last_event
|
||||
unless new_modified_files.empty?
|
||||
run_on_change_for_all_guards(new_modified_files)
|
||||
end
|
||||
end
|
||||
|
||||
@ -66,17 +66,13 @@ module Guard
|
||||
end
|
||||
|
||||
def run
|
||||
@run = true
|
||||
listener.stop
|
||||
UI.clear if options[:clear]
|
||||
begin
|
||||
yield
|
||||
rescue Interrupt
|
||||
end
|
||||
@run = false
|
||||
end
|
||||
|
||||
def running?
|
||||
@run == true
|
||||
listener.start
|
||||
end
|
||||
|
||||
def add_guard(name, watchers = [], options = {})
|
||||
|
@ -7,7 +7,7 @@ module Guard
|
||||
autoload :Polling, 'guard/listeners/polling'
|
||||
|
||||
class Listener
|
||||
attr_accessor :last_event, :changed_files
|
||||
attr_reader :last_event
|
||||
|
||||
def self.select_and_init
|
||||
if mac? && Darwin.usable?
|
||||
@ -21,38 +21,32 @@ module Guard
|
||||
end
|
||||
|
||||
def initialize
|
||||
@changed_files = []
|
||||
update_last_event
|
||||
end
|
||||
|
||||
def get_and_clear_changed_files
|
||||
files = changed_files.dup
|
||||
changed_files.clear
|
||||
files.uniq
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_changed_files(dirs, options = {})
|
||||
files = potentially_changed_files(dirs, options).select { |path| File.file?(path) && changed_file?(path) }
|
||||
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||
end
|
||||
|
||||
def potentially_changed_files(dirs, options = {})
|
||||
match = options[:all] ? "**/*" : "*"
|
||||
Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
||||
end
|
||||
|
||||
def changed_file?(file)
|
||||
File.mtime(file) >= last_event
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
def update_last_event
|
||||
@last_event = Time.now
|
||||
end
|
||||
|
||||
def modified_files(dirs, options = {})
|
||||
files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && recent_file?(path) }
|
||||
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def potentially_modified_files(dirs, options = {})
|
||||
match = options[:all] ? "**/*" : "*"
|
||||
Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
||||
end
|
||||
|
||||
def recent_file?(file)
|
||||
File.mtime(file) >= last_event
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
def self.mac?
|
||||
Config::CONFIG['target_os'] =~ /darwin/i
|
||||
end
|
||||
@ -63,3 +57,69 @@ module Guard
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# require 'rbconfig'
|
||||
#
|
||||
# module Guard
|
||||
#
|
||||
# autoload :Darwin, 'guard/listeners/darwin'
|
||||
# autoload :Linux, 'guard/listeners/linux'
|
||||
# autoload :Polling, 'guard/listeners/polling'
|
||||
#
|
||||
# class Listener
|
||||
# attr_accessor :last_event, :changed_files
|
||||
#
|
||||
# def self.select_and_init
|
||||
# if mac? && Darwin.usable?
|
||||
# Darwin.new
|
||||
# elsif linux? && Linux.usable?
|
||||
# Linux.new
|
||||
# else
|
||||
# UI.info "Using polling (Please help us to support your system better than that.)"
|
||||
# Polling.new
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def initialize
|
||||
# @changed_files = []
|
||||
# update_last_event
|
||||
# end
|
||||
#
|
||||
# def get_and_clear_changed_files
|
||||
# files = changed_files.dup
|
||||
# changed_files.clear
|
||||
# files.uniq
|
||||
# end
|
||||
#
|
||||
# private
|
||||
#
|
||||
# def find_changed_files(dirs, options = {})
|
||||
# files = potentially_changed_files(dirs, options).select { |path| File.file?(path) && changed_file?(path) }
|
||||
# files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||
# end
|
||||
#
|
||||
# def potentially_changed_files(dirs, options = {})
|
||||
# match = options[:all] ? "**/*" : "*"
|
||||
# Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
|
||||
# end
|
||||
#
|
||||
# def changed_file?(file)
|
||||
# File.mtime(file) >= last_event
|
||||
# rescue
|
||||
# false
|
||||
# end
|
||||
#
|
||||
# def update_last_event
|
||||
# @last_event = Time.now
|
||||
# end
|
||||
#
|
||||
# def self.mac?
|
||||
# Config::CONFIG['target_os'] =~ /darwin/i
|
||||
# end
|
||||
#
|
||||
# def self.linux?
|
||||
# Config::CONFIG['target_os'] =~ /linux/i
|
||||
# end
|
||||
#
|
||||
# end
|
||||
# end
|
@ -5,22 +5,22 @@ module Guard
|
||||
def initialize
|
||||
super
|
||||
@fsevent = FSEvent.new
|
||||
fsevent.watch Dir.pwd do |modified_dirs|
|
||||
@changed_files += find_changed_files(modified_dirs)
|
||||
end
|
||||
|
||||
def on_change(&callback)
|
||||
@fsevent.watch Dir.pwd do |modified_dirs|
|
||||
files = modified_files(modified_dirs)
|
||||
update_last_event
|
||||
callback.call(files)
|
||||
end
|
||||
end
|
||||
|
||||
def start
|
||||
# keep relaunching fsevent.run if not stopped by Guard
|
||||
while @stop != true
|
||||
fsevent.run
|
||||
end
|
||||
@fsevent.run
|
||||
end
|
||||
|
||||
def stop
|
||||
@stop = true
|
||||
fsevent.stop
|
||||
@fsevent.stop
|
||||
end
|
||||
|
||||
def self.usable?
|
||||
|
@ -1,23 +1,33 @@
|
||||
module Guard
|
||||
class Linux < Listener
|
||||
attr_reader :inotify
|
||||
attr_reader :inotify, :files, :latency, :callback
|
||||
|
||||
def initialize
|
||||
super
|
||||
|
||||
@inotify = INotify::Notifier.new
|
||||
inotify.watch(Dir.pwd, :recursive, :modify, :create, :delete, :move) do |event|
|
||||
unless event.name == "" # Event on root directory
|
||||
@changed_files << event.absolute_name.gsub("#{Dir.pwd}/", '')
|
||||
end
|
||||
end
|
||||
@files = []
|
||||
@latency = 0.5
|
||||
end
|
||||
|
||||
def start
|
||||
inotify.run
|
||||
@stop = false
|
||||
watch_change unless watch_change?
|
||||
end
|
||||
|
||||
def stop
|
||||
inotify.stop
|
||||
@stop = true
|
||||
sleep latency
|
||||
end
|
||||
|
||||
def on_change(&callback)
|
||||
@callback = callback
|
||||
inotify.watch(Dir.pwd, :recursive, :modify, :create, :delete, :move) do |event|
|
||||
unless event.name == "" # Event on root directory
|
||||
@files << event.absolute_name
|
||||
end
|
||||
end
|
||||
rescue Interrupt
|
||||
end
|
||||
|
||||
def self.usable?
|
||||
@ -33,5 +43,31 @@ module Guard
|
||||
false
|
||||
end
|
||||
|
||||
def watch_change?
|
||||
!!@watch_change
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def watch_change
|
||||
@watch_change = true
|
||||
while !@stop
|
||||
if Config::CONFIG['build'] =~ /java/ || IO.select([inotify.to_io], [], [], latency)
|
||||
break if @stop
|
||||
|
||||
sleep latency
|
||||
inotify.process
|
||||
update_last_event
|
||||
|
||||
unless files.empty?
|
||||
files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
|
||||
callback.call(files)
|
||||
files.clear
|
||||
end
|
||||
end
|
||||
end
|
||||
@watch_change = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -7,6 +7,10 @@ module Guard
|
||||
@latency = 1.5
|
||||
end
|
||||
|
||||
def on_change(&callback)
|
||||
@callback = callback
|
||||
end
|
||||
|
||||
def start
|
||||
@stop = false
|
||||
watch_change
|
||||
@ -21,10 +25,9 @@ module Guard
|
||||
def watch_change
|
||||
while !@stop
|
||||
start = Time.now.to_f
|
||||
if files = find_changed_files([Dir.pwd + '/'], :all => true)
|
||||
files = modified_files([Dir.pwd + '/'], :all => true)
|
||||
update_last_event
|
||||
@changed_files += files
|
||||
end
|
||||
callback.call(files) unless files.empty?
|
||||
nap_time = latency - (Time.now.to_f - start)
|
||||
sleep(nap_time) if nap_time > 0
|
||||
end
|
||||
|
@ -29,15 +29,15 @@ describe Guard::Listener do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#get_and_clear_changed_files" do
|
||||
subject { Guard::Listener.new }
|
||||
describe "#update_last_event" do
|
||||
subject { described_class.new }
|
||||
|
||||
it "should return uniq changed files and clear it" do
|
||||
subject.changed_files = ["foo", "bar", "bar"]
|
||||
subject.get_and_clear_changed_files.should == ["foo", "bar"]
|
||||
subject.changed_files.should be_empty
|
||||
end
|
||||
it "should update last_event with time.now" do
|
||||
time = Time.now
|
||||
subject.update_last_event
|
||||
subject.last_event.should >= time
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -16,7 +16,13 @@ describe Guard::Darwin do
|
||||
end
|
||||
|
||||
describe "watch" do
|
||||
subject { Guard::Darwin.new }
|
||||
before(:each) do
|
||||
@results = []
|
||||
@listener = Guard::Darwin.new
|
||||
@listener.on_change do |files|
|
||||
@results += files
|
||||
end
|
||||
end
|
||||
|
||||
it "should catch new file" do
|
||||
file = @fixture_path.join("newfile.rb")
|
||||
@ -25,7 +31,7 @@ describe Guard::Darwin do
|
||||
FileUtils.touch file
|
||||
stop
|
||||
File.delete file
|
||||
subject.changed_files.should == ['spec/fixtures/newfile.rb']
|
||||
@results.should == ['spec/fixtures/newfile.rb']
|
||||
end
|
||||
|
||||
it "should catch file update" do
|
||||
@ -34,7 +40,7 @@ describe Guard::Darwin do
|
||||
start
|
||||
FileUtils.touch file
|
||||
stop
|
||||
subject.changed_files.should == ['spec/fixtures/folder1/file1.txt']
|
||||
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
|
||||
it "should catch files update" do
|
||||
@ -46,7 +52,7 @@ describe Guard::Darwin do
|
||||
FileUtils.touch file1
|
||||
FileUtils.touch file2
|
||||
stop
|
||||
subject.changed_files.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -55,13 +61,13 @@ private
|
||||
|
||||
def start
|
||||
sleep 1
|
||||
Thread.new { subject.start }
|
||||
Thread.new { @listener.start }
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def stop
|
||||
sleep 1
|
||||
subject.stop
|
||||
@listener.stop
|
||||
end
|
||||
|
||||
end
|
@ -16,8 +16,38 @@ describe Guard::Linux do
|
||||
subject.should be_usable
|
||||
end
|
||||
|
||||
describe "start" do
|
||||
before(:each) do
|
||||
@listener = Guard::Linux.new
|
||||
end
|
||||
|
||||
it "should call watch_change if first start" do
|
||||
@listener.should_receive(:watch_change)
|
||||
start
|
||||
end
|
||||
|
||||
it "should not call watch_change if start after stop" do
|
||||
@listener.stub!(:stop)
|
||||
start
|
||||
stop
|
||||
@listener.should be_watch_change
|
||||
@listener.should_not_receive(:watch_change)
|
||||
start
|
||||
@listener.unstub!(:stop)
|
||||
stop
|
||||
@listener.should_not be_watch_change
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "watch" do
|
||||
subject { Guard::Linux.new }
|
||||
before(:each) do
|
||||
@results = []
|
||||
@listener = Guard::Linux.new
|
||||
@listener.on_change do |files|
|
||||
@results += files
|
||||
end
|
||||
end
|
||||
|
||||
it "should catch new file" do
|
||||
file = @fixture_path.join("newfile.rb")
|
||||
@ -26,7 +56,7 @@ describe Guard::Linux do
|
||||
FileUtils.touch file
|
||||
stop
|
||||
File.delete file
|
||||
subject.changed_files.should == ['spec/fixtures/newfile.rb']
|
||||
@results.should == ['spec/fixtures/newfile.rb']
|
||||
end
|
||||
|
||||
it "should catch file update" do
|
||||
@ -35,7 +65,7 @@ describe Guard::Linux do
|
||||
start
|
||||
File.open(file, 'w') {|f| f.write('') }
|
||||
stop
|
||||
subject.changed_files.should == ['spec/fixtures/folder1/file1.txt']
|
||||
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
|
||||
it "should catch files update" do
|
||||
@ -47,7 +77,7 @@ describe Guard::Linux do
|
||||
File.open(file1, 'w') {|f| f.write('') }
|
||||
File.open(file2, 'w') {|f| f.write('') }
|
||||
stop
|
||||
subject.changed_files.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
end
|
||||
|
||||
it "should catch deleted file" do
|
||||
@ -57,7 +87,7 @@ describe Guard::Linux do
|
||||
File.delete file
|
||||
stop
|
||||
FileUtils.touch file
|
||||
subject.changed_files.should == ['spec/fixtures/folder1/file1.txt']
|
||||
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
|
||||
it "should catch moved file" do
|
||||
@ -69,17 +99,17 @@ describe Guard::Linux do
|
||||
FileUtils.mv file1, file2
|
||||
stop
|
||||
FileUtils.mv file2, file1
|
||||
subject.changed_files.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt']
|
||||
@results.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt']
|
||||
end
|
||||
|
||||
# it "should not process change if stopped" do
|
||||
# file = @fixture_path.join("folder1/file1.txt")
|
||||
# File.exists?(file).should be_true
|
||||
# start
|
||||
# subject.changed_files.inotify.should_not_receive(:process)
|
||||
# stop
|
||||
# File.open(file, 'w') {|f| f.write('') }
|
||||
# end
|
||||
it "should not process change if stopped" do
|
||||
file = @fixture_path.join("folder1/file1.txt")
|
||||
File.exists?(file).should be_true
|
||||
start
|
||||
@listener.inotify.should_not_receive(:process)
|
||||
stop
|
||||
File.open(file, 'w') {|f| f.write('') }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -87,13 +117,13 @@ private
|
||||
|
||||
def start
|
||||
sleep 1
|
||||
Thread.new { subject.start }
|
||||
Thread.new { @listener.start }
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def stop
|
||||
sleep 1
|
||||
subject.stop
|
||||
@listener.stop
|
||||
sleep 1
|
||||
end
|
||||
|
||||
|
@ -2,7 +2,14 @@ require 'spec_helper'
|
||||
require 'guard/listeners/polling'
|
||||
|
||||
describe Guard::Polling do
|
||||
subject { Guard::Polling.new }
|
||||
|
||||
before(:each) do
|
||||
@results = []
|
||||
@listener = Guard::Polling.new
|
||||
@listener.on_change do |files|
|
||||
@results += files
|
||||
end
|
||||
end
|
||||
|
||||
it "should catch new file" do
|
||||
file = @fixture_path.join("newfile.rb")
|
||||
@ -11,7 +18,7 @@ describe Guard::Polling do
|
||||
FileUtils.touch file
|
||||
stop
|
||||
File.delete file
|
||||
subject.changed_files.should == ['spec/fixtures/newfile.rb']
|
||||
@results.should == ['spec/fixtures/newfile.rb']
|
||||
end
|
||||
|
||||
it "should catch file update" do
|
||||
@ -20,7 +27,7 @@ describe Guard::Polling do
|
||||
start
|
||||
FileUtils.touch file
|
||||
stop
|
||||
subject.changed_files.should == ['spec/fixtures/folder1/file1.txt']
|
||||
@results.should == ['spec/fixtures/folder1/file1.txt']
|
||||
end
|
||||
|
||||
it "should catch files update" do
|
||||
@ -32,19 +39,19 @@ describe Guard::Polling do
|
||||
FileUtils.touch file1
|
||||
FileUtils.touch file2
|
||||
stop
|
||||
subject.changed_files.sort.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
@results.sort.should == ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start
|
||||
Thread.new { subject.start }
|
||||
Thread.new { @listener.start }
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def stop
|
||||
sleep 1
|
||||
subject.stop
|
||||
@listener.stop
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user