class Polling
A cross-platform filesystem monitor that uses polling to detect changes.
This implementation works on all platforms by periodically checking file modification times. While less efficient than platform-specific native implementations, it provides a reliable fallback for platforms without native filesystem event support.
Definitions
def initialize(logger: nil)
Initialize a new polling monitor.
Signature
-
parameter
logger
Logger
Optional logger for debugging monitor activity.
Implementation
def initialize(logger: nil)
@directories = Hash.new do |hash, key|
hash[key] = Set.new
end
@updated = false
@deletions = nil
@logger = logger || Logger.new(nil)
end
attr :updated
Signature
-
attribute
Boolean
Whether the set of monitored directories has been updated.
def update(directories, *args)
Notify the monitor that files in these directories have changed.
Implementation
def update(directories, *args)
@logger.debug{"Update: #{directories} #{args.inspect}"}
delay_deletions do
directories.each do |directory|
@logger.debug{"Directory: #{directory}"}
@directories[directory].each do |handle|
@logger.debug{"Handle changed: #{handle.inspect}"}
# Changes here may not actually require an update to the handle:
handle.changed!(*args)
end
end
end
end
def roots
Signature
-
returns
Array(String)
The root directories currently being monitored.
Implementation
def roots
@directories.keys
end
def delete(handle)
Delete a handle from the monitor.
Signature
-
parameter
handle
Handle
The handle to remove.
Implementation
def delete(handle)
if @deletions
@logger.debug{"Delayed delete handle: #{handle}"}
@deletions << handle
else
purge(handle)
end
end
def track_changes(files, &block)
Track changes to a set of files.
Signature
-
parameter
files
List
The list of files to monitor.
-
yields
{|state| ...}
When changes are detected.
-
parameter
state
State
The current state with information about added, changed, and removed files.
-
parameter
-
returns
Handle
A handle that can be used to stop tracking these files.
Implementation
def track_changes(files, &block)
handle = Handle.new(self, files, &block)
add(handle)
end
def add(handle)
Add a handle to the monitor.
Signature
-
parameter
handle
Handle
The handle to add.
-
returns
Handle
The added handle.
Implementation
def add(handle)
@logger.debug{"Adding handle: #{handle}"}
handle.directories.each do |directory|
# We want the full path as a plain string:
directory = directory.to_s
@directories[directory] << handle
# We just added the first handle:
if @directories[directory].size == 1
# If the handle already existed, this might trigger unnecessarily.
@updated = true
end
end
handle
end
def run(latency: 0.1, **options, &block)
Run the monitor loop, checking for changes at regular intervals.
This method blocks until interrupted via throw :interrupt
. It continuously polls the filesystem for changes and invokes registered callbacks.
Signature
-
parameter
latency
Numeric
The time in seconds to wait between checks (default: 0.1).
Implementation
def run(latency: 0.1, **options, &block)
catch(:interrupt) do
while true
self.update(self.roots)
yield if block_given?
sleep(latency)
end
end
end