mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Provide a dir => extension API to file update checker.
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
require "active_support/core_ext/array/wrap"
|
||||
require "active_support/core_ext/array/extract_options"
|
||||
|
||||
module ActiveSupport
|
||||
# This class is responsible to track files and invoke the given block
|
||||
# whenever one of these files are changed. For example, this class
|
||||
@@ -15,15 +18,34 @@ module ActiveSupport
|
||||
class FileUpdateChecker
|
||||
attr_reader :paths, :last_update_at
|
||||
|
||||
# It accepts two parameters on initialization. The first is
|
||||
# the *paths* and the second is *calculate*, a boolean.
|
||||
#
|
||||
# paths must be an array of file paths but can contain a hash as
|
||||
# last argument. The hash must have directories as keys and the
|
||||
# value is an array of extensions to be watched under that directory.
|
||||
#
|
||||
# If *calculate* is true, the latest updated at will calculated
|
||||
# on initialization, therefore, the first call to execute_if_updated
|
||||
# will only evaluate the block if something really changed.
|
||||
#
|
||||
# This method must also receive a block that will be the block called
|
||||
# once a file changes.
|
||||
#
|
||||
# This particular implementation checks for added files and updated files,
|
||||
# but not removed files. Directories lookup are compiled to a glob for
|
||||
# performance.
|
||||
def initialize(paths, calculate=false, &block)
|
||||
@paths = paths
|
||||
@glob = compile_glob(@paths.extract_options!)
|
||||
@block = block
|
||||
@last_update_at = calculate ? updated_at : nil
|
||||
end
|
||||
|
||||
def updated_at
|
||||
# TODO: Use Enumerable check once we get rid of 1.8.7
|
||||
all = paths.is_a?(Array) ? paths : Dir[paths]
|
||||
all = []
|
||||
all.concat @paths
|
||||
all.concat Dir[@glob] if @glob
|
||||
all.map { |path| File.mtime(path) }.max
|
||||
end
|
||||
|
||||
@@ -37,5 +59,22 @@ module ActiveSupport
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compile_glob(hash)
|
||||
return if hash.empty?
|
||||
globs = []
|
||||
hash.each do |key, value|
|
||||
globs << "#{key}/**/*#{compile_ext(value)}"
|
||||
end
|
||||
"{#{globs.join(",")}}"
|
||||
end
|
||||
|
||||
def compile_ext(array)
|
||||
array = Array.wrap(array)
|
||||
return if array.empty?
|
||||
".{#{array.join(",")}}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,21 +4,19 @@ require 'fileutils'
|
||||
|
||||
MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
|
||||
|
||||
module FileUpdateCheckerSuite
|
||||
class FileUpdateCheckerWithEnumerableTest < Test::Unit::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(FILES)
|
||||
end
|
||||
|
||||
def args
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def test_should_not_execute_the_block_if_no_paths_are_given
|
||||
i = 0
|
||||
checker = ActiveSupport::FileUpdateChecker.new([]){ i += 1 }
|
||||
@@ -28,41 +26,51 @@ module FileUpdateCheckerSuite
|
||||
|
||||
def test_should_invoke_the_block_on_first_call_if_it_does_not_calculate_last_updated_at_on_load
|
||||
i = 0
|
||||
checker = ActiveSupport::FileUpdateChecker.new(args){ i += 1 }
|
||||
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
|
||||
checker.execute_if_updated
|
||||
assert_equal 1, i
|
||||
end
|
||||
|
||||
def test_should_not_invoke_the_block_on_first_call_if_it_calculates_last_updated_at_on_load
|
||||
i = 0
|
||||
checker = ActiveSupport::FileUpdateChecker.new(args, true){ i += 1 }
|
||||
checker = ActiveSupport::FileUpdateChecker.new(FILES, true){ 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(args, true){ i += 1 }
|
||||
checker = ActiveSupport::FileUpdateChecker.new(FILES, true){ 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(args, true){ i += 1 }
|
||||
checker = ActiveSupport::FileUpdateChecker.new(FILES, true){ i += 1 }
|
||||
sleep(1)
|
||||
FileUtils.touch(FILES)
|
||||
assert checker.execute_if_updated
|
||||
assert_equal 1, i
|
||||
end
|
||||
end
|
||||
|
||||
class FileUpdateCheckerWithEnumerableTest < Test::Unit::TestCase
|
||||
include FileUpdateCheckerSuite
|
||||
def args; FILES; end
|
||||
end
|
||||
def test_should_invoke_the_block_if_a_watched_dir_changed_its_glob
|
||||
i = 0
|
||||
checker = ActiveSupport::FileUpdateChecker.new([{"tmp_watcher" => [:txt]}], true){ i += 1 }
|
||||
FileUtils.cd "tmp_watcher" do
|
||||
FileUtils.touch(FILES)
|
||||
end
|
||||
assert checker.execute_if_updated
|
||||
assert_equal 1, i
|
||||
end
|
||||
|
||||
class FileUpdateCheckerWithStringTest < Test::Unit::TestCase
|
||||
include FileUpdateCheckerSuite
|
||||
def args; "{1,2,3}.txt"; end
|
||||
end
|
||||
def test_should_not_invoke_the_block_if_a_watched_dir_changed_its_glob
|
||||
i = 0
|
||||
checker = ActiveSupport::FileUpdateChecker.new([{"tmp_watcher" => :rb}], true){ i += 1 }
|
||||
FileUtils.cd "tmp_watcher" do
|
||||
FileUtils.touch(FILES)
|
||||
end
|
||||
assert !checker.execute_if_updated
|
||||
assert_equal 0, i
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user