Class: FileMonitor
- Inherits:
-
Object
- Object
- FileMonitor
- Defined in:
- lib/FileMonitor.rb
Overview
Purpose: Watches the file system for changes
Usage:
require 'filemonitor'
# Create a FileMonitor instance and assign the callback to the entire object.
# In the following example, "watched_item" is a MonitoredItems::Store, see the new
# method for a better example of working in your block.
file_spy = FileMonitor.new do |watched_item| ... end
# Any files in the working directory (Dir.pwd) and its sub-directories will
# be watched for changes. If a change is found the callback assigned above
# will be enacted.
file_spy << Dir.pwd
file_spy << "/path/to/other/file.rb"
# launch an independent process to do the monitoring:
file_spy.spawn
# Alternatively you can do all of the above in one line:
FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") do |watched_item| ... end
Constant Summary collapse
- VERSION =
'0.0.3'
Instance Attribute Summary collapse
-
#callback ⇒ Object
Returns the value of attribute callback.
-
#pid ⇒ Object
Returns the value of attribute pid.
-
#watched ⇒ Object
Returns the value of attribute watched.
Class Method Summary collapse
-
.when_modified(*paths, &callback) ⇒ Object
Returns a spawned FileMonitor instance.
Instance Method Summary collapse
-
#<<(path) ⇒ Object
The ‘<<’ method works the same way as the ‘add’ method but does not support a callback.
-
#add(path, regexp_file_filter = /.*/, &callback) ⇒ Object
The add method accepts a directory path or file path and optional callback.
-
#halt ⇒ Object
Halts a spawned FileMonitor Instance.
-
#index_of(path) ⇒ Object
Returns index of watched item or false if non existant.
-
#initialize(&callback) ⇒ FileMonitor
constructor
The new method may be called with an optional callback which must be a block either do…end or ….
-
#monitor(interval = 1) ⇒ Object
Runs an endless loop watching for changes.
-
#process ⇒ Object
Itterates watched files and runs callbacks when changes are detected.
-
#spawn(interval = 1) ⇒ Object
(also: #start)
Spauns a child process that is looking for changes at every given interval.
-
#stop ⇒ Object
Stops a spawned FileMonitor instance.
Constructor Details
#initialize(&callback) ⇒ FileMonitor
The new method may be called with an optional callback which must be a block either do…end or …. The block may consiste of upto arguments ie {|watched_item, monitored|}, in which case, the watched_item is an instance of FileMonitor::Store and monitored is the FileMonitor instances self.
The first argument of the block, ‘watched_item’ in this case, will respond to: (:path, :modified & :callback). The second argument of the block, ‘monitored’ in this case, will respond any FileMonitor method.
Example:
FileMonitor.new do |watched_item|
puts "I am watched this file: #{watched_item.path}"
puts "When I find a change I will call watched_item.callback"
end
FileMonitor.new do |watched_item, monitored|
# add files from a file that is a list of files to watch... Note: There
IO.readlines(watched_item.path).each {|file| monitored << file } if watched_item.path == '/path/to/file/watchme.list'
# clear watchme.list so we won't add all watch files every time the file changes
open(watched_item) { |f| puts '' }
end
51 52 53 54 |
# File 'lib/FileMonitor.rb', line 51 def initialize(&callback) @watched = [] @callback = callback unless callback.nil? end |
Instance Attribute Details
#callback ⇒ Object
Returns the value of attribute callback.
28 29 30 |
# File 'lib/FileMonitor.rb', line 28 def callback @callback end |
#pid ⇒ Object
Returns the value of attribute pid.
28 29 30 |
# File 'lib/FileMonitor.rb', line 28 def pid @pid end |
#watched ⇒ Object
Returns the value of attribute watched.
28 29 30 |
# File 'lib/FileMonitor.rb', line 28 def watched @watched end |
Class Method Details
.when_modified(*paths, &callback) ⇒ Object
Returns a spawned FileMonitor instance. The independent process automatically calls the given callback when changes are found.
Example:
fm = FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") {|watched_item, file_monitor| ... }
fm.pid # => 23994
fm.callback.nil? # => false
fm.watched.size # => 28
64 65 66 67 68 69 |
# File 'lib/FileMonitor.rb', line 64 def self.when_modified(*paths, &callback) fm = FileMonitor.new &callback paths.each {|path| fm << path} fm.spawn return fm end |
Instance Method Details
#<<(path) ⇒ Object
The ‘<<’ method works the same way as the ‘add’ method but does not support a callback.
Example:
fm = FileMonitor.new do |path|
puts "Detected a change on #{path}"
end
# The following will run the default callback when changes are found in the /tmp folder:
fm << '/tmp'
109 110 111 |
# File 'lib/FileMonitor.rb', line 109 def <<(path) add path, regexp_file_filter end |
#add(path, regexp_file_filter = /.*/, &callback) ⇒ Object
The add method accepts a directory path or file path and optional callback. If a directory path is given all files in that path are recursively added. If a callback is given then that proc will be called when a change is detected on that file or group of files. If no proc is given via the add method then the object callback is called. If a regexp is given as the second argument only files matching the regexp will be monitored.
Example:
fm = FileMonitor.new do |path|
puts "Detected a change on #{path}"
end
# The following will run the default callback when changes are found in the /tmp folder:
fm.add '/tmp'
# The following will run the given callback on any files ending in 'txt' in the /home folder when changed:
fm.add('/home', /txt$/) do |path|
puts "A users file has changed: #{path}"
end
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/FileMonitor.rb', line 88 def add(path, regexp_file_filter=/.*/, &callback) if File.file?(path) && regexp_file_filter === File.split(path).last index = index_of(path) || @watched.size @watched[index] = MonitoredItems::Store.new({:path=>File.(path), :callback=>callback, :digest=>digest(path)}) return true elsif File.directory? path files_recursive(path).each {|f| add(f, regexp_file_filter, &callback) } return true end false end |
#halt ⇒ Object
Halts a spawned FileMonitor Instance. The current iteration will be halted in its tracks. See Also: stop
Example:
fm = FileMonitor.new {|watched_item| puts 'do something when file is changed'}
fm.spawn # and now its doing its job...
fm.stop
224 225 226 227 228 229 |
# File 'lib/FileMonitor.rb', line 224 def halt() if Fixnum === pid Process.kill('USR2', pid) Process.wait pid end end |
#index_of(path) ⇒ Object
Returns index of watched item or false if non existant.
Example:
fm = FileMonitor.new
fm << '/tmp/first.txt'
fm << '/tmp/second.txt'
fm.index_of '/tmp/first.txt' # => 0
fm.index_of '/tmp/first.txt' # => 1
fm.index_of '/tmp/woops.txt' # => false
176 177 178 179 |
# File 'lib/FileMonitor.rb', line 176 def index_of(path) watched.each_with_index {|watched,i| return i if watched.path == path} false end |
#monitor(interval = 1) ⇒ Object
Runs an endless loop watching for changes. It will sleep for the given interval between looking for changes. This method is intended to be run in a subprocess or threaded environment. The spawn method calls this method and takes care of the forking and pid management for you.
Example:
fm = FileMonitor.new
fm << '/tmp'
fm.monitor
puts "will not get here unless a signal is sent to the process which interrupts the loop."
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/FileMonitor.rb', line 140 def monitor(interval = 1) trap("INT") do puts " FileMonitor was interrupted by Control-C... exiting gracefully" # exit @shutdown = true end trap("USR1") do puts " FileMonitor was asked nicely to stop." @shutdown = true pid = nil end trap("USR2") do puts " FileMonitor was halted." pid = nil exit end while true exit if @shutdown process sleep interval unless @shutdown end end |
#process ⇒ Object
Itterates watched files and runs callbacks when changes are detected. This is the semi-automatic way to run the FileMonitor.
Example:
changed_files = []
fm = FileMonitor.new() {|watched_item| changed_files = watched_item.path}
fm << '/tmp'
fm.process # this will look for changes in any watched items only once... call this when you want to look for changes.
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/FileMonitor.rb', line 120 def process @watched.each do |i| key = digest(i.path) # i.digest = key if i.digest.nil? # skip first change detection, its always unknown on first run unless i.digest == key respond_to_change(i, key) end end end |
#spawn(interval = 1) ⇒ Object Also known as: start
Spauns a child process that is looking for changes at every given interval.
The interval is in seconds and defaults to 1 second.
Example:
fm = FileMonitor.new {|watched_item| puts 'do something when file is changed'}
fm << @app_root + '/lib'
fm.spawn # and now its doing its job...
188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/FileMonitor.rb', line 188 def spawn(interval = 1) if pid.nil? @pid = fork {monitor interval} Process.detach(pid) Kernel.at_exit do # sends the kill command unless the pid is not found on the system Process.kill('HUP', pid) if process_running? pid = nil end end pid end |
#stop ⇒ Object
Stops a spawned FileMonitor instance. The FileMonitor will finish the the currnet iteration and exit gracefully. See Also: Halt
Example:
fm = FileMonitor.new {|watched_item| puts 'do something when file is changed'}
fm.spawn # and now its doing its job...
fm.stop
208 209 210 211 212 213 214 215 216 |
# File 'lib/FileMonitor.rb', line 208 def stop() # Send user defined signal USR1 to process. This is trapped in spauned processes and tells the process to Ctrl+C # The user defined signial is sent as a safty percausion because the process id is not tracked through a pid file # nor compared with the running command. The FileMonitor spaun will respond to the USR1 signal by exiting properly.* if Fixnum === pid Process.kill('USR1', pid) Process.wait pid end end |