From 785adabc4b8d892b6e06fca2f259e9c5147e9ca5 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Mon, 12 Oct 2015 20:41:14 +0200
Subject: implements an evented file update checker [Puneet Agarwal]

This is the implementation of the file update checker written
by Puneet Agarwal for GSoC 2015 (except for the tiny version
of the listen gem, which was 3.0.2 in the original patch).

Puneet's branch became too out of sync with upstream. This is
the final work in one single clean commit.

Credit goes in the first line using a convention understood
by the contrib app.
---
 activesupport/lib/active_support.rb                |   1 +
 .../active_support/file_evented_update_checker.rb  |  67 +++++++++++++
 .../test/file_evented_update_checker_test.rb       |  21 ++++
 activesupport/test/file_update_checker_test.rb     | 107 +-------------------
 ...le_update_checker_with_enumerable_test_cases.rb | 110 +++++++++++++++++++++
 5 files changed, 203 insertions(+), 103 deletions(-)
 create mode 100644 activesupport/lib/active_support/file_evented_update_checker.rb
 create mode 100644 activesupport/test/file_evented_update_checker_test.rb
 create mode 100644 activesupport/test/file_update_checker_with_enumerable_test_cases.rb

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 63277a65b4..3a2a7d28cb 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -34,6 +34,7 @@ module ActiveSupport
   autoload :Dependencies
   autoload :DescendantsTracker
   autoload :FileUpdateChecker
+  autoload :FileEventedUpdateChecker
   autoload :LogSubscriber
   autoload :Notifications
 
diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
new file mode 100644
index 0000000000..d45576bb00
--- /dev/null
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -0,0 +1,67 @@
+require 'listen'
+
+module ActiveSupport
+  class FileEventedUpdateChecker
+    attr_reader :listener
+    def initialize(files, directories={}, &block)
+      @files = files.map { |f| File.expand_path(f)}.to_set
+      @dirs = Hash.new
+      directories.each do |key,value|
+        @dirs[File.expand_path(key)] = Array(value) if !Array(value).empty?
+      end
+      @block = block
+      @modified = false
+      watch_dirs = base_directories
+      @listener = Listen.to(*watch_dirs,&method(:changed)) if !watch_dirs.empty?
+      @listener.start if @listener
+    end
+
+    def updated?
+      @modified
+    end
+
+    def execute
+      @block.call
+    ensure
+      @modified = false
+    end
+
+    def execute_if_updated
+      if updated?
+        execute
+        true
+      else
+        false
+      end
+    end
+
+    private
+
+    def watching?(file)
+      return true if @files.include?(file)
+      cfile = file
+      while !cfile.eql? "/"
+        cfile = File.expand_path("#{cfile}/..")
+        if !@dirs[cfile].nil? and file.end_with?(*(@dirs[cfile].map {|ext| ".#{ext.to_s}"}))
+          return true
+        end
+      end
+      false
+    end
+
+    def changed(modified, added, removed)
+      return if updated?
+      if (modified + added + removed).any? { |f| watching? f }
+        @modified = true
+      end
+    end
+
+    def base_directories
+      (@files.map { |f| existing_parent(File.expand_path("#{f}/..")) } + @dirs.keys.map {|dir| existing_parent(dir)}).uniq
+    end
+
+    def existing_parent(path)
+      File.exist?(path) ? path : existing_parent(File.expand_path("#{path}/.."))
+    end
+  end
+end
diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
new file mode 100644
index 0000000000..09087738dc
--- /dev/null
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -0,0 +1,21 @@
+require 'abstract_unit'
+require 'fileutils'
+require 'thread'
+require 'file_update_checker_with_enumerable_test_cases'
+
+MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
+
+class FileEventedUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase
+	include FileUpdateCheckerWithEnumerableTestCases
+  def build_new_watcher(files, dirs={}, &block)
+    ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block)
+  end
+
+  def test_modified_should_become_true_when_watched_file_is_updated
+  	watcher = ActiveSupport::FileEventedUpdateChecker.new(FILES){ i += 1 }
+  	assert_equal watcher.updated?, false
+  	FileUtils.rm(FILES)
+  	sleep 1
+  	assert_equal watcher.updated?, true
+  end
+end
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index bd1df0f858..c61193d133 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -1,112 +1,13 @@
 require 'abstract_unit'
 require 'fileutils'
 require 'thread'
+require 'file_update_checker_with_enumerable_test_cases'
 
 MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
 
 class FileUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase
-  FILES = %w(1.txt 2.txt 3.txt)
-
-  def setup
-    FileUtils.mkdir_p("tmp_watcher")
-    FileUtils.touch(FILES)
-  end
-
-  def teardown
-    FileUtils.rm_rf("tmp_watcher")
-    FileUtils.rm_rf(FILES)
-  end
-
-  def test_should_not_execute_the_block_if_no_paths_are_given
-    i = 0
-    checker = ActiveSupport::FileUpdateChecker.new([]){ i += 1 }
-    checker.execute_if_updated
-    assert_equal 0, i
-  end
-
-  def test_should_not_invoke_the_block_if_no_file_has_changed
-    i = 0
-    checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
-    5.times { assert !checker.execute_if_updated }
-    assert_equal 0, i
-  end
-
-  def test_should_invoke_the_block_if_a_file_has_changed
-    i = 0
-    checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
-    sleep(1)
-    FileUtils.touch(FILES)
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_be_robust_enough_to_handle_deleted_files
-    i = 0
-    checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
-    FileUtils.rm(FILES)
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_be_robust_to_handle_files_with_wrong_modified_time
-    i = 0
-    now = Time.now
-    time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
-    File.utime time, time, FILES[2]
-
-    checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
-
-    sleep(1)
-    FileUtils.touch(FILES[0..1])
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_cache_updated_result_until_execute
-    i = 0
-    checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
-    assert !checker.updated?
-
-    sleep(1)
-    FileUtils.touch(FILES)
-
-    assert checker.updated?
-    checker.execute
-    assert !checker.updated?
-  end
-
-  def test_should_invoke_the_block_if_a_watched_dir_changed_its_glob
-    i = 0
-    checker = ActiveSupport::FileUpdateChecker.new([], "tmp_watcher" => [:txt]){ i += 1 }
-    FileUtils.cd "tmp_watcher" do
-      FileUtils.touch(FILES)
-    end
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_not_invoke_the_block_if_a_watched_dir_changed_its_glob
-    i = 0
-    checker = ActiveSupport::FileUpdateChecker.new([], "tmp_watcher" => :rb){ i += 1 }
-    FileUtils.cd "tmp_watcher" do
-      FileUtils.touch(FILES)
-    end
-    assert !checker.execute_if_updated
-    assert_equal 0, i
-  end
-
-  def test_should_not_block_if_a_strange_filename_used
-    FileUtils.mkdir_p("tmp_watcher/valid,yetstrange,path,")
-    FileUtils.touch(FILES.map { |file_name| "tmp_watcher/valid,yetstrange,path,/#{file_name}" })
-
-    test = Thread.new do
-      ActiveSupport::FileUpdateChecker.new([],"tmp_watcher/valid,yetstrange,path," => :txt) { i += 1 }
-      Thread.exit
-    end
-    test.priority = -1
-    test.join(5)
-
-    assert !test.alive?
+  include FileUpdateCheckerWithEnumerableTestCases
+  def build_new_watcher(files, dirs={}, &block)
+    ActiveSupport::FileUpdateChecker.new(files, dirs, &block)
   end
 end
diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
new file mode 100644
index 0000000000..cd1f12d42f
--- /dev/null
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -0,0 +1,110 @@
+module FileUpdateCheckerWithEnumerableTestCases
+  FILES = %w(1.txt 2.txt 3.txt)
+
+  def setup
+    FileUtils.mkdir_p("tmp_watcher")
+    FileUtils.touch(FILES)
+  end
+
+  def teardown
+    FileUtils.rm_rf("tmp_watcher")
+    FileUtils.rm_rf(FILES)
+  end
+
+  def test_should_not_execute_the_block_if_no_paths_are_given
+    i = 0
+    checker = build_new_watcher([]){ i += 1}
+    checker.execute_if_updated
+    assert_equal 0, i
+  end
+
+  def test_should_not_invoke_the_block_if_no_file_has_changed
+    i = 0
+    checker = build_new_watcher(FILES){ i += 1 }
+    5.times { assert !checker.execute_if_updated }
+    assert_equal 0, i
+  end
+
+  def test_should_invoke_the_block_if_a_file_has_changed
+    i = 0
+    checker = build_new_watcher(FILES){ i += 1 }
+    sleep(1)
+    FileUtils.touch(FILES)
+    sleep(1) #extra
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_be_robust_enough_to_handle_deleted_files
+    i = 0
+    checker = build_new_watcher(FILES){ i += 1 }
+    FileUtils.rm(FILES)
+    sleep(1) #extra
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_be_robust_to_handle_files_with_wrong_modified_time
+    i = 0
+    now = Time.now
+    time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
+    File.utime time, time, FILES[2]
+
+    checker = build_new_watcher(FILES){ i += 1 }
+
+    sleep(1)
+    FileUtils.touch(FILES[0..1])
+    sleep(1) #extra
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_cache_updated_result_until_execute
+    i = 0
+    checker = build_new_watcher(FILES){ i += 1 }
+    assert !checker.updated?
+
+    sleep(1)
+    FileUtils.touch(FILES)
+    sleep(1) #extra
+    assert checker.updated?
+    checker.execute
+    assert !checker.updated?
+  end
+
+  def test_should_invoke_the_block_if_a_watched_dir_changed_its_glob
+    i = 0
+    checker = build_new_watcher([], "tmp_watcher" => [:txt]){ i += 1 }
+    FileUtils.cd "tmp_watcher" do
+      FileUtils.touch(FILES)
+    end
+    sleep(1) #extra
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_not_invoke_the_block_if_a_watched_dir_changed_its_glob
+    i = 0
+    checker = build_new_watcher([], "tmp_watcher" => :rb){ i += 1 }
+    FileUtils.cd "tmp_watcher" do
+      FileUtils.touch(FILES)
+    end
+    sleep(1) #extra
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+  end
+
+  def test_should_not_block_if_a_strange_filename_used
+    FileUtils.mkdir_p("tmp_watcher/valid,yetstrange,path,")
+    FileUtils.touch(FILES.map { |file_name| "tmp_watcher/valid,yetstrange,path,/#{file_name}" })
+
+    test = Thread.new do
+      build_new_watcher([],"tmp_watcher/valid,yetstrange,path," => :txt) { i += 1 }
+      Thread.exit
+    end
+    test.priority = -1
+    test.join(5)
+
+    assert !test.alive?
+  end
+end
-- 
cgit v1.2.3


From 087a79ab2fe4dfbe1cf1daafe91be5c9ef39be0c Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Mon, 12 Oct 2015 21:44:21 +0200
Subject: initial edit pass over the evented file checker patch

---
 .../test/file_evented_update_checker_test.rb       | 15 +---
 activesupport/test/file_update_checker_test.rb     |  5 +-
 ...le_update_checker_with_enumerable_test_cases.rb | 88 +++++++++++++++-------
 3 files changed, 67 insertions(+), 41 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index 09087738dc..ebcba078ce 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -3,19 +3,12 @@ require 'fileutils'
 require 'thread'
 require 'file_update_checker_with_enumerable_test_cases'
 
-MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
+MTIME_FIXTURES_PATH = File.expand_path('fixtures', __dir__)
+
+class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
+  include FileUpdateCheckerWithEnumerableTestCases
 
-class FileEventedUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase
-	include FileUpdateCheckerWithEnumerableTestCases
   def build_new_watcher(files, dirs={}, &block)
     ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block)
   end
-
-  def test_modified_should_become_true_when_watched_file_is_updated
-  	watcher = ActiveSupport::FileEventedUpdateChecker.new(FILES){ i += 1 }
-  	assert_equal watcher.updated?, false
-  	FileUtils.rm(FILES)
-  	sleep 1
-  	assert_equal watcher.updated?, true
-  end
 end
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index c61193d133..9c517290e2 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -3,10 +3,11 @@ require 'fileutils'
 require 'thread'
 require 'file_update_checker_with_enumerable_test_cases'
 
-MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
+MTIME_FIXTURES_PATH = File.expand_path('fixtures', __dir__)
 
-class FileUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase
+class FileUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
+
   def build_new_watcher(files, dirs={}, &block)
     ActiveSupport::FileUpdateChecker.new(files, dirs, &block)
   end
diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index cd1f12d42f..c7b5ffb7a5 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -2,71 +2,95 @@ module FileUpdateCheckerWithEnumerableTestCases
   FILES = %w(1.txt 2.txt 3.txt)
 
   def setup
-    FileUtils.mkdir_p("tmp_watcher")
+    FileUtils.mkdir_p('tmp_watcher')
     FileUtils.touch(FILES)
   end
 
   def teardown
-    FileUtils.rm_rf("tmp_watcher")
+    FileUtils.rm_rf('tmp_watcher')
     FileUtils.rm_rf(FILES)
   end
 
   def test_should_not_execute_the_block_if_no_paths_are_given
     i = 0
