aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/file_evented_update_checker.rb67
-rw-r--r--activesupport/test/file_evented_update_checker_test.rb21
-rw-r--r--activesupport/test/file_update_checker_test.rb107
-rw-r--r--activesupport/test/file_update_checker_with_enumerable_test_cases.rb110
5 files changed, 203 insertions, 103 deletions
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