aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/evented_file_update_checker.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/evented_file_update_checker.rb')
-rw-r--r--activesupport/lib/active_support/evented_file_update_checker.rb49
1 files changed, 44 insertions, 5 deletions
diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb
index 21fdf7bb80..a2dcf31132 100644
--- a/activesupport/lib/active_support/evented_file_update_checker.rb
+++ b/activesupport/lib/active_support/evented_file_update_checker.rb
@@ -3,6 +3,33 @@ require 'pathname'
require 'concurrent/atomic/atomic_boolean'
module ActiveSupport
+ # Allows you to "listen" to changes in a file system.
+ # The evented file updater does not hit disk when checking for updates
+ # instead it uses platform specific file system events to trigger a change
+ # in state.
+ #
+ # The file checker takes an array of files to watch or a hash specifying directories
+ # and file extensions to watch. It also takes a block that is called when
+ # EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated
+ # is run and there have been changes to the file system.
+ #
+ # Note: Forking will cause the first call to `updated?` to return `true`.
+ #
+ # Example:
+ #
+ # checker = EventedFileUpdateChecker.new(["/tmp/foo"], -> { puts "changed" })
+ # checker.updated?
+ # # => false
+ # checker.execute_if_updated
+ # # => nil
+ #
+ # FileUtils.touch("/tmp/foo")
+ #
+ # checker.updated?
+ # # => true
+ # checker.execute_if_updated
+ # # => "changed"
+ #
class EventedFileUpdateChecker #:nodoc: all
def initialize(files, dirs = {}, &block)
@ph = PathHelper.new
@@ -13,11 +40,13 @@ module ActiveSupport
@dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
end
- @block = block
- @updated = Concurrent::AtomicBoolean.new(false)
- @lcsp = @ph.longest_common_subpath(@dirs.keys)
+ @block = block
+ @updated = Concurrent::AtomicBoolean.new(false)
+ @lcsp = @ph.longest_common_subpath(@dirs.keys)
+ @pid = Process.pid
+ @boot_mutex = Mutex.new
- if (dtw = directories_to_watch).any?
+ if (@dtw = directories_to_watch).any?
# Loading listen triggers warnings. These are originated by a legit
# usage of attr_* macros for private attributes, but adds a lot of noise
# to our test suite. Thus, we lazy load it and disable warnings locally.
@@ -28,11 +57,18 @@ module ActiveSupport
raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
end
end
- Listen.to(*dtw, &method(:changed)).start
end
+ boot!
end
def updated?
+ @boot_mutex.synchronize do
+ if @pid != Process.pid
+ boot!
+ @pid = Process.pid
+ @updated.make_true
+ end
+ end
@updated.true?
end
@@ -50,6 +86,9 @@ module ActiveSupport
end
private
+ def boot!
+ Listen.to(*@dtw, &method(:changed)).start
+ end
def changed(modified, added, removed)
unless updated?