-    checker = build_new_watcher([]){ i += 1}
+
+    checker = build_new_watcher([]) { i += 1 }
     checker.execute_if_updated
+
     assert_equal 0, i
   end
 
   def test_should_not_invoke_the_block_if_no_file_has_changed
     i = 0
-    checker = build_new_watcher(FILES){ i += 1 }
-    5.times { assert !checker.execute_if_updated }
+
+    checker = build_new_watcher(FILES) { i += 1 }
+
+    assert !checker.execute_if_updated
     assert_equal 0, i
   end
 
   def test_should_invoke_the_block_if_a_file_has_changed
     i = 0
-    checker = build_new_watcher(FILES){ i += 1 }
-    sleep(1)
+
+    checker = build_new_watcher(FILES) { i += 1 }
+    sleep 1
+
     FileUtils.touch(FILES)
-    sleep(1) #extra
+    sleep 1
+
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
+  def test_updated_should_become_true_when_watched_files_are_deleted
+    watcher = build_new_watcher(FILES) { i += 1 }
+    assert !watcher.updated?
+
+    FileUtils.rm(FILES)
+    sleep 1
+
+    assert watcher.updated?
+  end
+
   def test_should_be_robust_enough_to_handle_deleted_files
     i = 0
-    checker = build_new_watcher(FILES){ i += 1 }
-    FileUtils.rm(FILES)
-    sleep(1) #extra
+
+    checker = build_new_watcher(FILES) { i += 1 }
+    FileUtils.rm_f(FILES)
+
+    sleep 1
+
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
   def test_should_be_robust_to_handle_files_with_wrong_modified_time
     i = 0
+
     now = Time.now
     time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
-    File.utime time, time, FILES[2]
+    File.utime(time, time, FILES[2])
 
-    checker = build_new_watcher(FILES){ i += 1 }
+    checker = build_new_watcher(FILES) { i += 1 }
+    sleep 1
 
-    sleep(1)
     FileUtils.touch(FILES[0..1])
-    sleep(1) #extra
+    sleep 1
+
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
   def test_should_cache_updated_result_until_execute
     i = 0
-    checker = build_new_watcher(FILES){ i += 1 }
+
+    checker = build_new_watcher(FILES) { i += 1 }
     assert !checker.updated?
+    sleep 1
 
-    sleep(1)
     FileUtils.touch(FILES)
-    sleep(1) #extra
+    sleep 1
+
     assert checker.updated?
     checker.execute
     assert !checker.updated?
@@ -74,34 +98,42 @@ module FileUpdateCheckerWithEnumerableTestCases
 
   def test_should_invoke_the_block_if_a_watched_dir_changed_its_glob
     i = 0
-    checker = build_new_watcher([], "tmp_watcher" => [:txt]){ i += 1 }
-    FileUtils.cd "tmp_watcher" do
+
+    checker = build_new_watcher([], 'tmp_watcher' => [:txt]) { i += 1 }
+
+    FileUtils.cd 'tmp_watcher' do
       FileUtils.touch(FILES)
     end
-    sleep(1) #extra
+    sleep 1
+
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
   def test_should_not_invoke_the_block_if_a_watched_dir_changed_its_glob
     i = 0
-    checker = build_new_watcher([], "tmp_watcher" => :rb){ i += 1 }
-    FileUtils.cd "tmp_watcher" do
+
+    checker = build_new_watcher([], 'tmp_watcher' => :rb) { i += 1 }
+
+    FileUtils.cd 'tmp_watcher' do
       FileUtils.touch(FILES)
     end
-    sleep(1) #extra
+    sleep 1
+
     assert !checker.execute_if_updated
     assert_equal 0, i
   end
 
-  def test_should_not_block_if_a_strange_filename_used
-    FileUtils.mkdir_p("tmp_watcher/valid,yetstrange,path,")
-    FileUtils.touch(FILES.map { |file_name| "tmp_watcher/valid,yetstrange,path,/#{file_name}" })
+  def test_should_not_block_with_unusual_file_names
+    unusual_dirname = 'tmp_watcher/valid,yetstrange,path,'
+    FileUtils.mkdir_p(unusual_dirname)
+    FileUtils.touch(FILES.map { |file_name| "#{unusual_dirname}/#{file_name}" })
 
     test = Thread.new do
-      build_new_watcher([],"tmp_watcher/valid,yetstrange,path," => :txt) { i += 1 }
+      build_new_watcher([], unusual_dirname => :txt) { i += 1 }
       Thread.exit
     end
+
     test.priority = -1
     test.join(5)
 
-- 
cgit v1.2.3


From 07369448d3c7b62c98abc0f497c497d1e4e5e799 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 18 Oct 2015 23:07:03 +0200
Subject: revises the implementation of the evented file monitor

---
 .../active_support/file_evented_update_checker.rb  | 89 ++++++++++++++++------
 1 file changed, 65 insertions(+), 24 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index d45576bb00..5a7e0d2794 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -1,19 +1,26 @@
 require 'listen'
+require 'set'
+require 'pathname'
 
 module ActiveSupport
   class FileEventedUpdateChecker
     attr_reader :listener
-    def initialize(files, directories={}, &block)
-      @files = files.map { |f| File.expand_path(f)}.to_set
-      @dirs = Hash.new
-      directories.each do |key,value|
-        @dirs[File.expand_path(key)] = Array(value) if !Array(value).empty?
+
+    def initialize(files, dirs={}, &block)
+      @files = files.map {|f| expand_path(f)}.to_set
+
+      @dirs = {}
+      dirs.each do |dir, exts|
+        @dirs[expand_path(dir)] = Array(exts).map(&:to_s)
       end
+
       @block = block
       @modified = false
-      watch_dirs = base_directories
-      @listener = Listen.to(*watch_dirs,&method(:changed)) if !watch_dirs.empty?
-      @listener.start if @listener
+
+      if (watch_dirs = base_directories).any?
+        @listener = Listen.to(*watch_dirs, &method(:changed))
+        @listener.start
+      end
     end
 
     def updated?
@@ -30,38 +37,72 @@ module ActiveSupport
       if updated?
         execute
         true
-      else
-        false
       end
     end
 
     private
 
-    def watching?(file)
-      return true if @files.include?(file)
-      cfile = file
-      while !cfile.eql? "/"
-        cfile = File.expand_path("#{cfile}/..")
-        if !@dirs[cfile].nil? and file.end_with?(*(@dirs[cfile].map {|ext| ".#{ext.to_s}"}))
-          return true
-        end
-      end
-      false
+    def expand_path(fname)
+      File.expand_path(fname)
     end
 
     def changed(modified, added, removed)
       return if updated?
-      if (modified + added + removed).any? { |f| watching? f }
+
+      if (modified + added + removed).any? {|f| watching?(f)}
         @modified = true
       end
     end
 
+    def watching?(file)
+      file = expand_path(file)
+      return true if @files.member?(file)
+
+      file = Pathname.new(file)
+      return false if file.directory?
+
+      ext = file.extname.sub(/\A\./, '')
+      dir = file.dirname
+
+      loop do
+        if @dirs.fetch(dir.to_path, []).include?(ext)
+          break true
+        else
+          if dir.root? # TODO: find a common parent directory in initialize
+            break false
+          end
+          dir = dir.parent
+        end
+      end
+    end
+
+    # TODO: Better return a list of non-nested directories.
     def base_directories
-      (@files.map { |f| existing_parent(File.expand_path("#{f}/..")) } + @dirs.keys.map {|dir| existing_parent(dir)}).uniq
+      [].tap do |bd|
+        bd.concat @files.map {|f| existing_parent(File.dirname(f))}
+        bd.concat @dirs.keys.map {|dir| existing_parent(dir)}
+        bd.compact!
+        bd.uniq!
+      end
     end
 
-    def existing_parent(path)
-      File.exist?(path) ? path : existing_parent(File.expand_path("#{path}/.."))
+    def existing_parent(dir)
+      dir = Pathname.new(File.expand_path(dir))
+
+      loop do
+        if dir.directory?
+          break dir.to_path
+        else
+          if dir.root?
+            # Edge case in which not even the root exists. For example, Windows
+            # paths could have a non-existing drive letter. Since the parent of
+            # root is root, we need to break to prevent an infinite loop.
+            break
+          else
+            dir = dir.parent
+          end
+        end
+      end
     end
   end
 end
-- 
cgit v1.2.3


From 0462329166cc667c2dc81abc85493e2ae6db5e4c Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 4 Nov 2015 08:32:33 +0100
Subject: no need to have access to the listener

---
 activesupport/lib/active_support/file_evented_update_checker.rb | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 5a7e0d2794..70d38c10bb 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -4,8 +4,6 @@ require 'pathname'
 
 module ActiveSupport
   class FileEventedUpdateChecker
-    attr_reader :listener
-
     def initialize(files, dirs={}, &block)
       @files = files.map {|f| expand_path(f)}.to_set
 
@@ -18,8 +16,7 @@ module ActiveSupport
       @modified = false
 
       if (watch_dirs = base_directories).any?
-        @listener = Listen.to(*watch_dirs, &method(:changed))
-        @listener.start
+        Listen.to(*watch_dirs, &method(:changed)).start
       end
     end
 
-- 
cgit v1.2.3


From b685095fc957a428fd86514d28a9183dbbdc3ab5 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 4 Nov 2015 08:34:28 +0100
Subject: remove explicit File.expand_path call

---
 activesupport/lib/active_support/file_evented_update_checker.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 70d38c10bb..18ac398a43 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -84,7 +84,7 @@ module ActiveSupport
     end
 
     def existing_parent(dir)
-      dir = Pathname.new(File.expand_path(dir))
+      dir = Pathname.new(expand_path(dir))
 
       loop do
         if dir.directory?
-- 
cgit v1.2.3


From cfb487535bbdaedd6b8f8b55476f0991829f809c Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 4 Nov 2015 17:16:05 +0100
Subject: remove unused constants in the file monitor suites

---
 activesupport/test/file_evented_update_checker_test.rb | 2 --
 activesupport/test/file_update_checker_test.rb         | 2 --
 2 files changed, 4 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index ebcba078ce..10b7a5c979 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -3,8 +3,6 @@ require 'fileutils'
 require 'thread'
 require 'file_update_checker_with_enumerable_test_cases'
 
-MTIME_FIXTURES_PATH = File.expand_path('fixtures', __dir__)
-
 class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
 
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index 9c517290e2..313c818691 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -3,8 +3,6 @@ require 'fileutils'
 require 'thread'
 require 'file_update_checker_with_enumerable_test_cases'
 
-MTIME_FIXTURES_PATH = File.expand_path('fixtures', __dir__)
-
 class FileUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
 
-- 
cgit v1.2.3


From a62387d620b8b6862922b6a359b76c584986075d Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 14:29:26 -0800
Subject: stop ascending at the longest common subpath

This commit also bases everything on Pathname internally.
---
 .../active_support/file_evented_update_checker.rb  | 99 ++++++++++++++--------
 .../test/file_evented_update_checker_test.rb       | 60 +++++++++++++
 2 files changed, 126 insertions(+), 33 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 18ac398a43..a4dd4ff30c 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -3,17 +3,19 @@ require 'set'
 require 'pathname'
 
 module ActiveSupport
-  class FileEventedUpdateChecker
+  class FileEventedUpdateChecker #:nodoc: all
     def initialize(files, dirs={}, &block)
-      @files = files.map {|f| expand_path(f)}.to_set
+      @ph    = PathHelper.new
+      @files = files.map {|f| @ph.xpath(f)}.to_set
 
       @dirs = {}
       dirs.each do |dir, exts|
-        @dirs[expand_path(dir)] = Array(exts).map(&:to_s)
+        @dirs[@ph.xpath(dir)] = Array(exts).map {|ext| @ph.normalize_extension(ext)}
       end
 
-      @block = block
+      @block    = block
       @modified = false
+      @lcsp     = @ph.longest_common_subpath(@dirs.keys)
 
       if (watch_dirs = base_directories).any?
         Listen.to(*watch_dirs, &method(:changed)).start
@@ -39,35 +41,31 @@ module ActiveSupport
 
     private
 
-    def expand_path(fname)
-      File.expand_path(fname)
-    end
-
     def changed(modified, added, removed)
-      return if updated?
-
-      if (modified + added + removed).any? {|f| watching?(f)}
-        @modified = true
+      unless updated?
+        @modified = (modified + added + removed).any? {|f| watching?(f)}
       end
     end
 
     def watching?(file)
-      file = expand_path(file)
-      return true if @files.member?(file)
+      file = @ph.xpath(file)
 
-      file = Pathname.new(file)
+      return true  if @files.member?(file)
       return false if file.directory?
 
-      ext = file.extname.sub(/\A\./, '')
+      ext = @ph.normalize_extension(file.extname)
       dir = file.dirname
 
       loop do
-        if @dirs.fetch(dir.to_path, []).include?(ext)
+        if @dirs.fetch(dir, []).include?(ext)
           break true
         else
-          if dir.root? # TODO: find a common parent directory in initialize
-            break false
+          if @lcsp
+            break false if dir == @lcsp
+          else
+            break false if dir.root?
           end
