0.21
No release in over a year
Detect changes in file system. Works anywhere.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.0
~> 0.6.0
~> 0.12.1
~> 3.8
~> 1.39.0
~> 0.21.0
~> 0.14.2

Runtime

 Project Readme

Filewatcher

Gem Version Build Status Codecov Depfu Code Climate License

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.

Installation

$ gem install filewatcher

or with bundler:

# Gemfile
gem 'filewatcher'

Usage

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

Plugins

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

Changelog can be found in an adjacent file.

Credits

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).

Note on Patches/Pull Requests

  • 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.

Releasing

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

Copyright (c) 2010 - 2024 Thomas Flemming. See LICENSE for details.