aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwycats <wycats@gmail.com>2011-02-06 13:41:56 -0800
committerwycats <wycats@gmail.com>2011-02-06 13:42:22 -0800
commit1fd9d978a737d36cf7cca698f0fcbfc6fcdbed98 (patch)
tree4642ca3e6ffcbc86d6252f6f55555af0fceafa7e
parent3026843dc1ff42a632ebe989e1f6dfadb0cd10a5 (diff)
downloadrails-1fd9d978a737d36cf7cca698f0fcbfc6fcdbed98.tar.gz
rails-1fd9d978a737d36cf7cca698f0fcbfc6fcdbed98.tar.bz2
rails-1fd9d978a737d36cf7cca698f0fcbfc6fcdbed98.zip
Add initial FileWatcher implementation. The Backend is just an abstract implementation, which will be inherited by backends that do the heavy lifting.
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/file_watcher.rb41
-rw-r--r--activesupport/test/file_watcher_test.rb65
3 files changed, 107 insertions, 0 deletions
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 6b87774978..6b662ac660 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -42,6 +42,7 @@ module ActiveSupport
autoload :DescendantsTracker
autoload :FileUpdateChecker
+ autoload :FileWatcher
autoload :LogSubscriber
autoload :Notifications
diff --git a/activesupport/lib/active_support/file_watcher.rb b/activesupport/lib/active_support/file_watcher.rb
new file mode 100644
index 0000000000..046a5a0309
--- /dev/null
+++ b/activesupport/lib/active_support/file_watcher.rb
@@ -0,0 +1,41 @@
+module ActiveSupport
+ class FileWatcher
+ class Backend
+ def initialize(path, watcher)
+ @watcher = watcher
+ @path = path
+ end
+
+ def trigger(files)
+ @watcher.trigger(files)
+ end
+ end
+
+ def initialize
+ @regex_matchers = {}
+ end
+
+ def watch(path, &block)
+ return watch_regex(path, &block) if path.is_a?(Regexp)
+ raise "Paths must be regular expressions. #{path.inspect} is a #{path.class}"
+ end
+
+ def watch_regex(regex, &block)
+ @regex_matchers[regex] = block
+ end
+
+ def trigger(files)
+ trigger_files = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
+
+ files.each do |file, state|
+ @regex_matchers.each do |regex, block|
+ trigger_files[block][state] << file if file =~ regex
+ end
+ end
+
+ trigger_files.each do |block, payload|
+ block.call payload
+ end
+ end
+ end
+end
diff --git a/activesupport/test/file_watcher_test.rb b/activesupport/test/file_watcher_test.rb
new file mode 100644
index 0000000000..54aa9f92f1
--- /dev/null
+++ b/activesupport/test/file_watcher_test.rb
@@ -0,0 +1,65 @@
+require 'abstract_unit'
+
+class FileWatcherTest < ActiveSupport::TestCase
+ class DumbBackend < ActiveSupport::FileWatcher::Backend
+ end
+
+ def setup
+ @watcher = ActiveSupport::FileWatcher.new
+
+ # In real life, the backend would take the path and use it to observe the file
+ # system. In our case, we will manually trigger the events for unit testing,
+ # so we can pass any path.
+ @backend = DumbBackend.new("RAILS_WOOT", @watcher)
+
+ @payload = []
+ @watcher.watch %r{^app/assets/.*\.scss$} do |pay|
+ pay.each do |status, files|
+ files.sort!
+ end
+ @payload << pay
+ end
+ end
+
+ def test_one_change
+ @backend.trigger("app/assets/main.scss" => :changed)
+ assert_equal({:changed => ["app/assets/main.scss"]}, @payload.first)
+ end
+
+ def test_multiple_changes
+ @backend.trigger("app/assets/main.scss" => :changed, "app/assets/javascripts/foo.coffee" => :changed)
+ assert_equal([{:changed => ["app/assets/main.scss"]}], @payload)
+ end
+
+ def test_multiple_changes_match
+ @backend.trigger("app/assets/main.scss" => :changed, "app/assets/print.scss" => :changed, "app/assets/javascripts/foo.coffee" => :changed)
+ assert_equal([{:changed => ["app/assets/main.scss", "app/assets/print.scss"]}], @payload)
+ end
+
+ def test_multiple_state_changes
+ @backend.trigger("app/assets/main.scss" => :created, "app/assets/print.scss" => :changed)
+ assert_equal([{:changed => ["app/assets/print.scss"], :created => ["app/assets/main.scss"]}], @payload)
+ end
+
+ def test_more_blocks
+ payload = []
+ @watcher.watch %r{^config/routes\.rb$} do |pay|
+ payload << pay
+ end
+
+ @backend.trigger "config/routes.rb" => :changed
+ assert_equal [:changed => ["config/routes.rb"]], payload
+ assert_equal [], @payload
+ end
+
+ def test_overlapping_watchers
+ payload = []
+ @watcher.watch %r{^app/assets/main\.scss$} do |pay|
+ payload << pay
+ end
+
+ @backend.trigger "app/assets/print.scss" => :changed, "app/assets/main.scss" => :changed
+ assert_equal [:changed => ["app/assets/main.scss"]], payload
+ assert_equal [:changed => ["app/assets/main.scss", "app/assets/print.scss"]], @payload
+ end
+end