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?

Returns:
  • (Boolean) - Whether or not this Ruby implementation supports
def self.supports_ruby_io?
  RUBY_PLATFORM !~ /java/
end

def close

Raises:
  • (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

Raises:
  • (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

Other tags:
    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

{#run} or {#process} are ususally preferable to calling this directly.

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)

Same as IO#readpartial, or as close as we need.
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

Other tags:
    See: #process -
def run
  @stop = false
  process until @stop
end

def stop

exit out as soon as we finish handling the events.
That is, if we're in a \{#run} loop,
Stop watching for filesystem events.
def stop
  @stop = true
end

def to_io

Raises:
  • (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)

Raises:
  • (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