From 06be2107afa6f56b3740aaee0e0bc73067a933dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Re=CC=81my=20Coutable?= <rymai@rymai.me>
Date: Thu, 21 Jul 2011 01:40:40 +0200
Subject: [PATCH] Dir.glob now ignores files that don't need to be watched

By default, we don't watch ., .., .bundle, .git (this is HUGE), log, tmp and vendor (this is also HUGE). Also don't append '/' to the dir given to Guard::Listener#potentially_modified_files, the method now handles it internally.

In my case, Guard::Listener#potentially_modified_files was taking ~56 seconds (in a big Rails project), it takes now... less than 1 second.

Enjoy.
---
 lib/guard/listener.rb                | 22 ++++++++++++++++++----
 lib/guard/listeners/polling.rb       |  2 +-
 lib/guard/listeners/windows.rb       |  1 -
 spec/guard/listener_spec.rb          | 16 ++++++++--------
 spec/guard/listeners/darwin_spec.rb  |  2 --
 spec/guard/listeners/linux_spec.rb   |  1 -
 spec/guard/listeners/polling_spec.rb |  6 ++----
 spec/guard/listeners/windows_spec.rb |  1 -
 spec/support/listener_helper.rb      | 11 ++++++-----
 9 files changed, 35 insertions(+), 27 deletions(-)

diff --git a/lib/guard/listener.rb b/lib/guard/listener.rb
index b06aad5..ae8b07a 100644
--- a/lib/guard/listener.rb
+++ b/lib/guard/listener.rb
@@ -61,7 +61,7 @@ module Guard
     end
 
     def all_files
-      potentially_modified_files [directory + '/'], :all => true
+      potentially_modified_files([@directory], :all => true)
     end
 
     # scopes all given paths to the current #directory
@@ -78,9 +78,23 @@ module Guard
 
   private
 
-    def potentially_modified_files(dirs, options = {})
-      match = options[:all] ? "**/*" : "*"
-      Dir.glob(dirs.map { |dir| "#{dir}#{match}" }, File::FNM_DOTMATCH).select { |file| File.file?(file) }
+    def potentially_modified_files(dirs, options={})
+      paths = Dir.glob(dirs.map { |d| "#{d.sub(%r{/+$}, '')}/*" }, File::FNM_DOTMATCH).reject do |path|
+        %w[. .. .bundle .git log tmp vendor].include?(File.basename(path))
+      end
+
+      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
     end
 
     # Depending on the filesystem, mtime is probably only precise to the second, so round
diff --git a/lib/guard/listeners/polling.rb b/lib/guard/listeners/polling.rb
index 1c9b0db..fad687c 100644
--- a/lib/guard/listeners/polling.rb
+++ b/lib/guard/listeners/polling.rb
@@ -22,7 +22,7 @@ module Guard
     def watch_change
       until @stop
         start = Time.now.to_f
-        files = modified_files([Dir.pwd + '/'], :all => true)
+        files = modified_files([Dir.pwd], :all => true)
         @callback.call(files) unless files.empty?
         nap_time = @latency - (Time.now.to_f - start)
         sleep(nap_time) if nap_time > 0
diff --git a/lib/guard/listeners/windows.rb b/lib/guard/listeners/windows.rb
index 64af05c..f6ca03a 100644
--- a/lib/guard/listeners/windows.rb
+++ b/lib/guard/listeners/windows.rb
@@ -33,7 +33,6 @@ module Guard
 
     def watch(directory)
       worker.watch(directory, :all_events, :recursive) do |event|
-        paths = [File.expand_path(event.watcher.path) + '/']
         paths = [File.expand_path(event.watcher.path)]
         files = modified_files(paths, :all => true)
         @callback.call(files) unless files.empty?
diff --git a/spec/guard/listener_spec.rb b/spec/guard/listener_spec.rb
index 6eb675a..e9ae26b 100644
--- a/spec/guard/listener_spec.rb
+++ b/spec/guard/listener_spec.rb
@@ -32,9 +32,9 @@ describe Guard::Listener do
       subject.stub!(:mac?).and_return(true)
       Guard::Darwin.stub!(:usable?).and_return(true)
 
-      path, opts = 'path', {:foo => 23}
+      path, opts = 'path', { :foo => 23 }
       Guard::Darwin.should_receive(:new).with(path, opts).and_return(true)
-      subject.select_and_init path, opts
+      subject.select_and_init(path, opts)
     end
   end
 
@@ -90,24 +90,24 @@ describe Guard::Listener do
     context "without the :all option" do
       it "finds modified files only in the directory supplied" do
         FileUtils.touch([file1, file2, file3])
