From 785adabc4b8d892b6e06fca2f259e9c5147e9ca5 Mon Sep 17 00:00:00 2001 From: Xavier Noria 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Time.zone.now when Time.zone or config.time_zone are set, otherwise just returns Time.now. 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 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 OpenSSL::Digest::SHA256.new(user_secret).digest or - # similar. + # key by using ActiveSupport::KeyGenerator or a similar key + # derivation function. # # Options: # * :cipher - Cipher to use. Can be any cipher returned by -- cgit v1.2.3