Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Provide a dir => extension API to file update checker.

  • Loading branch information...
commit 62cda03fa824ce1e1fc92aaee0367c29ade6a504 1 parent b04e2d8
@josevalim josevalim authored
View
43 activesupport/lib/active_support/file_update_checker.rb
@@ -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 @@ def execute_if_updated
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
View
44 activesupport/test/file_update_checker_test.rb
@@ -4,21 +4,19 @@
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 @@ def test_should_not_execute_the_block_if_no_paths_are_given
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
Please sign in to comment.
Something went wrong with that request. Please try again.