+
           dir = dir.parent
         end
       end
@@ -76,27 +74,62 @@ module ActiveSupport
     # TODO: Better return a list of non-nested directories.
     def base_directories
       [].tap do |bd|
-        bd.concat @files.map {|f| existing_parent(File.dirname(f))}
-        bd.concat @dirs.keys.map {|dir| existing_parent(dir)}
+        bd.concat @files.map {|f| @ph.existing_parent(f.dirname)}
+        bd.concat @dirs.keys.map {|dir| @ph.existing_parent(dir)}
         bd.compact!
         bd.uniq!
       end
     end
 
-    def existing_parent(dir)
-      dir = Pathname.new(expand_path(dir))
+    class PathHelper
+      def xpath(path)
+        Pathname.new(path).expand_path
+      end
 
-      loop do
-        if dir.directory?
-          break dir.to_path
-        else
-          if dir.root?
-            # Edge case in which not even the root exists. For example, Windows
-            # paths could have a non-existing drive letter. Since the parent of
-            # root is root, we need to break to prevent an infinite loop.
-            break
+      def normalize_extension(ext)
+        ext.to_s.sub(/\A\./, '')
+      end
+
+      # Given a collection of Pathname objects returns the longest subpath
+      # common to all of them, or +nil+ if there is none.
+      def longest_common_subpath(paths)
+        return if paths.empty?
+
+        csp = Pathname.new(paths[0])
+
+        paths[1..-1].each do |path|
+          loop do
+            break if path.ascend do |ascendant|
+              break true if ascendant == csp
+            end
+
+            if csp.root?
+              # A root directory is not an ascendant of path. This may happen
+              # if there are paths in different drives on Windows.
+              return
+            else
+              csp = csp.parent
+            end
+          end
+        end
+
+        csp
+      end
+
+      # Returns the deepest existing ascendant, which could be the argument itself.
+      def existing_parent(dir)
+        loop do
+          if dir.directory?
+            break dir
           else
-            dir = dir.parent
+            if dir.root?
+              # Edge case in which not even the root exists. For example, Windows
+              # paths could have a non-existing drive letter. Since the parent of
+              # root is root, we need to break to prevent an infinite loop.
+              break
+            else
+              dir = dir.parent
+            end
           end
         end
       end
diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index 10b7a5c979..727b99958e 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -1,6 +1,7 @@
 require 'abstract_unit'
 require 'fileutils'
 require 'thread'
+require 'pathname'
 require 'file_update_checker_with_enumerable_test_cases'
 
 class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
@@ -10,3 +11,62 @@ class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
     ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block)
   end
 end
+
+class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
+  def pn(path)
+    Pathname.new(path)
+  end
+
+  setup do
+    @ph = ActiveSupport::FileEventedUpdateChecker::PathHelper.new
+  end
+
+  test '#xpath returns the expanded path as a Pathname object' do
+    assert_equal pn(__FILE__).expand_path, @ph.xpath(__FILE__)
+  end
+
+  test '#normalize_extension returns a bare extension as is' do
+    assert_equal 'rb', @ph.normalize_extension('rb')
+  end
+
+  test '#normalize_extension removes a leading dot' do
+    assert_equal 'rb', @ph.normalize_extension('.rb')
+  end
+
+  test '#normalize_extension supports symbols' do
+    assert_equal 'rb', @ph.normalize_extension(:rb)
+  end
+
+  test '#longest_common_subpath finds the longest common subpath, if there is one' do
+    paths = %w(
+      /foo/bar
+      /foo/baz
+      /foo/bar/baz/woo/zoo
+    ).map {|path| pn(path)}
+
+    assert_equal pn('/foo'), @ph.longest_common_subpath(paths)
+  end
+
+  test '#longest_common_subpath returns the root directory as an edge case' do
+    paths = %w(
+      /foo/bar
+      /foo/baz
+      /foo/bar/baz/woo/zoo
+      /wadus
+    ).map {|path| pn(path)}
+
+    assert_equal pn('/'), @ph.longest_common_subpath(paths)
+  end
+
+  test '#longest_common_subpath returns nil for an empty collection' do
+    assert_nil @ph.longest_common_subpath([])
+  end
+
+  test '#existing_parent returns the most specific existing ascendant' do
+    wd = Pathname.getwd
+
+    assert_equal wd, @ph.existing_parent(wd)
+    assert_equal wd, @ph.existing_parent(wd.join('non-existing/directory'))
+    assert_equal pn('/'), @ph.existing_parent(pn('/non-existing/directory'))
+  end
+end
-- 
cgit v1.2.3


From 7ea3207f416b01e89fd4a8e61cb9a2257bb7e991 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 14:47:15 -0800
Subject: s/@modified/@updated/g

---
 .../lib/active_support/file_evented_update_checker.rb        | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index a4dd4ff30c..41ec12d829 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -13,9 +13,9 @@ module ActiveSupport
         @dirs[@ph.xpath(dir)] = Array(exts).map {|ext| @ph.normalize_extension(ext)}
       end
 
-      @block    = block
-      @modified = false
-      @lcsp     = @ph.longest_common_subpath(@dirs.keys)
+      @block   = block
+      @updated = false
+      @lcsp    = @ph.longest_common_subpath(@dirs.keys)
 
       if (watch_dirs = base_directories).any?
         Listen.to(*watch_dirs, &method(:changed)).start
@@ -23,13 +23,13 @@ module ActiveSupport
     end
 
     def updated?
-      @modified
+      @updated
     end
 
     def execute
       @block.call
     ensure
-      @modified = false
+      @updated = false
     end
 
     def execute_if_updated
@@ -43,7 +43,7 @@ module ActiveSupport
 
     def changed(modified, added, removed)
       unless updated?
-        @modified = (modified + added + removed).any? {|f| watching?(f)}
+        @updated = (modified + added + removed).any? {|f| watching?(f)}
       end
     end
 
-- 
cgit v1.2.3


From e8fbf68aa1c69c73595d32c6a6299fe4baa79108 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 18:14:08 -0800
Subject: let listen stop all listeners on teardown

---
 activesupport/test/file_evented_update_checker_test.rb | 5 +++++
 1 file changed, 5 insertions(+)

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index 727b99958e..a720b912d0 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -10,6 +10,11 @@ class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   def build_new_watcher(files, dirs={}, &block)
     ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block)
   end
+
+  def teardown
+    super
+    Listen.stop
+  end
 end
 
 class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
-- 
cgit v1.2.3


From 60de179c0970d9ca9b7f6f7f2d49d4c428b96afa Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 21:01:38 -0800
Subject: refactors the file monitors test suite

In particular files are no longer created in the current working directory,
but in a temporary folder.
---
 .../test/file_evented_update_checker_test.rb       |  8 +-
 activesupport/test/file_update_checker_test.rb     |  4 +-
 ...le_update_checker_with_enumerable_test_cases.rb | 92 ++++++++++------------
 3 files changed, 47 insertions(+), 57 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index a720b912d0..72cf9bd387 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -1,13 +1,11 @@
 require 'abstract_unit'
-require 'fileutils'
-require 'thread'
 require 'pathname'
 require 'file_update_checker_with_enumerable_test_cases'
 
 class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
 
-  def build_new_watcher(files, dirs={}, &block)
+  def build_new_watcher(files=[], dirs={}, &block)
     ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block)
   end
 
@@ -15,6 +13,10 @@ class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
     super
     Listen.stop
   end
+
+  def wait
+    sleep 0.5
+  end
 end
 
 class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index 313c818691..9c7198caa3 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -1,12 +1,10 @@
 require 'abstract_unit'
-require 'fileutils'
-require 'thread'
 require 'file_update_checker_with_enumerable_test_cases'
 
 class FileUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
 
-  def build_new_watcher(files, dirs={}, &block)
+  def build_new_watcher(files=[], dirs={}, &block)
     ActiveSupport::FileUpdateChecker.new(files, dirs, &block)
   end
 end
diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index c7b5ffb7a5..e9c54880ce 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -1,29 +1,36 @@
+require 'fileutils'
+
 module FileUpdateCheckerWithEnumerableTestCases
-  FILES = %w(1.txt 2.txt 3.txt)
+  include FileUtils
+
+  def wait
+    # noop
+  end
 
   def setup
-    FileUtils.mkdir_p('tmp_watcher')
-    FileUtils.touch(FILES)
+    @tmpdir = Dir.mktmpdir
+
+    @files = %w(foo.rb bar.rb baz.rb).map {|f| "#{@tmpdir}/#{f}"}
+    touch(@files)
   end
 
   def teardown
-    FileUtils.rm_rf('tmp_watcher')
-    FileUtils.rm_rf(FILES)
+    rm_rf(@tmpdir)
   end
 
   def test_should_not_execute_the_block_if_no_paths_are_given
     i = 0
 
-    checker = build_new_watcher([]) { i += 1 }
-    checker.execute_if_updated
+    checker = build_new_watcher { i += 1 }
 
+    assert !checker.execute_if_updated
     assert_equal 0, i
   end
 
   def test_should_not_invoke_the_block_if_no_file_has_changed
     i = 0
 
-    checker = build_new_watcher(FILES) { i += 1 }
+    checker = build_new_watcher(@files) { i += 1 }
 
     assert !checker.execute_if_updated
     assert_equal 0, i
@@ -32,22 +39,24 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_invoke_the_block_if_a_file_has_changed
     i = 0
 
-    checker = build_new_watcher(FILES) { i += 1 }
-    sleep 1
+    checker = build_new_watcher(@files) { i += 1 }
 
-    FileUtils.touch(FILES)
     sleep 1
+    touch(@files)
+    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
   def test_updated_should_become_true_when_watched_files_are_deleted
-    watcher = build_new_watcher(FILES) { i += 1 }
+    i = 0
+
+    watcher = build_new_watcher(@files) { i += 1 }
     assert !watcher.updated?
 
-    FileUtils.rm(FILES)
-    sleep 1
+    rm_f(@files)
+    wait
 
     assert watcher.updated?
   end
@@ -55,10 +64,10 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_be_robust_enough_to_handle_deleted_files
     i = 0
 
-    checker = build_new_watcher(FILES) { i += 1 }
-    FileUtils.rm_f(FILES)
+    checker = build_new_watcher(@files) { i += 1 }
 
-    sleep 1
+    rm_f(@files)
+    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -69,13 +78,13 @@ module FileUpdateCheckerWithEnumerableTestCases
 
     now = Time.now
     time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
-    File.utime(time, time, FILES[2])
+    File.utime(time, time, @files[0])
 
-    checker = build_new_watcher(FILES) { i += 1 }
-    sleep 1
+    checker = build_new_watcher(@files) { i += 1 }
 
-    FileUtils.touch(FILES[0..1])
     sleep 1
+    touch(@files[1..-1])
+    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -84,59 +93,40 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_cache_updated_result_until_execute
     i = 0
 
-    checker = build_new_watcher(FILES) { i += 1 }
+    checker = build_new_watcher(@files) { i += 1 }
     assert !checker.updated?
-    sleep 1
 
-    FileUtils.touch(FILES)
     sleep 1
+    touch(@files)
+    wait
 
     assert checker.updated?
     checker.execute
     assert !checker.updated?
   end
 
-  def test_should_invoke_the_block_if_a_watched_dir_changed_its_glob
+  def test_should_invoke_the_block_if_a_watched_dir_changes
     i = 0
 
-    checker = build_new_watcher([], 'tmp_watcher' => [:txt]) { i += 1 }
+    checker = build_new_watcher([], @tmpdir => :rb) { i += 1 }
 
-    FileUtils.cd 'tmp_watcher' do
-      FileUtils.touch(FILES)
-    end
     sleep 1
+    touch(@files)
+    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
-  def test_should_not_invoke_the_block_if_a_watched_dir_changed_its_glob
+  def test_should_not_invoke_the_block_if_a_watched_dir_does_not_change
     i = 0
 
-    checker = build_new_watcher([], 'tmp_watcher' => :rb) { i += 1 }
+    checker = build_new_watcher([], @tmpdir => :txt) { i += 1 }
 
-    FileUtils.cd 'tmp_watcher' do
-      FileUtils.touch(FILES)
-    end
-    sleep 1
+    touch(@files)
+    wait
 
     assert !checker.execute_if_updated
     assert_equal 0, i
   end
-
-  def test_should_not_block_with_unusual_file_names
-    unusual_dirname = 'tmp_watcher/valid,yetstrange,path,'
-    FileUtils.mkdir_p(unusual_dirname)
-    FileUtils.touch(FILES.map { |file_name| "#{unusual_dirname}/#{file_name}" })
-
-    test = Thread.new do
-      build_new_watcher([], unusual_dirname => :txt) { i += 1 }
-      Thread.exit
-    end
-
-    test.priority = -1
-    test.join(5)
-
-    assert !test.alive?
-  end
 end
-- 
cgit v1.2.3


From e97200405ec83ee9575cd01e53cd3c4872f05016 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 21:04:17 -0800
Subject: renames the monitor creation helper

