aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2010-06-20 13:26:42 +0200
committerJosé Valim <jose.valim@gmail.com>2010-06-20 13:26:42 +0200
commit71703c98ba2bf65a6fed94917b039827cb63bace (patch)
tree186380d706185edae8b801f48354cec6ab0dd08e
parent9e081caee74e6d08035a8835899dcc566536a871 (diff)
downloadrails-71703c98ba2bf65a6fed94917b039827cb63bace.tar.gz
rails-71703c98ba2bf65a6fed94917b039827cb63bace.tar.bz2
rails-71703c98ba2bf65a6fed94917b039827cb63bace.zip
Add ActiveSupport::FileUpdateChecker.
-rw-r--r--activesupport/lib/active_support.rb4
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb37
-rw-r--r--activesupport/test/file_update_checker_test.rb56
3 files changed, 96 insertions, 1 deletions
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index f93b351655..3ce5476bbd 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -39,6 +39,9 @@ require "active_support/dependencies/autoload"
module ActiveSupport
extend ActiveSupport::Autoload
+ autoload :DescendantsTracker
+ autoload :FileUpdateChecker
+
# TODO: Narrow this list down
eager_autoload do
autoload :BacktraceCleaner
@@ -51,7 +54,6 @@ module ActiveSupport
autoload :Concern
autoload :Configurable
autoload :Deprecation
- autoload :DescendantsTracker
autoload :Gzip
autoload :Inflector
autoload :JSON
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
new file mode 100644
index 0000000000..c0b5ca4deb
--- /dev/null
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -0,0 +1,37 @@
+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
+ # is used by Rails to reload routes whenever they are changed upon
+ # a new request.
+ #
+ # routes_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
+ # paths.each { |p| load(p) }
+ # Rails::Application.routes.reload!
+ # end
+ #
+ # ActionDispatch::Callbacks.to_prepare do
+ # routes_reloader.execute_if_updated
+ # end
+ #
+ class FileUpdateChecker
+ attr_reader :paths, :last_update_at
+
+ def initialize(paths, calculate=false, &block)
+ @paths = paths
+ @block = block
+ @last_update_at = updated_at if calculate
+ end
+
+ def updated_at
+ paths.map { |path| File.stat(path).mtime }.max
+ end
+
+ def execute_if_updated
+ current_update_at = self.updated_at
+ if @last_update_at != current_update_at
+ @last_update_at = current_update_at
+ @block.call
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
new file mode 100644
index 0000000000..baf29cc337
--- /dev/null
+++ b/activesupport/test/file_update_checker_test.rb
@@ -0,0 +1,56 @@
+require 'abstract_unit'
+require 'test/unit'
+require 'active_support'
+require 'fileutils'
+
+MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__)
+
+class FileUpdateCheckerTest < Test::Unit::TestCase
+ FILES = %w(1.txt 2.txt 3.txt)
+
+ def setup
+ FileUtils.touch(FILES)
+ end
+
+ def teardown
+ FileUtils.rm(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_invoke_the_block_on_first_call_if_it_does_not_calculate_last_updated_at_on_load
+ i = 0
+ 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(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(FILES){ i += 1 }
+ 5.times { checker.execute_if_updated }
+ assert_equal 1, i
+ end
+
+ def test_should_invoke_the_block_if_a_file_has_changed
+ i = 0
+ checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
+ checker.execute_if_updated
+ sleep(1)
+ FileUtils.touch(FILES)
+ checker.execute_if_updated
+ assert_equal 2, i
+ end
+end