From 92a9744f8132f46b0e5f49fa5b4d8c66b4e861cb Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 9 Dec 2015 03:07:49 +0100 Subject: renames AS::FileEventedUpdateChecker to AS::EventedFileUpdateChecker Better English. --- activesupport/lib/active_support.rb | 2 +- .../active_support/evented_file_update_checker.rb | 147 +++++++++++++++++++ .../active_support/file_evented_update_checker.rb | 147 ------------------- .../test/evented_file_update_checker_test.rb | 155 +++++++++++++++++++++ .../test/file_evented_update_checker_test.rb | 155 --------------------- 5 files changed, 303 insertions(+), 303 deletions(-) create mode 100644 activesupport/lib/active_support/evented_file_update_checker.rb delete mode 100644 activesupport/lib/active_support/file_evented_update_checker.rb create mode 100644 activesupport/test/evented_file_update_checker_test.rb delete mode 100644 activesupport/test/file_evented_update_checker_test.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 3a2a7d28cb..2019afeb00 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -34,7 +34,7 @@ module ActiveSupport autoload :Dependencies autoload :DescendantsTracker autoload :FileUpdateChecker - autoload :FileEventedUpdateChecker + autoload :EventedFileUpdateChecker autoload :LogSubscriber autoload :Notifications diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb new file mode 100644 index 0000000000..c1c30b1a86 --- /dev/null +++ b/activesupport/lib/active_support/evented_file_update_checker.rb @@ -0,0 +1,147 @@ +require 'listen' +require 'set' +require 'pathname' +require 'concurrent/atomic/atomic_boolean' + +module ActiveSupport + class EventedFileUpdateChecker #:nodoc: all + def initialize(files, dirs = {}, &block) + @ph = PathHelper.new + @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) } + end + + @block = block + @updated = Concurrent::AtomicBoolean.new(false) + @lcsp = @ph.longest_common_subpath(@dirs.keys) + + if (dtw = directories_to_watch).any? + Listen.to(*dtw, &method(:changed)).start + end + end + + def updated? + @updated.true? + end + + def execute + @updated.make_false + @block.call + end + + def execute_if_updated + if updated? + execute + true + end + end + + private + + def changed(modified, added, removed) + unless updated? + @updated.make_true if (modified + added + removed).any? { |f| watching?(f) } + end + end + + def watching?(file) + file = @ph.xpath(file) + + 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 + end + end + end + + def directories_to_watch + dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) } + dtw.compact! + dtw.uniq! + + @ph.filter_out_descendants(dtw) + end + + class PathHelper + using Module.new { + refine Pathname do + def ascendant_of?(other) + self != other && other.ascend do |ascendant| + break true if self == ascendant + end + end + end + } + + def xpath(path) + Pathname.new(path).expand_path + end + + 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? + + lcsp = Pathname.new(paths[0]) + + paths[1..-1].each do |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 + # Windows. + return + else + lcsp = lcsp.parent + end + end + end + + lcsp + end + + # Returns the deepest existing ascendant, which could be the argument itself. + def existing_parent(dir) + dir.ascend do |ascendant| + break ascendant if ascendant.directory? + end + end + + # Filters out directories which are descendants of others in the collection (stable). + def filter_out_descendants(dirs) + return dirs if dirs.length < 2 + + dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length } + descendants = [] + + until dirs_sorted_by_nparts.empty? + dir = dirs_sorted_by_nparts.shift + + dirs_sorted_by_nparts.reject! do |possible_descendant| + dir.ascendant_of?(possible_descendant) && descendants << possible_descendant + end + end + + # Array#- preserves order. + dirs - descendants + end + end + end +end diff --git a/activesupport/lib/active_support/file_evented_update_checker.rb b/activesupport/lib/active_support/file_evented_update_checker.rb deleted file mode 100644 index bb0f26f874..0000000000 --- a/activesupport/lib/active_support/file_evented_update_checker.rb +++ /dev/null @@ -1,147 +0,0 @@ -require 'listen' -require 'set' -require 'pathname' -require 'concurrent/atomic/atomic_boolean' - -module ActiveSupport - class FileEventedUpdateChecker #:nodoc: all - def initialize(files, dirs = {}, &block) - @ph = PathHelper.new - @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) } - end - - @block = block - @updated = Concurrent::AtomicBoolean.new(false) - @lcsp = @ph.longest_common_subpath(@dirs.keys) - - if (dtw = directories_to_watch).any? - Listen.to(*dtw, &method(:changed)).start - end - end - - def updated? - @updated.true? - end - - def execute - @updated.make_false - @block.call - end - - def execute_if_updated - if updated? - execute - true - end - end - - private - - def changed(modified, added, removed) - unless updated? - @updated.make_true if (modified + added + removed).any? { |f| watching?(f) } - end - end - - def watching?(file) - file = @ph.xpath(file) - - 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 - end - end - end - - def directories_to_watch - dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) } - dtw.compact! - dtw.uniq! - - @ph.filter_out_descendants(dtw) - end - - class PathHelper - using Module.new { - refine Pathname do - def ascendant_of?(other) - self != other && other.ascend do |ascendant| - break true if self == ascendant - end - end - end - } - - def xpath(path) - Pathname.new(path).expand_path - end - - 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? - - lcsp = Pathname.new(paths[0]) - - paths[1..-1].each do |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 - # Windows. - return - else - lcsp = lcsp.parent - end - end - end - - lcsp - end - - # Returns the deepest existing ascendant, which could be the argument itself. - def existing_parent(dir) - dir.ascend do |ascendant| - break ascendant if ascendant.directory? - end - end - - # Filters out directories which are descendants of others in the collection (stable). - def filter_out_descendants(dirs) - return dirs if dirs.length < 2 - - dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length } - descendants = [] - - until dirs_sorted_by_nparts.empty? - dir = dirs_sorted_by_nparts.shift - - dirs_sorted_by_nparts.reject! do |possible_descendant| - dir.ascendant_of?(possible_descendant) && descendants << possible_descendant - end - end - - # Array#- preserves order. - dirs - descendants - end - end - end -end diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb new file mode 100644 index 0000000000..bc3f77bd54 --- /dev/null +++ b/activesupport/test/evented_file_update_checker_test.rb @@ -0,0 +1,155 @@ +require 'abstract_unit' +require 'pathname' +require 'file_update_checker_shared_tests' + +class EventedFileUpdateCheckerTest < ActiveSupport::TestCase + include FileUpdateCheckerSharedTests + + def setup + skip if ENV['LISTEN'] == '0' + super + end + + def new_checker(files = [], dirs = {}, &block) + ActiveSupport::EventedFileUpdateChecker.new(files, dirs, &block).tap do + wait + end + end + + def teardown + super + Listen.stop + end + + 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 EventedFileUpdateCheckerPathHelperTest < ActiveSupport::TestCase + def pn(path) + Pathname.new(path) + end + + setup do + @ph = ActiveSupport::EventedFileUpdateChecker::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 + + 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 + + test '#filter_out_descendants works on path units' do + paths = %w( + /foo/bar + /foo/barrrr + ).map { |path| pn(path) } + + assert_equal paths, @ph.filter_out_descendants(paths) + end + + test '#filter_out_descendants deals correctly with the root directory' do + paths = %w( + / + /foo + /foo/bar + ).map { |path| pn(path) } + + assert_equal paths.values_at(0), @ph.filter_out_descendants(paths) + end + + test '#filter_out_descendants preserves duplicates' do + paths = %w( + /foo + /foo/bar + /foo + ).map { |path| pn(path) } + + assert_equal paths.values_at(0, 2), @ph.filter_out_descendants(paths) + end +end diff --git a/activesupport/test/file_evented_update_checker_test.rb b/activesupport/test/file_evented_update_checker_test.rb deleted file mode 100644 index ec3a7e28f3..0000000000 --- a/activesupport/test/file_evented_update_checker_test.rb +++ /dev/null @@ -1,155 +0,0 @@ -require 'abstract_unit' -require 'pathname' -require 'file_update_checker_shared_tests' - -class FileEventedUpdateCheckerTest < ActiveSupport::TestCase - include FileUpdateCheckerSharedTests - - def setup - skip if ENV['LISTEN'] == '0' - super - end - - def new_checker(files = [], dirs = {}, &block) - ActiveSupport::FileEventedUpdateChecker.new(files, dirs, &block).tap do - wait - end - end - - def teardown - super - Listen.stop - end - - 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 - 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 - - 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 - - test '#filter_out_descendants works on path units' do - paths = %w( - /foo/bar - /foo/barrrr - ).map { |path| pn(path) } - - assert_equal paths, @ph.filter_out_descendants(paths) - end - - test '#filter_out_descendants deals correctly with the root directory' do - paths = %w( - / - /foo - /foo/bar - ).map { |path| pn(path) } - - assert_equal paths.values_at(0), @ph.filter_out_descendants(paths) - end - - test '#filter_out_descendants preserves duplicates' do - paths = %w( - /foo - /foo/bar - /foo - ).map { |path| pn(path) } - - assert_equal paths.values_at(0, 2), @ph.filter_out_descendants(paths) - end -end -- cgit v1.2.3