---
 activesupport/test/file_evented_update_checker_test.rb |  2 +-
 activesupport/test/file_update_checker_test.rb         |  2 +-
 .../file_update_checker_with_enumerable_test_cases.rb  | 18 +++++++++---------
 3 files changed, 11 insertions(+), 11 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index 72cf9bd387..bfaee8fb55 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -5,7 +5,7 @@ require 'file_update_checker_with_enumerable_test_cases'
 class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
 
-  def build_new_watcher(files=[], dirs={}, &block)
+  def new_checker(files=[], dirs={}, &block)
     ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block)
   end
 
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index 9c7198caa3..de6b51823e 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -4,7 +4,7 @@ require 'file_update_checker_with_enumerable_test_cases'
 class FileUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
 
-  def build_new_watcher(files=[], dirs={}, &block)
+  def new_checker(files=[], dirs={}, &block)
     ActiveSupport::FileUpdateChecker.new(files, dirs, &block)
   end
 end
diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index e9c54880ce..fe19e617fa 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -21,7 +21,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_not_execute_the_block_if_no_paths_are_given
     i = 0
 
-    checker = build_new_watcher { i += 1 }
+    checker = new_checker { i += 1 }
 
     assert !checker.execute_if_updated
     assert_equal 0, i
@@ -30,7 +30,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_not_invoke_the_block_if_no_file_has_changed
     i = 0
 
-    checker = build_new_watcher(@files) { i += 1 }
+    checker = new_checker(@files) { i += 1 }
 
     assert !checker.execute_if_updated
     assert_equal 0, i
@@ -39,7 +39,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_invoke_the_block_if_a_file_has_changed
     i = 0
 
-    checker = build_new_watcher(@files) { i += 1 }
+    checker = new_checker(@files) { i += 1 }
 
     sleep 1
     touch(@files)
@@ -52,7 +52,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_updated_should_become_true_when_watched_files_are_deleted
     i = 0
 
-    watcher = build_new_watcher(@files) { i += 1 }
+    watcher = new_checker(@files) { i += 1 }
     assert !watcher.updated?
 
     rm_f(@files)
@@ -64,7 +64,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_be_robust_enough_to_handle_deleted_files
     i = 0
 
-    checker = build_new_watcher(@files) { i += 1 }
+    checker = new_checker(@files) { i += 1 }
 
     rm_f(@files)
     wait
@@ -80,7 +80,7 @@ module FileUpdateCheckerWithEnumerableTestCases
     time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
     File.utime(time, time, @files[0])
 
-    checker = build_new_watcher(@files) { i += 1 }
+    checker = new_checker(@files) { i += 1 }
 
     sleep 1
     touch(@files[1..-1])
@@ -93,7 +93,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_cache_updated_result_until_execute
     i = 0
 
-    checker = build_new_watcher(@files) { i += 1 }
+    checker = new_checker(@files) { i += 1 }
     assert !checker.updated?
 
     sleep 1
@@ -108,7 +108,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_invoke_the_block_if_a_watched_dir_changes
     i = 0
 
-    checker = build_new_watcher([], @tmpdir => :rb) { i += 1 }
+    checker = new_checker([], @tmpdir => :rb) { i += 1 }
 
     sleep 1
     touch(@files)
@@ -121,7 +121,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_not_invoke_the_block_if_a_watched_dir_does_not_change
     i = 0
 
-    checker = build_new_watcher([], @tmpdir => :txt) { i += 1 }
+    checker = new_checker([], @tmpdir => :txt) { i += 1 }
 
     touch(@files)
     wait
-- 
cgit v1.2.3


From dda54de48ccf72caa447b251986c65d032d81bba Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 21:10:47 -0800
Subject: encapsulate sleep margin when touching files

---
 .../test/file_update_checker_with_enumerable_test_cases.rb    | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index fe19e617fa..4ae6e8c5e2 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -7,11 +7,16 @@ module FileUpdateCheckerWithEnumerableTestCases
     # noop
   end
 
+  def touch(files)
+    sleep 1
+    super
+  end
+
   def setup
     @tmpdir = Dir.mktmpdir
 
     @files = %w(foo.rb bar.rb baz.rb).map {|f| "#{@tmpdir}/#{f}"}
-    touch(@files)
+    FileUtils.touch(@files)
   end
 
   def teardown
@@ -41,7 +46,6 @@ module FileUpdateCheckerWithEnumerableTestCases
 
     checker = new_checker(@files) { i += 1 }
 
-    sleep 1
     touch(@files)
     wait
 
@@ -82,7 +86,6 @@ module FileUpdateCheckerWithEnumerableTestCases
 
     checker = new_checker(@files) { i += 1 }
 
-    sleep 1
     touch(@files[1..-1])
     wait
 
@@ -96,7 +99,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     checker = new_checker(@files) { i += 1 }
     assert !checker.updated?
 
-    sleep 1
     touch(@files)
     wait
 
@@ -110,7 +112,6 @@ module FileUpdateCheckerWithEnumerableTestCases
 
     checker = new_checker([], @tmpdir => :rb) { i += 1 }
 
-    sleep 1
     touch(@files)
     wait
 
-- 
cgit v1.2.3


From e201b9d45514c310b8194b3f0d9b8d5d711a1216 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 22:12:32 -0800
Subject: create the tmpdir under test

Mac OS X tries by all means to hide that /var is /private/var, and that is
what FSEvents reports back.
---
 activesupport/test/file_update_checker_with_enumerable_test_cases.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index 4ae6e8c5e2..342bd9021a 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -13,7 +13,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   end
 
   def setup
-    @tmpdir = Dir.mktmpdir
+    @tmpdir = Dir.mktmpdir(nil, __dir__)
 
     @files = %w(foo.rb bar.rb baz.rb).map {|f| "#{@tmpdir}/#{f}"}
     FileUtils.touch(@files)
-- 
cgit v1.2.3


From 19e54baaad43251b7a2dfb1bb0f0111781e0f919 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 22:32:46 -0800
Subject: more ad-hoc sleeps

This sucks, but otherwise I get occasional Fs on Mac OS X.
---
 activesupport/test/file_evented_update_checker_test.rb | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index bfaee8fb55..85c7bf29d9 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -6,7 +6,9 @@ class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerWithEnumerableTestCases
 
   def new_checker(files=[], dirs={}, &block)
-    ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block)
+    ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block).tap do
+      wait
+    end
   end
 
   def teardown
@@ -15,7 +17,7 @@ class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   end
 
   def wait
-    sleep 0.5
+    sleep 1
   end
 end
 
-- 
cgit v1.2.3


From ab0c915ab54538f66406edc44070c1bdeacdd844 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Sun, 8 Nov 2015 22:55:29 -0800
Subject: s/watcher/checker/g

"checker" is the name being used everywhere.
---
 .../test/file_update_checker_with_enumerable_test_cases.rb          | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index 342bd9021a..1518562d7c 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -56,13 +56,13 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_updated_should_become_true_when_watched_files_are_deleted
     i = 0
 
-    watcher = new_checker(@files) { i += 1 }
-    assert !watcher.updated?
+    checker = new_checker(@files) { i += 1 }
+    assert !checker.updated?
 
     rm_f(@files)
     wait
 
-    assert watcher.updated?
+    assert checker.updated?
   end
 
   def test_should_be_robust_enough_to_handle_deleted_files
-- 
cgit v1.2.3


From 9a59beac6feced4e51fdddb5761208472be71719 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Mon, 9 Nov 2015 09:11:13 -0800
Subject: adds more tests for the file monitors

---
 ...le_update_checker_with_enumerable_test_cases.rb | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index 1518562d7c..250fd90dde 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -130,4 +130,64 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert !checker.execute_if_updated
     assert_equal 0, i
   end
+
+  def test_does_not_assume_files_exist_on_instantiation
+    i = 0
+
+    non_existing = "#{@tmpdir}/non_existing.rb"
+    checker = new_checker([non_existing]) { i += 1 }
+
+    touch(non_existing)
+    wait
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_detects_files_in_new_subdirectories
+    i = 0
+
+    checker = new_checker([], @tmpdir => :rb) { i += 1 }
+
+    subdir = "#{@tmpdir}/subdir"
+    mkdir(subdir)
+    wait
+
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+
+    touch("#{subdir}/nested.rb")
+    wait
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_looked_up_extensions_are_inherited_in_subdirectories_not_listening_to_them
+    i = 0
+
+    subdir = "#{@tmpdir}/subdir"
+    mkdir(subdir)
+
+    checker = new_checker([], @tmpdir => :rb, subdir => :txt) { i += 1 }
+
+    touch("#{@tmpdir}/new.txt")
+    wait
+
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+
+    # subdir does not look for Ruby files, but its parent @tmpdir does.
+    touch("#{subdir}/nested.rb")
+    wait
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+
+    touch("#{subdir}/nested.txt")
+    wait
+
+    assert checker.execute_if_updated
+    assert_equal 2, i
+  end
 end
-- 
cgit v1.2.3


From a22d431d4236b9bdb3f27d7e25118fe36aa08243 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Mon, 9 Nov 2015 09:24:41 -0800
Subject: improves waiting in the file monitors suite

---
 .../test/file_evented_update_checker_test.rb       | 10 +++++++++
 activesupport/test/file_update_checker_test.rb     |  9 ++++++++
 ...le_update_checker_with_enumerable_test_cases.rb | 25 ++--------------------
 3 files changed, 21 insertions(+), 23 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index 85c7bf29d9..93b62fe5b7 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -19,6 +19,16 @@ class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   def wait
     sleep 1
   end
+
+  def touch(files)
+    super
+    wait # wait for the events to fire
+  end
+
+  def rm_f(files)
+    super
+    wait
+  end
 end
 
 class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index de6b51823e..e3233e5721 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -7,4 +7,13 @@ class FileUpdateCheckerTest < ActiveSupport::TestCase
   def new_checker(files=[], dirs={}, &block)
     ActiveSupport::FileUpdateChecker.new(files, dirs, &block)
   end
+
+  def wait
+    # noop
+  end
+
+  def touch(files)
+    sleep 1 # let's wait a bit to ensure there's a new mtime
+    super
+  end
 end
diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index 250fd90dde..4885a14f81 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -3,15 +3,6 @@ require 'fileutils'
 module FileUpdateCheckerWithEnumerableTestCases
   include FileUtils
 
-  def wait
-    # noop
-  end
-
-  def touch(files)
-    sleep 1
-    super
-  end
-
   def setup
     @tmpdir = Dir.mktmpdir(nil, __dir__)
 
@@ -20,7 +11,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   end
 
   def teardown
-    rm_rf(@tmpdir)
+    FileUtils.rm_rf(@tmpdir)
   end
 
   def test_should_not_execute_the_block_if_no_paths_are_given
@@ -47,7 +38,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     checker = new_checker(@files) { i += 1 }
 
     touch(@files)
-    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -60,18 +50,16 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert !checker.updated?
 
     rm_f(@files)
-    wait
 
     assert checker.updated?
   end
 
-  def test_should_be_robust_enough_to_handle_deleted_files
+  def test_should_detect_deleted_files
     i = 0
 
     checker = new_checker(@files) { i += 1 }
 
     rm_f(@files)
-    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -87,7 +75,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     checker = new_checker(@files) { i += 1 }
 
     touch(@files[1..-1])
-    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -100,7 +87,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert !checker.updated?
 
     touch(@files)
-    wait
 
     assert checker.updated?
     checker.execute
@@ -113,7 +99,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     checker = new_checker([], @tmpdir => :rb) { i += 1 }
 
     touch(@files)
-    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -125,7 +110,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     checker = new_checker([], @tmpdir => :txt) { i += 1 }
 
     touch(@files)
-    wait
 
     assert !checker.execute_if_updated
     assert_equal 0, i
@@ -138,7 +122,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     checker = new_checker([non_existing]) { i += 1 }
 
     touch(non_existing)
-    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -157,7 +140,6 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert_equal 0, i
 
     touch("#{subdir}/nested.rb")
-    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -172,20 +154,17 @@ module FileUpdateCheckerWithEnumerableTestCases
     checker = new_checker([], @tmpdir => :rb, subdir => :txt) { i += 1 }
 
     touch("#{@tmpdir}/new.txt")
-    wait
 
     assert !checker.execute_if_updated
     assert_equal 0, i
 
     # subdir does not look for Ruby files, but its parent @tmpdir does.
     touch("#{subdir}/nested.rb")
-    wait
 
     assert checker.execute_if_updated
     assert_equal 1, i
 
     touch("#{subdir}/nested.txt")
-    wait
 
     assert checker.execute_if_updated
     assert_equal 2, i
-- 
cgit v1.2.3


From 3cd4775211bba820cdb5803ef0200040a2254f84 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Mon, 9 Nov 2015 09:39:07 -0800
Subject: editorial pass over test method names

---
 .../test/file_update_checker_with_enumerable_test_cases.rb     | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index 4885a14f81..0aa1aaa727 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -23,7 +23,7 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert_equal 0, i
   end
 
-  def test_should_not_invoke_the_block_if_no_file_has_changed
+  def test_should_not_execute_the_block_if_no_files_change
     i = 0
 
     checker = new_checker(@files) { i += 1 }
@@ -32,7 +32,7 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert_equal 0, i
   end
 
