From eda503c836c6cd02937e111b175979c5722677fd Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 10 Nov 2015 02:28:14 -0800 Subject: [PATCH] the evented monitor filters out descendants --- .../file_evented_update_checker.rb | 46 +++++++++++++++---- .../test/file_evented_update_checker_test.rb | 30 ++++++++++++ 2 files changed, 66 insertions(+), 10 deletions(-) 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 @@ def initialize(files, dirs={}, &block) @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 @@ def watching?(file) 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 @@ def existing_parent(dir) 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 @@ def pn(path) 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 -- GitLab