class INotify::Notifier
notifier.run
# Nothing happens until you run the notifier!
end
puts “#{event.name} is no longer in the directory!”
# The #name field of the event object contains the name of the affected file
notifier.watch(“path/to/directory”, :delete, :moved_from) do |event|
# or moved out of the directory.
# Watch for any file in the directory being deleted
end
puts “Foo.txt was accessed!”
notifier.watch(“path/to/foo.txt”, :access) do
# Run this callback whenever the file path/to/foo.txt is read
notifier = INotify::Notifier.new
# Create the notifier
@example
but usually unnecessary.
It’s possible to have more than one instance,
Notifier wraps a single instance of inotify.
def self.supports_ruby_io?
-
(Boolean)
- Whether or not this Ruby implementation supports
def self.supports_ruby_io? RUBY_PLATFORM !~ /java/ end
def close
-
(SystemCallError)
- if closing the underlying file descriptor fails.
def close stop if Native.close(@fd) == 0 @watchers.clear return end raise SystemCallError.new("Failed to properly close inotify socket" + case FFI.errno when Errno::EBADF::Errno; ": invalid or closed file descriptior" when Errno::EIO::Errno; ": an I/O error occured" end, FFI.errno) end
def initialize
-
(SystemCallError)
- if inotify failed to initialize for some reason
Returns:
-
(Notifier)
-
def initialize @fd = Native.inotify_init @watchers = {} return unless @fd < 0 raise SystemCallError.new( "Failed to initialize inotify" + case FFI.errno when Errno::EMFILE::Errno; ": the user limit on the total number of inotify instances has been reached." when Errno::ENFILE::Errno; ": the system limit on the total number of file descriptors has been reached." when Errno::ENOMEM::Errno; ": insufficient kernel memory is available." else; "" end, FFI.errno) end
def process
- See: #run -
def process read_events.each do |event| event.callback! event.flags.include?(:ignored) && event.notifier.watchers.delete(event.watcher_id) end end
def read_events
This can return an empty list if the watcher was closed elsewhere.
objects.
has watchers registered for. Once there are events, returns their {Event}
Blocks until there are one or more filesystem events that this notifier
def read_events size = Native::Event.size + Native.fpathconf(fd, Native::Flags::PC_NAME_MAX) + 1 tries = 1 begin data = readpartial(size) rescue SystemCallError => er # EINVAL means that there's more data to be read # than will fit in the buffer size raise er unless er.errno == Errno::EINVAL::Errno && tries < 5 size *= 2 tries += 1 retry end return [] if data.nil? events = [] cookies = {} while event = Event.consume(data, self) events << event next if event.cookie == 0 cookies[event.cookie] ||= [] cookies[event.cookie] << event end cookies.each {|c, evs| evs.each {|ev| ev.related.replace(evs - [ev]).freeze}} events end
def readpartial(size)
def readpartial(size) # Use Ruby's readpartial if possible, to avoid blocking other threads. begin return to_io.readpartial(size) if self.class.supports_ruby_io? rescue Errno::EBADF, IOError # If the IO has already been closed, reading from it will cause # Errno::EBADF. In JRuby it can raise IOError with invalid or # closed file descriptor. return nil rescue IOError => ex return nil if ex.message =~ /stream closed/ raise end tries = 0 begin tries += 1 buffer = FFI::MemoryPointer.new(:char, size) size_read = Native.read(fd, buffer, size) return buffer.read_string(size_read) if size_read >= 0 end while FFI.errno == Errno::EINTR::Errno && tries <= 5 raise SystemCallError.new("Error reading inotify events" + case FFI.errno when Errno::EAGAIN::Errno; ": no data available for non-blocking I/O" when Errno::EBADF::Errno; ": invalid or closed file descriptor" when Errno::EFAULT::Errno; ": invalid buffer" when Errno::EINVAL::Errno; ": invalid file descriptor" when Errno::EIO::Errno; ": I/O error" when Errno::EISDIR::Errno; ": file descriptor is a directory" else; "" end, FFI.errno) end
def run
- See: #process -
def run @stop = false process until @stop end
def stop
That is, if we're in a \{#run} loop,
Stop watching for filesystem events.
def stop @stop = true end
def to_io
-
(NotImplementedError)
- if this is being called in JRuby
Returns:
-
(IO)
- An IO object wrapping the file descriptor
def to_io unless self.class.supports_ruby_io? raise NotImplementedError.new("INotify::Notifier#to_io is not supported under JRuby") end @io ||= IO.new(@fd) end
def watch(path, *flags, &callback)
-
(SystemCallError)
- if the file or directory can't be watched,
Returns:
-
(Watcher)
- A Watcher set up to watch this path for these events
Other tags:
- Yieldparam: event - The Event object containing information
Other tags:
- Yield: - A block that will be called
Parameters:
-
flags
(Array
) -- Which events to watch for -
path
(String
) -- The path to the file or directory
def watch(path, *flags, &callback) return Watcher.new(self, path, *flags, &callback) unless flags.include?(:recursive) dir = Dir.new(path) dir.each do |base| d = File.join(path, base) binary_d = d.respond_to?(:force_encoding) ? d.dup.force_encoding('BINARY') : d next if binary_d =~ /\/\.\.?$/ # Current or parent directory next if RECURSIVE_BLACKLIST.include?(d) next if flags.include?(:dont_follow) && File.symlink?(d) next if !File.directory?(d) watch(d, *flags, &callback) end dir.close rec_flags = [:create, :moved_to] return watch(path, *((flags - [:recursive]) | rec_flags)) do |event| callback.call(event) if flags.include?(:all_events) || !(flags & event.flags).empty? next if (rec_flags & event.flags).empty? || !event.flags.include?(:isdir) begin watch(event.absolute_name, *flags, &callback) rescue Errno::ENOENT # If the file has been deleted since the glob was run, we don't want to error out. end end end