-  def test_should_invoke_the_block_if_a_file_has_changed
+  def test_should_execute_the_block_once_when_files_change
     i = 0
 
     checker = new_checker(@files) { i += 1 }
@@ -54,7 +54,7 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert checker.updated?
   end
 
-  def test_should_detect_deleted_files
+  def test_should_execute_the_block_once_when_files_are_deleted
     i = 0
 
     checker = new_checker(@files) { i += 1 }
@@ -93,7 +93,7 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert !checker.updated?
   end
 
-  def test_should_invoke_the_block_if_a_watched_dir_changes
+  def test_should_execute_the_block_if_files_change_in_a_watched_directory
     i = 0
 
     checker = new_checker([], @tmpdir => :rb) { i += 1 }
@@ -104,7 +104,7 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert_equal 1, i
   end
 
-  def test_should_not_invoke_the_block_if_a_watched_dir_does_not_change
+  def test_should_not_execute_the_block_if_the_file_extension_is_not_watched
     i = 0
 
     checker = new_checker([], @tmpdir => :txt) { i += 1 }
-- 
cgit v1.2.3


From 9fa366a318625ee54bd96052b1c9d79dbcf959c7 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Tue, 10 Nov 2015 00:59:46 -0800
Subject: adds another test case in the monitors suite

---
 .../file_update_checker_with_enumerable_test_cases.rb  | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index 0aa1aaa727..e00ba893da 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -93,7 +93,7 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert !checker.updated?
   end
 
-  def test_should_execute_the_block_if_files_change_in_a_watched_directory
+  def test_should_execute_the_block_if_files_change_in_a_watched_directory_one_extension
     i = 0
 
     checker = new_checker([], @tmpdir => :rb) { i += 1 }
@@ -104,6 +104,22 @@ module FileUpdateCheckerWithEnumerableTestCases
     assert_equal 1, i
   end
 
+  def test_should_execute_the_block_if_files_change_in_a_watched_directory_several_extensions
+    i = 0
+
+    checker = new_checker([], @tmpdir => [:rb, :txt]) { i += 1 }
+
+    touch("#{@tmpdir}/foo.rb")
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+
+    touch("#{@tmpdir}/foo.rb")
+
+    assert checker.execute_if_updated
+    assert_equal 2, i
+  end
+
   def test_should_not_execute_the_block_if_the_file_extension_is_not_watched
     i = 0
 
-- 
cgit v1.2.3


From 8a64824306fd04895f4f6f14e6edb7a82bfe2503 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Tue, 10 Nov 2015 01:12:51 -0800
Subject: better tmp file management in the monitors suite

---
 ...le_update_checker_with_enumerable_test_cases.rb | 124 +++++++++++++++------
 1 file changed, 88 insertions(+), 36 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
index e00ba893da..23d8ffa5de 100644
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
@@ -3,15 +3,20 @@ require 'fileutils'
 module FileUpdateCheckerWithEnumerableTestCases
   include FileUtils
 
-  def setup
-    @tmpdir = Dir.mktmpdir(nil, __dir__)
+  def tmpdir
+    @tmpdir ||= Dir.mktmpdir(nil, __dir__)
+  end
+
+  def tmpfile(name)
+    "#{tmpdir}/#{name}"
+  end
 
-    @files = %w(foo.rb bar.rb baz.rb).map {|f| "#{@tmpdir}/#{f}"}
-    FileUtils.touch(@files)
+  def tmpfiles
+    @tmpfiles ||= %w(foo.rb bar.rb baz.rb).map {|f| tmpfile(f)}
   end
 
   def teardown
-    FileUtils.rm_rf(@tmpdir)
+    FileUtils.rm_rf(@tmpdir) if @tmpdir
   end
 
   def test_should_not_execute_the_block_if_no_paths_are_given
@@ -26,55 +31,102 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_not_execute_the_block_if_no_files_change
     i = 0
 
-    checker = new_checker(@files) { i += 1 }
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
 
     assert !checker.execute_if_updated
     assert_equal 0, i
   end
 
-  def test_should_execute_the_block_once_when_files_change
+  def test_should_execute_the_block_once_when_files_are_created
     i = 0
 
-    checker = new_checker(@files) { i += 1 }
+    checker = new_checker(tmpfiles) { i += 1 }
 
-    touch(@files)
+    touch(tmpfiles)
 
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
-  def test_updated_should_become_true_when_watched_files_are_deleted
+  def test_should_execute_the_block_once_when_files_are_modified
     i = 0
 
-    checker = new_checker(@files) { i += 1 }
-    assert !checker.updated?
+    FileUtils.touch(tmpfiles)
 
-    rm_f(@files)
+    checker = new_checker(tmpfiles) { i += 1 }
 
-    assert checker.updated?
+    touch(tmpfiles)
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
   end
 
   def test_should_execute_the_block_once_when_files_are_deleted
     i = 0
 
-    checker = new_checker(@files) { i += 1 }
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
 
-    rm_f(@files)
+    rm_f(tmpfiles)
 
     assert checker.execute_if_updated
     assert_equal 1, i
   end
 
+
+  def test_updated_should_become_true_when_watched_files_are_created
+    i = 0
+
+    checker = new_checker(tmpfiles) { i += 1 }
+    assert !checker.updated?
+
+    touch(tmpfiles)
+
+    assert checker.updated?
+  end
+
+
+  def test_updated_should_become_true_when_watched_files_are_modified
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
+    assert !checker.updated?
+
+    touch(tmpfiles)
+
+    assert checker.updated?
+  end
+
+  def test_updated_should_become_true_when_watched_files_are_deleted
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
+    assert !checker.updated?
+
+    rm_f(tmpfiles)
+
+    assert checker.updated?
+  end
+
   def test_should_be_robust_to_handle_files_with_wrong_modified_time
     i = 0
 
-    now = Time.now
+    FileUtils.touch(tmpfiles)
+
+    now  = Time.now
     time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
-    File.utime(time, time, @files[0])
+    File.utime(time, time, tmpfiles[0])
 
-    checker = new_checker(@files) { i += 1 }
+    checker = new_checker(tmpfiles) { i += 1 }
 
-    touch(@files[1..-1])
+    touch(tmpfiles[1..-1])
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -83,10 +135,10 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_cache_updated_result_until_execute
     i = 0
 
-    checker = new_checker(@files) { i += 1 }
+    checker = new_checker(tmpfiles) { i += 1 }
     assert !checker.updated?
 
-    touch(@files)
+    touch(tmpfiles)
 
     assert checker.updated?
     checker.execute
@@ -96,9 +148,9 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_execute_the_block_if_files_change_in_a_watched_directory_one_extension
     i = 0
 
-    checker = new_checker([], @tmpdir => :rb) { i += 1 }
+    checker = new_checker([], tmpdir => :rb) { i += 1 }
 
-    touch(@files)
+    touch(tmpfile('foo.rb'))
 
     assert checker.execute_if_updated
     assert_equal 1, i
@@ -107,14 +159,14 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_execute_the_block_if_files_change_in_a_watched_directory_several_extensions
     i = 0
 
-    checker = new_checker([], @tmpdir => [:rb, :txt]) { i += 1 }
+    checker = new_checker([], tmpdir => [:rb, :txt]) { i += 1 }
 
-    touch("#{@tmpdir}/foo.rb")
+    touch(tmpfile('foo.rb'))
 
     assert checker.execute_if_updated
     assert_equal 1, i
 
-    touch("#{@tmpdir}/foo.rb")
+    touch(tmpfile('foo.txt'))
 
     assert checker.execute_if_updated
     assert_equal 2, i
@@ -123,9 +175,9 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_should_not_execute_the_block_if_the_file_extension_is_not_watched
     i = 0
 
-    checker = new_checker([], @tmpdir => :txt) { i += 1 }
+    checker = new_checker([], tmpdir => :txt) { i += 1 }
 
-    touch(@files)
+    touch(tmpfile('foo.rb'))
 
     assert !checker.execute_if_updated
     assert_equal 0, i
@@ -134,7 +186,7 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_does_not_assume_files_exist_on_instantiation
     i = 0
 
-    non_existing = "#{@tmpdir}/non_existing.rb"
+    non_existing = tmpfile('non_existing.rb')
     checker = new_checker([non_existing]) { i += 1 }
 
     touch(non_existing)
@@ -146,9 +198,9 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_detects_files_in_new_subdirectories
     i = 0
 
-    checker = new_checker([], @tmpdir => :rb) { i += 1 }
+    checker = new_checker([], tmpdir => :rb) { i += 1 }
 
-    subdir = "#{@tmpdir}/subdir"
+    subdir = tmpfile('subdir')
     mkdir(subdir)
     wait
 
@@ -164,17 +216,17 @@ module FileUpdateCheckerWithEnumerableTestCases
   def test_looked_up_extensions_are_inherited_in_subdirectories_not_listening_to_them
     i = 0
 
-    subdir = "#{@tmpdir}/subdir"
+    subdir = tmpfile('subdir')
     mkdir(subdir)
 
-    checker = new_checker([], @tmpdir => :rb, subdir => :txt) { i += 1 }
+    checker = new_checker([], tmpdir => :rb, subdir => :txt) { i += 1 }
 
-    touch("#{@tmpdir}/new.txt")
+    touch(tmpfile('new.txt'))
 
     assert !checker.execute_if_updated
     assert_equal 0, i
 
-    # subdir does not look for Ruby files, but its parent @tmpdir does.
+    # subdir does not look for Ruby files, but its parent tmpdir does.
     touch("#{subdir}/nested.rb")
 
     assert checker.execute_if_updated
-- 
cgit v1.2.3


From eda503c836c6cd02937e111b175979c5722677fd Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Tue, 10 Nov 2015 02:28:14 -0800
Subject: the evented monitor filters out descendants

---
 .../active_support/file_evented_update_checker.rb  | 46 +++++++++++++++++-----
 .../test/file_evented_update_checker_test.rb       | 30 ++++++++++++++
 2 files changed, 66 insertions(+), 10 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 41ec12d829..262e801ce3 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -17,8 +17,8 @@ module ActiveSupport
       @updated = false
       @lcsp    = @ph.longest_common_subpath(@dirs.keys)
 
-      if (watch_dirs = base_directories).any?
-        Listen.to(*watch_dirs, &method(:changed)).start
+      if (dtw = directories_to_watch).any?
+        Listen.to(*dtw, &method(:changed)).start
       end
     end
 
@@ -71,14 +71,15 @@ module ActiveSupport
       end
     end
 
-    # TODO: Better return a list of non-nested directories.
-    def base_directories
-      [].tap do |bd|
-        bd.concat @files.map {|f| @ph.existing_parent(f.dirname)}
-        bd.concat @dirs.keys.map {|dir| @ph.existing_parent(dir)}
-        bd.compact!
-        bd.uniq!
-      end
+    def directories_to_watch
+      bd = []
+
+      bd.concat @files.map {|f| @ph.existing_parent(f.dirname)}
+      bd.concat @dirs.keys.map {|dir| @ph.existing_parent(dir)}
+      bd.compact!
+      bd.uniq!
+
+      @ph.filter_out_descendants(bd)
     end
 
     class PathHelper
@@ -133,6 +134,31 @@ module ActiveSupport
           end
         end
       end
+
+      # Filters out directories which are descendants of others in the collection (stable).
+      def filter_out_descendants(directories)
+        return directories if directories.length < 2
+
+        sorted      = directories.sort_by {|dir| dir.each_filename.to_a.length}
+        descendants = []
+
+        until sorted.empty?
+          directory = sorted.shift
+
+          sorted.each do |candidate_to_descendant|
+            if candidate_to_descendant.to_path.start_with?(directory.to_path)
+              dparts = directory.each_filename.to_a
+              cparts = candidate_to_descendant.each_filename.to_a
+
+              if cparts[0, dparts.length] == dparts
+                descendants << candidate_to_descendant
+              end
+            end
+          end
+        end
+
+        directories - descendants
+      end
     end
   end
 end
diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index 93b62fe5b7..5aba9a3e0b 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -88,4 +88,34 @@ class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
     assert_equal wd, @ph.existing_parent(wd.join('non-existing/directory'))
     assert_equal pn('/'), @ph.existing_parent(pn('/non-existing/directory'))
   end
+
+  test '#filter_out_descendants returns the same collection if there are no descendants (empty)' do
+    assert_equal [], @ph.filter_out_descendants([])
+  end
+
+  test '#filter_out_descendants returns the same collection if there are no descendants (one)' do
+    assert_equal ['/foo'], @ph.filter_out_descendants(['/foo'])
+  end
+
+  test '#filter_out_descendants returns the same collection if there are no descendants (several)' do
+    paths = %w(
+      /Rails.root/app/controllers
+      /Rails.root/app/models
+      /Rails.root/app/helpers
+    ).map {|path| pn(path)}
+
+    assert_equal paths, @ph.filter_out_descendants(paths)
+  end
+
+  test '#filter_out_descendants filters out descendants preserving order' do
+    paths = %w(
+      /Rails.root/app/controllers
+      /Rails.root/app/controllers/concerns
+      /Rails.root/app/models
+      /Rails.root/app/models/concerns
+      /Rails.root/app/helpers
+    ).map {|path| pn(path)}
+
+    assert_equal paths.values_at(0, 2, 4), @ph.filter_out_descendants(paths)
+  end
 end