-        subject.modified_files([@fixture_path.join("folder1/")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
+        subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
       end
     end
 
     context "with the :all options" do
       it "finds modified files within subdirectories" do
         FileUtils.touch([file1, file2, file3])
-        subject.modified_files([@fixture_path.join("folder1/")], { :all => true }).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt", "spec/fixtures/folder1/folder2/file2.txt"]
+        subject.modified_files([@fixture_path.join("folder1")], { :all => true }).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt", "spec/fixtures/folder1/folder2/file2.txt"]
       end
     end
 
     context "without updating the content" do
       it "ignores the files for the second time" do
         FileUtils.touch([file1, file2, file3])
-        subject.modified_files([@fixture_path.join("folder1/")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
+        subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
         subject.update_last_event
         FileUtils.touch([file1, file2, file3])
-        subject.modified_files([@fixture_path.join("folder1/")], {}).should == []
+        subject.modified_files([@fixture_path.join("folder1")], {}).should be_empty
         sleep 1
       end
     end
@@ -117,11 +117,11 @@ describe Guard::Listener do
 
       it "identifies the files for the second time" do
         FileUtils.touch([file1, file2, file3])
-        subject.modified_files([@fixture_path.join("folder1/")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
+        subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/deletedfile1.txt", "spec/fixtures/folder1/file1.txt"]
         subject.update_last_event
         FileUtils.touch([file2, file3])
         File.open(file1, "w") { |f| f.write("changed content") }
-        subject.modified_files([@fixture_path.join("folder1/")], {}).should =~ ["spec/fixtures/folder1/file1.txt"]
+        subject.modified_files([@fixture_path.join("folder1")], {}).should =~ ["spec/fixtures/folder1/file1.txt"]
         sleep 1
       end
     end
diff --git a/spec/guard/listeners/darwin_spec.rb b/spec/guard/listeners/darwin_spec.rb
index 25c26f3..1b679bb 100644
--- a/spec/guard/listeners/darwin_spec.rb
+++ b/spec/guard/listeners/darwin_spec.rb
@@ -23,7 +23,5 @@ describe Guard::Darwin do
 
     it_should_behave_like "a listener that reacts to #on_change"
     it_should_behave_like "a listener scoped to a specific directory"
-
   end
-
 end
diff --git a/spec/guard/listeners/linux_spec.rb b/spec/guard/listeners/linux_spec.rb
index c6ea166..8bd1598 100644
--- a/spec/guard/listeners/linux_spec.rb
+++ b/spec/guard/listeners/linux_spec.rb
@@ -74,5 +74,4 @@ describe Guard::Linux do
     end
 
   end
-
 end
diff --git a/spec/guard/listeners/polling_spec.rb b/spec/guard/listeners/polling_spec.rb
index 90d389f..072421e 100644
--- a/spec/guard/listeners/polling_spec.rb
+++ b/spec/guard/listeners/polling_spec.rb
@@ -2,10 +2,8 @@ require 'spec_helper'
 require 'guard/listeners/polling'
 
 describe Guard::Polling do
-
   subject { Guard::Polling }
 
-  it_should_behave_like "a listener that reacts to #on_change" 
-  it_should_behave_like "a listener scoped to a specific directory" 
-
+  it_should_behave_like "a listener that reacts to #on_change"
+  it_should_behave_like "a listener scoped to a specific directory"
 end
diff --git a/spec/guard/listeners/windows_spec.rb b/spec/guard/listeners/windows_spec.rb
index 133dc83..f59cf55 100644
--- a/spec/guard/listeners/windows_spec.rb
+++ b/spec/guard/listeners/windows_spec.rb
@@ -25,5 +25,4 @@ describe Guard::Windows do
     it_should_behave_like "a listener scoped to a specific directory" 
 
   end
-
 end
diff --git a/spec/support/listener_helper.rb b/spec/support/listener_helper.rb
index b52bd5d..03bc21b 100644
--- a/spec/support/listener_helper.rb
+++ b/spec/support/listener_helper.rb
@@ -53,7 +53,7 @@ shared_examples_for 'a listener that reacts to #on_change' do
     file = @fixture_path.join("folder1/file1.txt")
     File.exists?(file).should be_true
     start
-    File.open(file, 'w') {|f| f.write('') }
+    File.open(file, 'w') { |f| f.write('') }
     stop
     results.should =~ ['spec/fixtures/folder1/file1.txt']
   end
@@ -62,7 +62,7 @@ shared_examples_for 'a listener that reacts to #on_change' do
     file = @fixture_path.join(".dotfile")
     File.exists?(file).should be_true
     start
-    File.open(file, 'w') {|f| f.write('') }
+    File.open(file, 'w') { |f| f.write('') }
     stop
     results.should =~ ['spec/fixtures/.dotfile']
   end
@@ -73,8 +73,8 @@ shared_examples_for 'a listener that reacts to #on_change' do
     File.exists?(file1).should be_true
     File.exists?(file2).should be_true
     start
-    File.open(file1, 'w') {|f| f.write('') }
-    File.open(file2, 'w') {|f| f.write('') }
+    File.open(file1, 'w') { |f| f.write('') }
+    File.open(file2, 'w') { |f| f.write('') }
     stop
     results.should =~ ['spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/folder2/file2.txt']
   end
@@ -108,6 +108,7 @@ shared_examples_for "a listener scoped to a specific directory" do
     @wd = @fixture_path.join("folder1")
     @listener = described_class.new @wd
   end
+
   it "should base paths within this directory" do
     record_results
     new_file = @wd.join("folder2/newfile.rb")
@@ -116,7 +117,7 @@ shared_examples_for "a listener scoped to a specific directory" do
     File.exists?(new_file).should be_false
     start
     FileUtils.touch new_file
-    File.open(modified, 'w') {|f| f.write('') }
+    File.open(modified, 'w') { |f| f.write('') }
     stop
     File.delete new_file
     results.should =~ ["folder2/newfile.rb", 'file1.txt']