Lightweight file watcher weighing about 300 LOC. No runtime dependencies and no platform specific code. Works everywhere. Monitors changes in the file system by polling. Has no config files.
$ gem install filewatcher
or with bundler
:
# Gemfile
gem 'filewatcher'
Watch a list of files and directories:
require 'filewatcher'
Filewatcher.new(['lib/', 'Rakefile']).watch do |changes|
changes.each do |filename, event|
puts "#{filename} #{event}"
end
end
Watch a single directory, for changes in all files and subdirectories:
Filewatcher.new('lib/').watch do |changes|
# ...
end
Notice that the previous is equivalent to the following:
Filewatcher.new('lib/**/*').watch do |changes|
# ...
end
Watch files and directories in the given directory - and not in subdirectories:
Filewatcher.new('lib/*').watch do |changes|
# ...
end
Watch an absolute directory:
Filewatcher.new('/tmp/foo').watch do |changes|
# ...
end
To detect if a file is updated, added or deleted:
Filewatcher.new(['README.rdoc']).watch do |changes|
changes.each do |filename, event|
puts "File #{event}: #{filename}"
end
end
When a file is renamed and every
option is enabled, it is detected as
a new file followed by a file deletion:
Filewatcher.new(['lib/'], every: true).watch do |changes|
changes.each do |filename, event|
puts "File #{event}: #{filename}"
end
end
# Rename from `old_test.rb` to `new_test.rb` will print:
# File created: /absolute/path/lib/new_test.rb
# File deleted: /absolute/path/lib/old_test.rb
The API takes some of the same options as the command line interface. To watch all files recursively except files that matches *.rb and only wait for 0.1 seconds between each scan:
Filewatcher.new('**/*.*', exclude: '**/*.rb', interval: 0.1).watch do |changes|
changes.each do |filename, event|
puts filename
end
end
Use patterns to match filenames in current directory and subdirectories. The pattern is not a regular expression; instead it follows rules similar to shell filename globbing. See Ruby documentation for syntax.
Filewatcher.new(['*.rb', '*.xml']).watch do |changes|
changes.each do |filename, _event|
puts "Updated #{filename}"
end
end
Start, pause, resume, stop, and finalize a running watch. This is particularly useful when the update block takes a while to process each file (e.g. sending over the network).
filewatcher = Filewatcher.new(['*.rb'])
thread = Thread.new(filewatcher) { |fw| fw.watch { |changes| p changes } }
# ...
filewatcher.pause # block stops responding to file system changes
filewatcher.finalize # Ensure all file system changes made prior to
# pausing are handled.
# ...
filewatcher.resume # block begins responding again, but is not given
# changes made between #pause_watch and
# #resume_watch
# ...
filewatcher.stop # block stops responding to file system changes
# and takes a final snapshot of the file system
thread.join
filewatcher.finalize # Ensure all file system changes made prior to
# ending the watch are handled.
If basename, relative filename or absolute filename is necessary
use the standard pathname
like this:
require 'pathname'
Filewatcher.new(['**/*.*']).watch do |changes|
changes.each do |filename, event|
path = Pathname.new(filename)
puts "Basename : #{path.basename}"
puts "Relative filename: #{File.join('.', path)}"
puts "Absolute filename: #{path.realpath}"
end
end
You can require plugins for Filewatcher, which extends core functionality.
Example:
require 'filewatcher'
require 'filewatcher-spinner'
# With the `true` value of option there will be an ASCII spinner in the STDOUT while waiting changes
Filewatcher.new('lib/', spinner: true).watch do |changes|
changes.each do |filename, event|
puts "#{filename} #{event}"
end
end
Available methods are:
#after_initialize
#before_pause_sleep
#before_resume_sleep
#after_stop
#finalizing
If you have questions, problems or suggestions about plugins system — please, don't hesitate to create a new issue.
Official plugins:
Changelog can be found in an adjacent file.
This project would not be where it is today without the generous help provided by people reporting issues and these contributors:
-
Thomas Flemming: Original author. Restart option. Exported variables.
-
Penn Taylor: Spinner displayed in the terminal and Start, pause, resume, stop, and finalize a running watch.
-
Franco Leonardo Bulgarelli: Support for absolute and globbed paths.
-
Kristoffer Roupé: Command line globbing.
-
Alexander Popov: Plugin system, daemon mode (CLI), tests improvements, code style improvements, many other fixes and improvements.
This gem was initially inspired by Tom Lieber's blog posting (Web Archive version).
- Fork the project.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don't break it in a future version unintentionally.
- Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
- Send me a pull request. Bonus points for topic branches.
This section is for maintainers.
Use toys gem release 9.8.7
for releasing new versions.
It'll make changes to the version.rb
file,
CHANGELOG.md
(with confirmation, feel free to edit before continue),
make a git commit, a git tag, push them, build a gem, push the gem.
Copyright (c) 2010 - 2024 Thomas Flemming. See LICENSE for details.