-- 
cgit v1.2.3


From 8793f77c4a96197bb0f5e29b00828bf5903fcad7 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Tue, 10 Nov 2015 07:15:30 -0800
Subject: simplifies PathHelper with a Pathname refinement

---
 .../active_support/file_evented_update_checker.rb  | 44 +++++++++++-----------
 1 file changed, 22 insertions(+), 22 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 262e801ce3..27da2e1572 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -83,6 +83,14 @@ module ActiveSupport
     end
 
     class PathHelper
+      using Module.new {
+        refine Pathname do
+          def ascendant_of?(other)
+            other.to_s =~ /\A#{Regexp.quote(to_s)}#{Pathname::SEPARATOR_PAT}?/
+          end
+        end
+      }
+
       def xpath(path)
         Pathname.new(path).expand_path
       end
@@ -96,25 +104,24 @@ module ActiveSupport
       def longest_common_subpath(paths)
         return if paths.empty?
 
-        csp = Pathname.new(paths[0])
+        lcsp = Pathname.new(paths[0])
 
         paths[1..-1].each do |path|
           loop do
-            break if path.ascend do |ascendant|
-              break true if ascendant == csp
-            end
+            break if lcsp.ascendant_of?(path)
 
-            if csp.root?
-              # A root directory is not an ascendant of path. This may happen
-              # if there are paths in different drives on Windows.
+            if lcsp.root?
+              # If we get here a root directory is not an ascendant of path.
+              # This may happen if there are paths in different drives on
+              # Windows.
               return
             else
-              csp = csp.parent
+              lcsp = lcsp.parent
             end
           end
         end
 
-        csp
+        lcsp
       end
 
       # Returns the deepest existing ascendant, which could be the argument itself.
@@ -139,22 +146,15 @@ module ActiveSupport
       def filter_out_descendants(directories)
         return directories if directories.length < 2
 
-        sorted      = directories.sort_by {|dir| dir.each_filename.to_a.length}
+        sorted_by_nparts = directories.sort_by {|dir| dir.each_filename.to_a.length}
         descendants = []
 
-        until sorted.empty?
-          directory = sorted.shift
+        until sorted_by_nparts.empty?
+          dir = sorted_by_nparts.shift
 
-          sorted.each do |candidate_to_descendant|
-            if candidate_to_descendant.to_path.start_with?(directory.to_path)
-              dparts = directory.each_filename.to_a
-              cparts = candidate_to_descendant.each_filename.to_a
-
-              if cparts[0, dparts.length] == dparts
-                descendants << candidate_to_descendant
-              end
-            end
-          end
+          descendants.concat sorted_by_nparts.select { |possible_descendant|
+            dir.ascendant_of?(possible_descendant)
+          }
         end
 
         directories - descendants
-- 
cgit v1.2.3


From b7c8ce5bc2dbe12f1c21ebd8fd55953d891e98e3 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Tue, 10 Nov 2015 07:21:33 -0800
Subject: renames the module with shared tests for file monitors

---
 .../test/file_evented_update_checker_test.rb       |   4 +-
 .../test/file_update_checker_shared_tests.rb       | 240 +++++++++++++++++++++
 activesupport/test/file_update_checker_test.rb     |   4 +-
 ...le_update_checker_with_enumerable_test_cases.rb | 240 ---------------------
 4 files changed, 244 insertions(+), 244 deletions(-)
 create mode 100644 activesupport/test/file_update_checker_shared_tests.rb
 delete mode 100644 activesupport/test/file_update_checker_with_enumerable_test_cases.rb

(limited to 'activesupport')

diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index 5aba9a3e0b..f98b656d10 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -1,9 +1,9 @@
 require 'abstract_unit'
 require 'pathname'
-require 'file_update_checker_with_enumerable_test_cases'
+require 'file_update_checker_shared_tests'
 
 class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
-  include FileUpdateCheckerWithEnumerableTestCases
+  include FileUpdateCheckerSharedTests
 
   def new_checker(files=[], dirs={}, &block)
     ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block).tap do
diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb
new file mode 100644
index 0000000000..caa4d6191d
--- /dev/null
+++ b/activesupport/test/file_update_checker_shared_tests.rb
@@ -0,0 +1,240 @@
+require 'fileutils'
+
+module FileUpdateCheckerSharedTests
+  include FileUtils
+
+  def tmpdir
+    @tmpdir ||= Dir.mktmpdir(nil, __dir__)
+  end
+
+  def tmpfile(name)
+    "#{tmpdir}/#{name}"
+  end
+
+  def tmpfiles
+    @tmpfiles ||= %w(foo.rb bar.rb baz.rb).map {|f| tmpfile(f)}
+  end
+
+  def teardown
+    FileUtils.rm_rf(@tmpdir) if @tmpdir
+  end
+
+  def test_should_not_execute_the_block_if_no_paths_are_given
+    i = 0
+
+    checker = new_checker { i += 1 }
+
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+  end
+
+  def test_should_not_execute_the_block_if_no_files_change
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
+
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+  end
+
+  def test_should_execute_the_block_once_when_files_are_created
+    i = 0
+
+    checker = new_checker(tmpfiles) { i += 1 }
+
+    touch(tmpfiles)
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_execute_the_block_once_when_files_are_modified
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
+
+    touch(tmpfiles)
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_execute_the_block_once_when_files_are_deleted
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
+
+    rm_f(tmpfiles)
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+
+  def test_updated_should_become_true_when_watched_files_are_created
+    i = 0
+
+    checker = new_checker(tmpfiles) { i += 1 }
+    assert !checker.updated?
+
+    touch(tmpfiles)
+
+    assert checker.updated?
+  end
+
+
+  def test_updated_should_become_true_when_watched_files_are_modified
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
+    assert !checker.updated?
+
+    touch(tmpfiles)
+
+    assert checker.updated?
+  end
+
+  def test_updated_should_become_true_when_watched_files_are_deleted
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    checker = new_checker(tmpfiles) { i += 1 }
+    assert !checker.updated?
+
+    rm_f(tmpfiles)
+
+    assert checker.updated?
+  end
+
+  def test_should_be_robust_to_handle_files_with_wrong_modified_time
+    i = 0
+
+    FileUtils.touch(tmpfiles)
+
+    now  = Time.now
+    time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
+    File.utime(time, time, tmpfiles[0])
+
+    checker = new_checker(tmpfiles) { i += 1 }
+
+    touch(tmpfiles[1..-1])
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_cache_updated_result_until_execute
+    i = 0
+
+    checker = new_checker(tmpfiles) { i += 1 }
+    assert !checker.updated?
+
+    touch(tmpfiles)
+
+    assert checker.updated?
+    checker.execute
+    assert !checker.updated?
+  end
+
+  def test_should_execute_the_block_if_files_change_in_a_watched_directory_one_extension
+    i = 0
+
+    checker = new_checker([], tmpdir => :rb) { i += 1 }
+
+    touch(tmpfile('foo.rb'))
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_should_execute_the_block_if_files_change_in_a_watched_directory_several_extensions
+    i = 0
+
+    checker = new_checker([], tmpdir => [:rb, :txt]) { i += 1 }
+
+    touch(tmpfile('foo.rb'))
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+
+    touch(tmpfile('foo.txt'))
+
+    assert checker.execute_if_updated
+    assert_equal 2, i
+  end
+
+  def test_should_not_execute_the_block_if_the_file_extension_is_not_watched
+    i = 0
+
+    checker = new_checker([], tmpdir => :txt) { i += 1 }
+
+    touch(tmpfile('foo.rb'))
+
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+  end
+
+  def test_does_not_assume_files_exist_on_instantiation
+    i = 0
+
+    non_existing = tmpfile('non_existing.rb')
+    checker = new_checker([non_existing]) { i += 1 }
+
+    touch(non_existing)
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_detects_files_in_new_subdirectories
+    i = 0
+
+    checker = new_checker([], tmpdir => :rb) { i += 1 }
+
+    subdir = tmpfile('subdir')
+    mkdir(subdir)
+    wait
+
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+
+    touch("#{subdir}/nested.rb")
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+  end
+
+  def test_looked_up_extensions_are_inherited_in_subdirectories_not_listening_to_them
+    i = 0
+
+    subdir = tmpfile('subdir')
+    mkdir(subdir)
+
+    checker = new_checker([], tmpdir => :rb, subdir => :txt) { i += 1 }
+
+    touch(tmpfile('new.txt'))
+
+    assert !checker.execute_if_updated
+    assert_equal 0, i
+
+    # subdir does not look for Ruby files, but its parent tmpdir does.
+    touch("#{subdir}/nested.rb")
+
+    assert checker.execute_if_updated
+    assert_equal 1, i
+
+    touch("#{subdir}/nested.txt")
+
+    assert checker.execute_if_updated
+    assert_equal 2, i
+  end
+end
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index e3233e5721..bf376f75bc 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -1,8 +1,8 @@
 require 'abstract_unit'
-require 'file_update_checker_with_enumerable_test_cases'
+require 'file_update_checker_shared_tests'
 
 class FileUpdateCheckerTest < ActiveSupport::TestCase
-  include FileUpdateCheckerWithEnumerableTestCases
+  include FileUpdateCheckerSharedTests
 
   def new_checker(files=[], dirs={}, &block)
     ActiveSupport::FileUpdateChecker.new(files, dirs, &block)
diff --git a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb b/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
deleted file mode 100644
index 23d8ffa5de..0000000000
--- a/activesupport/test/file_update_checker_with_enumerable_test_cases.rb
+++ /dev/null
@@ -1,240 +0,0 @@
-require 'fileutils'
-
-module FileUpdateCheckerWithEnumerableTestCases
-  include FileUtils
-
-  def tmpdir
-    @tmpdir ||= Dir.mktmpdir(nil, __dir__)
-  end
-
-  def tmpfile(name)
-    "#{tmpdir}/#{name}"
-  end
-
-  def tmpfiles
-    @tmpfiles ||= %w(foo.rb bar.rb baz.rb).map {|f| tmpfile(f)}
-  end
-
-  def teardown
-    FileUtils.rm_rf(@tmpdir) if @tmpdir
-  end
-
-  def test_should_not_execute_the_block_if_no_paths_are_given
-    i = 0
-
-    checker = new_checker { i += 1 }
-
-    assert !checker.execute_if_updated
-    assert_equal 0, i
-  end
-
-  def test_should_not_execute_the_block_if_no_files_change
-    i = 0
-
-    FileUtils.touch(tmpfiles)
-
-    checker = new_checker(tmpfiles) { i += 1 }
-
-    assert !checker.execute_if_updated
-    assert_equal 0, i
-  end
-
-  def test_should_execute_the_block_once_when_files_are_created
-    i = 0
-
-    checker = new_checker(tmpfiles) { i += 1 }
-
-    touch(tmpfiles)
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_execute_the_block_once_when_files_are_modified
-    i = 0
-
-    FileUtils.touch(tmpfiles)
-
-    checker = new_checker(tmpfiles) { i += 1 }
-
-    touch(tmpfiles)
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_execute_the_block_once_when_files_are_deleted
-    i = 0
-
-    FileUtils.touch(tmpfiles)
-
-    checker = new_checker(tmpfiles) { i += 1 }
-
-    rm_f(tmpfiles)
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-
-  def test_updated_should_become_true_when_watched_files_are_created
-    i = 0
-
-    checker = new_checker(tmpfiles) { i += 1 }
-    assert !checker.updated?
-
-    touch(tmpfiles)
-
-    assert checker.updated?
-  end
-
-
-  def test_updated_should_become_true_when_watched_files_are_modified
-    i = 0
-
-    FileUtils.touch(tmpfiles)
-
-    checker = new_checker(tmpfiles) { i += 1 }
-    assert !checker.updated?
-
-    touch(tmpfiles)
-
-    assert checker.updated?
-  end
-
-  def test_updated_should_become_true_when_watched_files_are_deleted
-    i = 0
-
-    FileUtils.touch(tmpfiles)
-
-    checker = new_checker(tmpfiles) { i += 1 }
-    assert !checker.updated?
-
-    rm_f(tmpfiles)
-
-    assert checker.updated?
-  end
-
-  def test_should_be_robust_to_handle_files_with_wrong_modified_time
-    i = 0
-
-    FileUtils.touch(tmpfiles)
-
-    now  = Time.now
-    time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
-    File.utime(time, time, tmpfiles[0])
-
-    checker = new_checker(tmpfiles) { i += 1 }
-
-    touch(tmpfiles[1..-1])
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_cache_updated_result_until_execute
-    i = 0
-
-    checker = new_checker(tmpfiles) { i += 1 }
-    assert !checker.updated?
-
-    touch(tmpfiles)
-
-    assert checker.updated?
-    checker.execute
-    assert !checker.updated?
-  end
-
-  def test_should_execute_the_block_if_files_change_in_a_watched_directory_one_extension
-    i = 0
-
-    checker = new_checker([], tmpdir => :rb) { i += 1 }
-
-    touch(tmpfile('foo.rb'))
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_should_execute_the_block_if_files_change_in_a_watched_directory_several_extensions
-    i = 0
-
-    checker = new_checker([], tmpdir => [:rb, :txt]) { i += 1 }
-
-    touch(tmpfile('foo.rb'))
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-
-    touch(tmpfile('foo.txt'))
-
-    assert checker.execute_if_updated
-    assert_equal 2, i
-  end
-
-  def test_should_not_execute_the_block_if_the_file_extension_is_not_watched
-    i = 0
-
-    checker = new_checker([], tmpdir => :txt) { i += 1 }
-
-    touch(tmpfile('foo.rb'))
-
-    assert !checker.execute_if_updated
-    assert_equal 0, i
-  end
-
-  def test_does_not_assume_files_exist_on_instantiation
-    i = 0
-
-    non_existing = tmpfile('non_existing.rb')
-    checker = new_checker([non_existing]) { i += 1 }
-
-    touch(non_existing)
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_detects_files_in_new_subdirectories
-    i = 0
-
-    checker = new_checker([], tmpdir => :rb) { i += 1 }
-
-    subdir = tmpfile('subdir')
-    mkdir(subdir)
-    wait
-
-    assert !checker.execute_if_updated
-    assert_equal 0, i
-
-    touch("#{subdir}/nested.rb")
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-  end
-
-  def test_looked_up_extensions_are_inherited_in_subdirectories_not_listening_to_them
-    i = 0
-
-    subdir = tmpfile('subdir')
-    mkdir(subdir)
-
-    checker = new_checker([], tmpdir => :rb, subdir => :txt) { i += 1 }
-
-    touch(tmpfile('new.txt'))
-
-    assert !checker.execute_if_updated
-    assert_equal 0, i
-
-    # subdir does not look for Ruby files, but its parent tmpdir does.
-    touch("#{subdir}/nested.rb")
-
-    assert checker.execute_if_updated
-    assert_equal 1, i
-
-    touch("#{subdir}/nested.txt")
-
-    assert checker.execute_if_updated
-    assert_equal 2, i
-  end
-end
-- 
cgit v1.2.3


