Class: DirectoryWatcher::EmScanner

Inherits:
Scanner
  • Object
show all
Defined in:
lib/directory_watcher/em_scanner.rb

Overview

The EmScanner uses the EventMachine reactor loop to monitor changes to files in the watched directory. This scanner is more efficient than the pure Ruby scanner because it relies on the operating system kernel notifictions instead of a periodic polling and stat of every file in the watched directory (the technique used by the Scanner class).

EventMachine cannot notify us when a file is added to the watched directory; therefore, added files are only picked up when we apply the glob pattern to the directory. This is done at the configured interval.

Notes:

* Kqueue does not generate notifications when "touch" is used to update
  a file's timestamp. This applies to Mac and BSD systems.

* New files are detected only when the watched directory is polled at the
  configured interval.

Defined Under Namespace

Classes: Watcher

Instance Attribute Summary

Attributes inherited from Scanner

#files, #glob, #interval, #stable

Instance Method Summary collapse

Methods inherited from Scanner

#reset, #run_once

Constructor Details

#initialize(&block) ⇒ EmScanner

call-seq:

EmScanner.new { |events| block }

Create an EventMachine based scanner that will generate file events and pass those events (as an array) to the given block.



39
40
41
42
43
44
# File 'lib/directory_watcher/em_scanner.rb', line 39

def initialize( &block )
  super(&block)
  @timer = nil
  @run_loop = lambda {_run_loop}
  @watchers = {}
end

Instance Method Details

#_event!(watcher) ⇒ Object

:stopdoc:

This callback is invoked by a Watcher instance when some event has occured on the file. The scanner determines if the file has been modified or deleted and notifies the directory watcher accordingly.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/directory_watcher/em_scanner.rb', line 113

def _event!( watcher )
  fn = watcher.path
  stat = watcher.stat

  if stat
    _watch_file fn unless watcher.active?
    @files[fn] = stat
    @events << ::DirectoryWatcher::Event.new(:modified, fn)
  else
    if watcher.active?
      watcher.stop_watching
      @watchers.delete fn
    end
    @files.delete fn
    @events << ::DirectoryWatcher::Event.new(:removed, fn)
  end

  notify
end

#join(limit = nil) ⇒ Object

call-seq:

join( limit = nil )

This is a no-op method for the EventMachine file scanner.



104
105
# File 'lib/directory_watcher/em_scanner.rb', line 104

def join( limit = nil )
end

#running?Boolean

Returns true if the scanner is currently running. Returns false if this is not the case.

Returns:

  • (Boolean)


49
50
51
# File 'lib/directory_watcher/em_scanner.rb', line 49

def running?
  !@timer.nil?
end

#startObject

Start the EventMachine scanner. If the scanner has already been started this method will return without taking any action.

If the EventMachine reactor is not running, it will be started by this method.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/directory_watcher/em_scanner.rb', line 59

def start
  return if running?

  unless EventMachine.reactor_running?
    @thread = Thread.new {EventMachine.run}
    Thread.pass until EventMachine.reactor_running?
  end

  @files.keys.each do |fn|
    if test ?e, fn
      _watch_file fn
      next
    end

    @files.delete fn
    @events << ::DirectoryWatcher::Event.new(:removed, fn)
  end

  _run_loop
end

#stopObject

Stop the EventMachine scanner. If the scanner is already stopped this method will return without taking any action.

The EventMachine reactor will not be stopped by this method. It is up to the user to stop the reactor using the EventMachine#stop_event_loop method.



87
88
89
90
91
92
93
94
95
96
97
# File 'lib/directory_watcher/em_scanner.rb', line 87

def stop
  return unless running?

  EventMachine.cancel_timer @timer rescue nil
  @timer = nil

  @watchers.each_value {|w| w.stop_watching if w.active?}
  @watchers.clear

  notify
end