From 4e1cff0e20c473edcd5cbb3ca344284bb5f428c0 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Tue, 10 Nov 2015 07:32:08 -0800
Subject: indents private methods as per our guidelines

---
 .../active_support/file_evented_update_checker.rb  | 56 +++++++++++-----------
 1 file changed, 28 insertions(+), 28 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 27da2e1572..8f734ac4e4 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -41,46 +41,46 @@ module ActiveSupport
 
     private
 
-    def changed(modified, added, removed)
-      unless updated?
-        @updated = (modified + added + removed).any? {|f| watching?(f)}
+      def changed(modified, added, removed)
+        unless updated?
+          @updated = (modified + added + removed).any? {|f| watching?(f)}
+        end
       end
-    end
 
-    def watching?(file)
-      file = @ph.xpath(file)
+      def watching?(file)
+        file = @ph.xpath(file)
 
-      return true  if @files.member?(file)
-      return false if file.directory?
+        return true  if @files.member?(file)
+        return false if file.directory?
 
-      ext = @ph.normalize_extension(file.extname)
-      dir = file.dirname
+        ext = @ph.normalize_extension(file.extname)
+        dir = file.dirname
 
-      loop do
-        if @dirs.fetch(dir, []).include?(ext)
-          break true
-        else
-          if @lcsp
-            break false if dir == @lcsp
+        loop do
+          if @dirs.fetch(dir, []).include?(ext)
+            break true
           else
-            break false if dir.root?
-          end
+            if @lcsp
+              break false if dir == @lcsp
+            else
+              break false if dir.root?
+            end
 
-          dir = dir.parent
+            dir = dir.parent
+          end
         end
       end
-    end
 
-    def directories_to_watch
-      bd = []
+      def directories_to_watch
+        bd = []
 
-      bd.concat @files.map {|f| @ph.existing_parent(f.dirname)}
-      bd.concat @dirs.keys.map {|dir| @ph.existing_parent(dir)}
-      bd.compact!
-      bd.uniq!
+        bd.concat @files.map {|f| @ph.existing_parent(f.dirname)}
+        bd.concat @dirs.keys.map {|dir| @ph.existing_parent(dir)}
+        bd.compact!
+        bd.uniq!
 
-      @ph.filter_out_descendants(bd)
-    end
+        @ph.filter_out_descendants(bd)
+      end
 
     class PathHelper
       using Module.new {
-- 
cgit v1.2.3


From fe918b3bc4554693e16a536c91480db92ea35440 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Tue, 10 Nov 2015 07:38:38 -0800
Subject: simplifies the implementation of existing parent

---
 .../lib/active_support/file_evented_update_checker.rb     | 15 ++-------------
 1 file changed, 2 insertions(+), 13 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 8f734ac4e4..51c8cf5690 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -126,19 +126,8 @@ module ActiveSupport
 
       # Returns the deepest existing ascendant, which could be the argument itself.
       def existing_parent(dir)
-        loop do
-          if dir.directory?
-            break dir
-          else
-            if dir.root?
-              # Edge case in which not even the root exists. For example, Windows
-              # paths could have a non-existing drive letter. Since the parent of
-              # root is root, we need to break to prevent an infinite loop.
-              break
-            else
-              dir = dir.parent
-            end
-          end
+        dir.ascend do |ascendant|
+          break ascendant if ascendant.directory?
         end
       end
 
-- 
cgit v1.2.3


From 0a2e9b02d60ef3bf1ec1dc9d032d25aefc81987e Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 11 Nov 2015 05:23:29 +0100
Subject: rewrites bare loop as until

---
 activesupport/lib/active_support/file_evented_update_checker.rb | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 51c8cf5690..d63c8e2438 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -107,9 +107,7 @@ module ActiveSupport
         lcsp = Pathname.new(paths[0])
 
         paths[1..-1].each do |path|
-          loop do
-            break if lcsp.ascendant_of?(path)
-
+          until lcsp.ascendant_of?(path)
             if lcsp.root?
               # If we get here a root directory is not an ascendant of path.
               # This may happen if there are paths in different drives on
-- 
cgit v1.2.3


From c5846344ee2a3e9dcdbc2d26ca4ff5b6002d09b0 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 11 Nov 2015 05:24:06 +0100
Subject: adds a comment about how does filter_out_descendants preserve order

---
 activesupport/lib/active_support/file_evented_update_checker.rb | 1 +
 1 file changed, 1 insertion(+)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index d63c8e2438..ca3788a187 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -144,6 +144,7 @@ module ActiveSupport
           }
         end
 
+        # Array#- preserves order.
         directories - descendants
       end
     end
-- 
cgit v1.2.3


From 59be32b572cdff24da64ca6fd82db53a075c8bb6 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 11 Nov 2015 05:54:12 +0100
Subject: registers these changes in the CHANGELOGs

---
 activesupport/CHANGELOG.md | 11 +++++++++++
 1 file changed, 11 insertions(+)

(limited to 'activesupport')

diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index d505218c7a..3fe627a7cf 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,14 @@
+*   Implements an evented file system monitor to asynchronously detect changes
+    in the application source code, routes, locales, etc.
+
+    To opt-in load the [listen](https://github.com/guard/listen) gem in `Gemfile`:
+
+      group :development do
+        gem 'listen', '~> 3.0.4'
+      end
+
+    *Puneet Agarwal* and *Xavier Noria*
+
 * Updated `parameterize` to preserve the case of a string, optionally.
 
     Example:
-- 
cgit v1.2.3


From 0eb6d198d3aaddb62b5241a707f6b1fcedfbedb8 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 11 Nov 2015 06:07:55 +0100
Subject: simplifies directories_to_watch

---
 .../lib/active_support/file_evented_update_checker.rb         | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index ca3788a187..8cb422ad36 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -72,14 +72,11 @@ module ActiveSupport
       end
 
       def directories_to_watch
-        bd = []
+        dtw = (@files + @dirs.keys).map {|f| @ph.existing_parent(f)}
+        dtw.compact!
+        dtw.uniq!
 
-        bd.concat @files.map {|f| @ph.existing_parent(f.dirname)}
-        bd.concat @dirs.keys.map {|dir| @ph.existing_parent(dir)}
-        bd.compact!
-        bd.uniq!
-
-        @ph.filter_out_descendants(bd)
+        @ph.filter_out_descendants(dtw)
       end
 
     class PathHelper
-- 
cgit v1.2.3


From 40a1945d67869f660e7b07120a74106b0150ff23 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 11 Nov 2015 06:15:54 +0100
Subject: simplifies the implementation of #watching?

---
 .../active_support/file_evented_update_checker.rb  | 28 ++++++++++------------
 1 file changed, 12 insertions(+), 16 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 8cb422ad36..6375de4a7d 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -50,23 +50,19 @@ module ActiveSupport
       def watching?(file)
         file = @ph.xpath(file)
 
-        return true  if @files.member?(file)
-        return false if file.directory?
-
-        ext = @ph.normalize_extension(file.extname)
-        dir = file.dirname
-
-        loop do
-          if @dirs.fetch(dir, []).include?(ext)
-            break true
-          else
-            if @lcsp
-              break false if dir == @lcsp
-            else
-              break false if dir.root?
+        if @files.member?(file)
+          true
+        elsif file.directory?
+          false
+        else
+          ext = @ph.normalize_extension(file.extname)
+
+          file.dirname.ascend do |dir|
+            if @dirs.fetch(dir, []).include?(ext)
+              break true
+            elsif dir == @lcsp || dir.root?
+              break false
             end
-
-            dir = dir.parent
           end
         end
       end
-- 
cgit v1.2.3


From f3516f171b7a975e1d3d8aaa37f721476fe49806 Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 11 Nov 2015 06:38:18 +0100
Subject: applies code style guidelines

---
 .../lib/active_support/file_evented_update_checker.rb        | 12 ++++++------
 activesupport/lib/active_support/file_update_checker.rb      |  2 +-
 activesupport/test/file_evented_update_checker_test.rb       | 10 +++++-----
 activesupport/test/file_update_checker_shared_tests.rb       |  2 +-
 activesupport/test/file_update_checker_test.rb               |  2 +-
 5 files changed, 14 insertions(+), 14 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb
index 6375de4a7d..ae500d697f 100644
--- a/activesupport/lib/active_support/file_evented_update_checker.rb
+++ b/activesupport/lib/active_support/file_evented_update_checker.rb
@@ -4,13 +4,13 @@ require 'pathname'
 
 module ActiveSupport
   class FileEventedUpdateChecker #:nodoc: all
-    def initialize(files, dirs={}, &block)
+    def initialize(files, dirs = {}, &block)
       @ph    = PathHelper.new
-      @files = files.map {|f| @ph.xpath(f)}.to_set
+      @files = files.map { |f| @ph.xpath(f) }.to_set
 
       @dirs = {}
       dirs.each do |dir, exts|
-        @dirs[@ph.xpath(dir)] = Array(exts).map {|ext| @ph.normalize_extension(ext)}
+        @dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
       end
 
       @block   = block
@@ -43,7 +43,7 @@ module ActiveSupport
 
       def changed(modified, added, removed)
         unless updated?
-          @updated = (modified + added + removed).any? {|f| watching?(f)}
+          @updated = (modified + added + removed).any? { |f| watching?(f) }
         end
       end
 
@@ -68,7 +68,7 @@ module ActiveSupport
       end
 
       def directories_to_watch
-        dtw = (@files + @dirs.keys).map {|f| @ph.existing_parent(f)}
+        dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
         dtw.compact!
         dtw.uniq!
 
@@ -126,7 +126,7 @@ module ActiveSupport
       def filter_out_descendants(directories)
         return directories if directories.length < 2
 
-        sorted_by_nparts = directories.sort_by {|dir| dir.each_filename.to_a.length}
+        sorted_by_nparts = directories.sort_by { |dir| dir.each_filename.to_a.length }
         descendants = []
 
         until sorted_by_nparts.empty?
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index 78b627c286..1fa9335080 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -35,7 +35,7 @@ module ActiveSupport
     # This method must also receive a block that will be called once a path
     # changes. The array of files and list of directories cannot be changed
     # after FileUpdateChecker has been initialized.
-    def initialize(files, dirs={}, &block)
+    def initialize(files, dirs = {}, &block)
       @files = files.freeze
       @glob  = compile_glob(dirs)
       @block = block
diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb
index f98b656d10..9d09cbca8f 100644
--- a/activesupport/test/file_evented_update_checker_test.rb
+++ b/activesupport/test/file_evented_update_checker_test.rb
@@ -5,7 +5,7 @@ require 'file_update_checker_shared_tests'
 class FileEventedUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerSharedTests
 
-  def new_checker(files=[], dirs={}, &block)
+  def new_checker(files = [], dirs = {}, &block)
     ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block).tap do
       wait
     end
@@ -61,7 +61,7 @@ class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
       /foo/bar
       /foo/baz
       /foo/bar/baz/woo/zoo
-    ).map {|path| pn(path)}
+    ).map { |path| pn(path) }
 
     assert_equal pn('/foo'), @ph.longest_common_subpath(paths)
   end
@@ -72,7 +72,7 @@ class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
       /foo/baz
       /foo/bar/baz/woo/zoo
       /wadus
-    ).map {|path| pn(path)}
+    ).map { |path| pn(path) }
 
     assert_equal pn('/'), @ph.longest_common_subpath(paths)
   end
@@ -102,7 +102,7 @@ class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
       /Rails.root/app/controllers
       /Rails.root/app/models
       /Rails.root/app/helpers
-    ).map {|path| pn(path)}
+    ).map { |path| pn(path) }
 
     assert_equal paths, @ph.filter_out_descendants(paths)
   end
@@ -114,7 +114,7 @@ class FileEventedUpdateCheckerPathHelperTest < ActiveSupport::TestCase
       /Rails.root/app/models
       /Rails.root/app/models/concerns
       /Rails.root/app/helpers
-    ).map {|path| pn(path)}
+    ).map { |path| pn(path) }
 
     assert_equal paths.values_at(0, 2, 4), @ph.filter_out_descendants(paths)
   end
diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb
index caa4d6191d..72955bba8a 100644
--- a/activesupport/test/file_update_checker_shared_tests.rb
+++ b/activesupport/test/file_update_checker_shared_tests.rb
@@ -12,7 +12,7 @@ module FileUpdateCheckerSharedTests
   end
 
   def tmpfiles
-    @tmpfiles ||= %w(foo.rb bar.rb baz.rb).map {|f| tmpfile(f)}
+    @tmpfiles ||= %w(foo.rb bar.rb baz.rb).map { |f| tmpfile(f) }
   end
 
   def teardown
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index bf376f75bc..752f7836cd 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -4,7 +4,7 @@ require 'file_update_checker_shared_tests'
 class FileUpdateCheckerTest < ActiveSupport::TestCase
   include FileUpdateCheckerSharedTests
 
-  def new_checker(files=[], dirs={}, &block)
+  def new_checker(files = [], dirs = {}, &block)
     ActiveSupport::FileUpdateChecker.new(files, dirs, &block)
   end
 
-- 
cgit v1.2.3


From 7edc8d9600465577c29fd36de2cc57392f56616d Mon Sep 17 00:00:00 2001
From: Xavier Noria <fxn@hashref.com>
Date: Wed, 11 Nov 2015 07:00:47 +0100
Subject: modernizes the test definitions in the file monitors suite

---
 .../test/file_update_checker_shared_tests.rb       | 33 +++++++++++-----------
 1 file changed, 17 insertions(+), 16 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb
index 72955bba8a..6be3ab6047 100644
--- a/activesupport/test/file_update_checker_shared_tests.rb
+++ b/activesupport/test/file_update_checker_shared_tests.rb
@@ -1,6 +1,7 @@
 require 'fileutils'
 
 module FileUpdateCheckerSharedTests
+  extend ActiveSupport::Testing::Declarative
   include FileUtils
 
   def tmpdir
@@ -19,7 +20,7 @@ module FileUpdateCheckerSharedTests
     FileUtils.rm_rf(@tmpdir) if @tmpdir
   end
 
-  def test_should_not_execute_the_block_if_no_paths_are_given
+  test 'should not execute the block if no paths are given' do
     i = 0
 
     checker = new_checker { i += 1 }
@@ -28,7 +29,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 0, i
   end
 
-  def test_should_not_execute_the_block_if_no_files_change
+  test 'should not execute the block if no files change' do
     i = 0
 
     FileUtils.touch(tmpfiles)
@@ -39,7 +40,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 0, i
   end
 
-  def test_should_execute_the_block_once_when_files_are_created
+  test 'should execute the block once when files are created' do
     i = 0
 
     checker = new_checker(tmpfiles) { i += 1 }
@@ -50,7 +51,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 1, i
   end
 
-  def test_should_execute_the_block_once_when_files_are_modified
+  test 'should execute the block once when files are modified' do
     i = 0
 
     FileUtils.touch(tmpfiles)
@@ -63,7 +64,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 1, i
   end
 
-  def test_should_execute_the_block_once_when_files_are_deleted
+  test 'should execute the block once when files are deleted' do
     i = 0
 
     FileUtils.touch(tmpfiles)
@@ -77,7 +78,7 @@ module FileUpdateCheckerSharedTests
   end
 
 
-  def test_updated_should_become_true_when_watched_files_are_created
+  test 'updated should become true when watched files are created' do
     i = 0
 
     checker = new_checker(tmpfiles) { i += 1 }
@@ -89,7 +90,7 @@ module FileUpdateCheckerSharedTests
   end
 
 
-  def test_updated_should_become_true_when_watched_files_are_modified
+  test 'updated should become true when watched files are modified' do
     i = 0
 
     FileUtils.touch(tmpfiles)
@@ -102,7 +103,7 @@ module FileUpdateCheckerSharedTests
     assert checker.updated?
   end
 
-  def test_updated_should_become_true_when_watched_files_are_deleted
+  test 'updated should become true when watched files are deleted' do
     i = 0
 
     FileUtils.touch(tmpfiles)
@@ -115,7 +116,7 @@ module FileUpdateCheckerSharedTests
     assert checker.updated?
   end
 
-  def test_should_be_robust_to_handle_files_with_wrong_modified_time
+  test 'should be robust to handle files with wrong modified time' do
     i = 0
 
     FileUtils.touch(tmpfiles)
@@ -132,7 +133,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 1, i
   end
 
-  def test_should_cache_updated_result_until_execute
+  test 'should cache updated result until execute' do
     i = 0
 
     checker = new_checker(tmpfiles) { i += 1 }
@@ -145,7 +146,7 @@ module FileUpdateCheckerSharedTests
     assert !checker.updated?
   end
 
-  def test_should_execute_the_block_if_files_change_in_a_watched_directory_one_extension
+  test 'should execute the block if files change in a watched directory one extension' do
     i = 0
 
     checker = new_checker([], tmpdir => :rb) { i += 1 }
@@ -156,7 +157,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 1, i
   end
 
-  def test_should_execute_the_block_if_files_change_in_a_watched_directory_several_extensions
+  test 'should execute the block if files change in a watched directory several extensions' do
     i = 0
 
     checker = new_checker([], tmpdir => [:rb, :txt]) { i += 1 }
@@ -172,7 +173,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 2, i
   end
 
-  def test_should_not_execute_the_block_if_the_file_extension_is_not_watched
+  test 'should not execute the block if the file extension is not watched' do
     i = 0
 
     checker = new_checker([], tmpdir => :txt) { i += 1 }
@@ -183,7 +184,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 0, i
   end
 
-  def test_does_not_assume_files_exist_on_instantiation
+  test 'does not assume files exist on instantiation' do
     i = 0
 
     non_existing = tmpfile('non_existing.rb')
@@ -195,7 +196,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 1, i
   end
 
-  def test_detects_files_in_new_subdirectories
+  test 'detects files in new subdirectories' do
     i = 0
 
     checker = new_checker([], tmpdir => :rb) { i += 1 }
@@ -213,7 +214,7 @@ module FileUpdateCheckerSharedTests
     assert_equal 1, i
   end
 
-  def test_looked_up_extensions_are_inherited_in_subdirectories_not_listening_to_them
+  test 'looked up extensions are inherited in subdirectories not listening to them' do
     i = 0
 
     subdir = tmpfile('subdir')
-- 
cgit v1.2.3


From f87774bb8b3249dbcd1f16035874f9e583df82d0 Mon Sep 17 00:00:00 2001
From: Michael Grosser <michael@grosser.it>
Date: Sat, 7 Nov 2015 12:27:39 -0800
Subject: fast and consistent return when local_cache does not exist

---
 .../active_support/cache/strategy/local_cache.rb    | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index d521061004..fa007aad56 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -79,22 +79,26 @@ module ActiveSupport
         end
 
         def clear(options = nil) # :nodoc:
-          local_cache.clear(options) if local_cache
+          return super unless cache = local_cache
+          cache.clear(options)
           super
         end
 
         def cleanup(options = nil) # :nodoc:
-          local_cache.clear(options) if local_cache
+          return super unless cache = local_cache
+          cache.clear(options)
           super
         end
 
         def increment(name, amount = 1, options = nil) # :nodoc:
+          return super unless local_cache
           value = bypass_local_cache{super}
           set_cache_value(value, name, amount, options)
           value
         end
 
         def decrement(name, amount = 1, options = nil) # :nodoc:
+          return super unless local_cache
           value = bypass_local_cache{super}
           set_cache_value(value, name, amount, options)
           value
@@ -120,13 +124,12 @@ module ActiveSupport
           end
 
           def set_cache_value(value, name, amount, options) # :nodoc:
-            if local_cache
-              local_cache.mute do
-                if value
-                  local_cache.write(name, value, options)
-                else
-                  local_cache.delete(name, options)
-                end
+            cache = local_cache
+            cache.mute do
+              if value
+                cache.write(name, value, options)
+              else
+                cache.delete(name, options)
               end
             end
           end
-- 
cgit v1.2.3


From cff8b4951fd337075bd6079fa1a679428312baad Mon Sep 17 00:00:00 2001
From: Yuri Kasperovich <y.kasperovich@dev1team.net>
Date: Mon, 9 Nov 2015 23:55:16 +0300
Subject: Minor fix in Module#mattr_reader documentation

---
 activesupport/lib/active_support/core_ext/module/attribute_accessors.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index bf175a8a70..124f90dc0f 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -5,7 +5,7 @@ require 'active_support/core_ext/array/extract_options'
 # attributes.
 class Module
   # Defines a class attribute and creates a class and instance reader methods.
-  # The underlying the class variable is set to +nil+, if it is not previously
+  # The underlying class variable is set to +nil+, if it is not previously
   # defined.
   #
   #   module HairColors
-- 
cgit v1.2.3


From 2f4f4d2cf1e4c5a442459fc250daf66186d110fa Mon Sep 17 00:00:00 2001
From: Jon Pascoe <jon.pascoe@me.com>
Date: Tue, 10 Nov 2015 09:26:48 +0000
Subject: Add days_in_year method

---
 activesupport/CHANGELOG.md                            | 11 ++++++++---
 .../lib/active_support/core_ext/time/calculations.rb  |  6 ++++++
 activesupport/test/core_ext/time_ext_test.rb          | 19 +++++++++++++++++++
 3 files changed, 33 insertions(+), 3 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 3fe627a7cf..faa4a3d6fc 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -3,12 +3,17 @@
 
     To opt-in load the [listen](https://github.com/guard/listen) gem in `Gemfile`:
 
-      group :development do
-        gem 'listen', '~> 3.0.4'
-      end
+        group :development do
+          gem 'listen', '~> 3.0.4'
+        end
 
     *Puneet Agarwal* and *Xavier Noria*
 
+* Added `Time#days_in_year` to return the number of days in the given year, or the
+  current year if no argument is provided.
+
+  *Jon Pascoe*
+
 * Updated `parameterize` to preserve the case of a string, optionally.
 
     Example:
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 82e003fc3b..675db8a36b 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -26,6 +26,12 @@ class Time
       end
     end
 
+    # Returns the number of days in the given year.
+    # If no year is specified, it will use the current year.
+    def days_in_year(year = current.year)
+      days_in_month(2, year) + 337
+    end
+
     # Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
     def current
       ::Time.zone ? ::Time.zone.now : ::Time.now
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 2d0fb70a6b..e45df63fd5 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -617,6 +617,25 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
     end
   end
 
+  def test_days_in_year_with_year
+    assert_equal 365, Time.days_in_year(2005)
+    assert_equal 366, Time.days_in_year(2004)
+    assert_equal 366, Time.days_in_year(2000)
+    assert_equal 365, Time.days_in_year(1900)
+  end
+
+  def test_days_in_year_in_common_year_without_year_arg
+    Time.stub(:now, Time.utc(2007)) do
+      assert_equal 365, Time.days_in_year
+    end
+  end
+
+  def test_days_in_year_in_leap_year_without_year_arg
+    Time.stub(:now, Time.utc(2008)) do
+      assert_equal 366, Time.days_in_year
+    end
+  end
+
   def test_last_month_on_31st
     assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).last_month
   end
-- 
cgit v1.2.3


From 1583c8290a0bbb44ae684974e8fdd459f548f0f4 Mon Sep 17 00:00:00 2001
From: Paul Kehrer <paul.l.kehrer@gmail.com>
Date: Tue, 10 Nov 2015 23:41:28 -0600
Subject: update docs for MessageEncryptor#new to recommend a KDF [ci skip]

---
 activesupport/lib/active_support/message_encryptor.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'activesupport')

diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index c82a13511e..2dde01c844 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -34,8 +34,8 @@ module ActiveSupport
     # Initialize a new MessageEncryptor. +secret+ must be at least as long as
     # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
     # bits. If you are using a user-entered secret, you can generate a suitable
-    # key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
-    # similar.
+    # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
+    # derivation function.
     #
     # Options:
     # * <tt>:cipher</tt>     - Cipher to use. Can be any cipher returned by
-- 
cgit v1.2.3