directory_watcher 1.4.1 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/History.txt +10 -0
- data/README.txt +1 -1
- data/Rakefile +16 -5
- data/lib/directory_watcher.rb +166 -139
- data/lib/directory_watcher/collector.rb +283 -0
- data/lib/directory_watcher/configuration.rb +228 -0
- data/lib/directory_watcher/coolio_scanner.rb +61 -127
- data/lib/directory_watcher/em_scanner.rb +81 -153
- data/lib/directory_watcher/event.rb +72 -0
- data/lib/directory_watcher/eventable_scanner.rb +242 -0
- data/lib/directory_watcher/file_stat.rb +65 -0
- data/lib/directory_watcher/logable.rb +26 -0
- data/lib/directory_watcher/notifier.rb +49 -0
- data/lib/directory_watcher/paths.rb +55 -0
- data/lib/directory_watcher/rev_scanner.rb +68 -131
- data/lib/directory_watcher/scan.rb +72 -0
- data/lib/directory_watcher/scan_and_queue.rb +22 -0
- data/lib/directory_watcher/scanner.rb +26 -209
- data/lib/directory_watcher/threaded.rb +277 -0
- data/lib/directory_watcher/version.rb +8 -0
- data/spec/directory_watcher_spec.rb +37 -0
- data/spec/paths_spec.rb +7 -0
- data/spec/scanner_scenarios.rb +236 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/utility_classes.rb +117 -0
- data/version.txt +1 -1
- metadata +123 -23
- data/bin/dw +0 -2
@@ -0,0 +1,55 @@
|
|
1
|
+
class DirectoryWatcher
|
2
|
+
# Paths contains helpful methods to determine paths of files inside the
|
3
|
+
# DirectoryWatcher library
|
4
|
+
#
|
5
|
+
module Paths
|
6
|
+
# The root directory of the project is considered the parent directory of
|
7
|
+
# the 'lib' directory.
|
8
|
+
#
|
9
|
+
# Returns The full expanded path of the parent directory of 'lib' going up
|
10
|
+
# the path from the current file. Trailing File::SEPARATOR is guaranteed
|
11
|
+
#
|
12
|
+
def root_dir
|
13
|
+
path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR)
|
14
|
+
lib_index = path_parts.rindex("lib")
|
15
|
+
return path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return a path relative to the 'lib' directory in this project
|
19
|
+
#
|
20
|
+
def lib_path(*args,&block)
|
21
|
+
sub_path('lib', *args, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return a path relative to the 'root' directory in the project
|
25
|
+
#
|
26
|
+
def path(*args,&block)
|
27
|
+
sub_path('', *args, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Calculate the full expanded path of the item with respect to a sub path of
|
31
|
+
# 'root_dir'
|
32
|
+
#
|
33
|
+
def sub_path(sub,*args,&block)
|
34
|
+
rv = ::File.join(root_dir, sub) + ::File::SEPARATOR
|
35
|
+
rv = ::File.join(rv, *args) if args
|
36
|
+
if block
|
37
|
+
with_load_path( rv ) do
|
38
|
+
rv = block.call
|
39
|
+
end
|
40
|
+
end
|
41
|
+
return rv
|
42
|
+
end
|
43
|
+
|
44
|
+
# Execute a block in the context of a path added to $LOAD_PATH
|
45
|
+
#
|
46
|
+
def with_load_path(path, &block)
|
47
|
+
$LOAD_PATH.unshift path
|
48
|
+
block.call
|
49
|
+
ensure
|
50
|
+
$LOAD_PATH.shift
|
51
|
+
end
|
52
|
+
|
53
|
+
extend self
|
54
|
+
end
|
55
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
begin
|
3
2
|
require 'rev'
|
4
3
|
DirectoryWatcher::HAVE_REV = true
|
@@ -8,177 +7,115 @@ end
|
|
8
7
|
|
9
8
|
if DirectoryWatcher::HAVE_REV
|
10
9
|
|
10
|
+
# Deprecated:
|
11
|
+
#
|
11
12
|
# The RevScanner uses the Rev loop to monitor changes to files in the
|
12
13
|
# watched directory. This scanner is more efficient than the pure Ruby
|
13
|
-
# scanner because it relies on the operating system kernel
|
14
|
+
# scanner because it relies on the operating system kernel notifications
|
14
15
|
# instead of a periodic polling and stat of every file in the watched
|
15
16
|
# directory (the technique used by the Scanner class).
|
16
17
|
#
|
17
|
-
|
18
|
-
|
18
|
+
# The RevScanner is essentially the exact same as the CoolioScanner with class
|
19
|
+
# names changed and using _rev_loop instead of _coolio_loop. Unfortunately the
|
20
|
+
# RevScanner cannot be a sub class of CoolioScanner because of C-extension
|
21
|
+
# reasons between the rev and coolio gems
|
22
|
+
#
|
23
|
+
# Rev cannot notify us when a file is added to the watched
|
24
|
+
# directory; therefore, added files are only picked up when we apply the
|
25
|
+
# glob pattern to the directory. This is done at the configured interval.
|
26
|
+
#
|
27
|
+
class DirectoryWatcher::RevScanner < ::DirectoryWatcher::EventableScanner
|
19
28
|
# call-seq:
|
20
|
-
# RevScanner.new
|
21
|
-
#
|
22
|
-
# Create a Rev based scanner that will generate file events and pass
|
23
|
-
# those events (as an array) to the given _block_.
|
29
|
+
# RevScanner.new( glob, interval, collection_queue )
|
24
30
|
#
|
25
|
-
def initialize(
|
26
|
-
super(
|
27
|
-
@watchers = {}
|
31
|
+
def initialize( glob, interval, collection_queue )
|
32
|
+
super(glob, interval, collection_queue)
|
28
33
|
end
|
29
34
|
|
30
|
-
#
|
31
|
-
# will
|
35
|
+
# Called by EventablScanner#start to start the loop up and attach the periodic
|
36
|
+
# timer that will poll the globs for new files.
|
32
37
|
#
|
33
|
-
def
|
34
|
-
return if
|
35
|
-
|
36
|
-
@
|
37
|
-
|
38
|
-
|
39
|
-
@files.keys.each do |fn|
|
40
|
-
if test ?e, fn
|
41
|
-
_watch_file fn
|
42
|
-
next
|
43
|
-
end
|
44
|
-
|
45
|
-
@files.delete fn
|
46
|
-
@events << ::DirectoryWatcher::Event.new(:removed, fn)
|
47
|
-
end
|
48
|
-
|
49
|
-
@timer.attach rev_loop
|
50
|
-
rev_loop.run
|
38
|
+
def start_loop_with_attached_scan_timer
|
39
|
+
return if @loop_thread
|
40
|
+
@timer = ScanTimer.new( self )
|
41
|
+
@loop_thread = Thread.new {
|
42
|
+
@timer.attach(event_loop)
|
43
|
+
event_loop.run
|
51
44
|
}
|
52
45
|
end
|
53
46
|
|
54
|
-
#
|
55
|
-
#
|
47
|
+
# Called by EventableScanner#stop to stop the loop as part of the shutdown
|
48
|
+
# process.
|
56
49
|
#
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
@watchers.each_value {|w| w.detach}
|
64
|
-
@watchers.clear
|
65
|
-
|
66
|
-
notify
|
67
|
-
|
68
|
-
@thread._rev_loop.stop rescue nil
|
69
|
-
@thread.kill # for some reason the rev loop is not returning after stopping
|
70
|
-
@thread = nil
|
50
|
+
def stop_loop
|
51
|
+
if @loop_thread then
|
52
|
+
event_loop.stop rescue nil
|
53
|
+
@loop_thread.kill
|
54
|
+
@loop_thread = nil
|
55
|
+
end
|
71
56
|
end
|
72
57
|
|
73
|
-
#
|
58
|
+
# Return the rev loop object
|
74
59
|
#
|
75
|
-
# This
|
76
|
-
#
|
77
|
-
# modified or deleted and notifies the directory watcher accordingly.
|
60
|
+
# This is used during the startup, shutdown process and for the Watcher to
|
61
|
+
# attach and detach from the event loop
|
78
62
|
#
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
if stat
|
84
|
-
if @files[fn] != stat
|
85
|
-
@files[fn] = stat
|
86
|
-
@events << ::DirectoryWatcher::Event.new(:modified, fn)
|
87
|
-
end
|
63
|
+
def event_loop
|
64
|
+
if @loop_thread then
|
65
|
+
@loop_thread._rev_loop
|
88
66
|
else
|
89
|
-
|
90
|
-
@watchers.delete fn
|
91
|
-
@files.delete fn
|
92
|
-
@events << ::DirectoryWatcher::Event.new(:removed, fn)
|
67
|
+
Thread.current._rev_loop
|
93
68
|
end
|
94
|
-
|
95
|
-
notify
|
96
|
-
end
|
97
|
-
|
98
|
-
# This callback is invoked by the Timer instance when it is triggered by
|
99
|
-
# the Rev loop. This method will check for added files and stable files
|
100
|
-
# and notify the directory watcher accordingly.
|
101
|
-
#
|
102
|
-
def _on_timer
|
103
|
-
_find_added
|
104
|
-
_find_stable
|
105
|
-
notify
|
106
69
|
end
|
107
|
-
# :startdoc:
|
108
|
-
|
109
|
-
|
110
|
-
private
|
111
70
|
|
112
|
-
#
|
113
|
-
# not currently watching and add them to the watch list. Generate "added"
|
114
|
-
# events for those newly found files.
|
71
|
+
# :stopdoc:
|
115
72
|
#
|
116
|
-
|
117
|
-
cur = list_files
|
118
|
-
prev = @files.keys
|
119
|
-
added = cur - prev
|
120
|
-
|
121
|
-
added.each do |fn|
|
122
|
-
@files[fn] = _watch_file(fn).stat
|
123
|
-
@events << ::DirectoryWatcher::Event.new(:added, fn)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# Iterate over the FileStat instances looking for those with non-nil
|
128
|
-
# stable counts. Decrement these counts and generate "stable" events for
|
129
|
-
# those files whose count reaches zero.
|
73
|
+
# Watch files using the Rev::StatWatcher.
|
130
74
|
#
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
@events << ::DirectoryWatcher::Event.new(:stable, fn)
|
137
|
-
stat.stable = nil
|
138
|
-
end
|
75
|
+
# The rev +on_change+ callback is converted to the appropriate +on_removed+
|
76
|
+
# and +on_modified+ callbacks for the EventableScanner.
|
77
|
+
class Watcher < ::Rev::StatWatcher
|
78
|
+
def self.watch(fn, scanner )
|
79
|
+
new(fn, scanner)
|
139
80
|
end
|
140
|
-
end
|
141
81
|
|
142
|
-
# Create and return a new Watcher instance for the given filename _fn_.
|
143
|
-
#
|
144
|
-
def _watch_file( fn )
|
145
|
-
w = Watcher.new(fn, self)
|
146
|
-
w.attach(@thread ? @thread._rev_loop : Thread.current._rev_loop)
|
147
|
-
@watchers[fn] = w
|
148
|
-
end
|
149
|
-
|
150
|
-
# :stopdoc:
|
151
|
-
#
|
152
|
-
class Watcher < Rev::StatWatcher
|
153
82
|
def initialize( fn, scanner )
|
154
|
-
|
83
|
+
# for file watching, we want to make sure this happens at a reasonable
|
84
|
+
# value, so set it to 0 if the scanner.interval is > 5 seconds. This will
|
85
|
+
# make it use the system value, and allow us to test.
|
86
|
+
i = scanner.interval < 5 ? scanner.interval : 0
|
87
|
+
super(fn, i)
|
155
88
|
@scanner = scanner
|
89
|
+
attach(scanner.event_loop)
|
156
90
|
end
|
157
91
|
|
92
|
+
# Rev uses on_change so we convert that to the appropriate
|
93
|
+
# EventableScanner calls. Unlike Coolio, Rev's on_change() takes no
|
94
|
+
# parameters
|
95
|
+
#
|
158
96
|
def on_change
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
::DirectoryWatcher::FileStat.new(stat.mtime, stat.size, @scanner.stable)
|
97
|
+
if File.exist?(path) then
|
98
|
+
@scanner.on_removed(self, ::DirectoryWatcher::FileStat.for_removed_path(path))
|
99
|
+
else
|
100
|
+
stat = File.stat(path)
|
101
|
+
@scanner.on_modified(self, ::DirectoryWatcher::FileStat.new(path, stat.mtime, stat.size))
|
102
|
+
end
|
166
103
|
end
|
167
104
|
end
|
168
105
|
|
169
|
-
|
106
|
+
# Periodically execute a Scan. Hook this into the EventableScanner#on_scan
|
107
|
+
#
|
108
|
+
class ScanTimer< Rev::TimerWatcher
|
170
109
|
def initialize( scanner )
|
171
110
|
super(scanner.interval, true)
|
172
111
|
@scanner = scanner
|
173
112
|
end
|
174
113
|
|
175
|
-
def on_timer
|
176
|
-
@scanner.
|
114
|
+
def on_timer( *args )
|
115
|
+
@scanner.on_scan
|
177
116
|
end
|
178
117
|
end
|
179
|
-
# :startdoc:
|
180
118
|
|
181
119
|
end # class DirectoryWatcher::RevScanner
|
182
|
-
end # if DirectoryWatcher::HAVE_REV
|
183
120
|
|
184
|
-
#
|
121
|
+
end # if DirectoryWatcher::HAVE_REV
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# A Scan is the scan of a full directory structure with the ability to iterate
|
2
|
+
# over the results, or return them as a full dataset
|
3
|
+
#
|
4
|
+
# results = Scan.new( globs ).run
|
5
|
+
#
|
6
|
+
class DirectoryWatcher::Scan
|
7
|
+
|
8
|
+
def initialize( globs = Array.new )
|
9
|
+
@globs = [ globs ].flatten
|
10
|
+
@results = Array.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# Run the entire scan and collect all the results. The Scan will only ever
|
14
|
+
# be run once.
|
15
|
+
#
|
16
|
+
# Return the array of FileStat results
|
17
|
+
def run
|
18
|
+
results
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return the results of the scan. If the scan has not been run yet, then run
|
22
|
+
# it
|
23
|
+
def results
|
24
|
+
@results = collect_all_stats if @results.empty?
|
25
|
+
return @results
|
26
|
+
end
|
27
|
+
|
28
|
+
#######
|
29
|
+
private
|
30
|
+
#######
|
31
|
+
|
32
|
+
# Collect all the Stats into an Array and return them
|
33
|
+
#
|
34
|
+
def collect_all_stats
|
35
|
+
r = []
|
36
|
+
each { |stat| r << stat }
|
37
|
+
return r
|
38
|
+
end
|
39
|
+
|
40
|
+
# Iterate over each glob, yielding it
|
41
|
+
#
|
42
|
+
def each_glob( &block )
|
43
|
+
@globs.each do |glob|
|
44
|
+
yield glob
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Iterate over each item that matches the glob.
|
49
|
+
# The item yielded is a ::DirectoryWatcher::FileStat object.
|
50
|
+
#
|
51
|
+
def each( &block )
|
52
|
+
each_glob do |glob|
|
53
|
+
Dir.glob(glob).each do |fn|
|
54
|
+
if stat = file_stat( fn ) then
|
55
|
+
yield stat if block_given?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return the stat of of the file in question. If the item is not a file,
|
62
|
+
# then return the value of the passed in +if_not_file+
|
63
|
+
#
|
64
|
+
def file_stat( fn, if_not_file = false )
|
65
|
+
stat = File.stat fn
|
66
|
+
return if_not_file unless stat.file?
|
67
|
+
return DirectoryWatcher::FileStat.new( fn, stat.mtime, stat.size )
|
68
|
+
rescue SystemCallError => e
|
69
|
+
# swallow
|
70
|
+
$stderr.puts "Error Stating #{fn} : #{e}"
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# ScanAndQueue creates a Scan from its input globs and then sends that Scan to
|
2
|
+
# its Queue.
|
3
|
+
#
|
4
|
+
# Every time scan_and_queue is called a new scan is created an sent to the
|
5
|
+
# queue.
|
6
|
+
class DirectoryWatcher::ScanAndQueue
|
7
|
+
|
8
|
+
def initialize( glob, queue )
|
9
|
+
@globs = glob
|
10
|
+
@queue =queue
|
11
|
+
end
|
12
|
+
|
13
|
+
# Create and run a Scan and submit it to the Queue.
|
14
|
+
#
|
15
|
+
# Returns the Scan that was run
|
16
|
+
def scan_and_queue
|
17
|
+
scan = ::DirectoryWatcher::Scan.new( @globs )
|
18
|
+
scan.run
|
19
|
+
@queue.enq scan
|
20
|
+
return scan
|
21
|
+
end
|
22
|
+
end
|
@@ -1,8 +1,6 @@
|
|
1
|
-
|
2
1
|
# The Scanner is responsible for polling the watched directory at a regular
|
3
|
-
# interval and generating
|
4
|
-
#
|
5
|
-
# registered observers.
|
2
|
+
# interval and generating a Scan which it will then send down the collection
|
3
|
+
# queue to the Collector.
|
6
4
|
#
|
7
5
|
# The Scanner is a pure Ruby class, and as such it works across all Ruby
|
8
6
|
# interpreters on the major platforms. This also means that it can be
|
@@ -10,220 +8,39 @@
|
|
10
8
|
# intervals. Your mileage will vary, but it is something to keep an eye on.
|
11
9
|
#
|
12
10
|
class DirectoryWatcher::Scanner
|
13
|
-
|
14
|
-
|
15
|
-
attr_accessor :interval
|
16
|
-
attr_accessor :stable
|
17
|
-
attr_accessor :files
|
18
|
-
|
19
|
-
# call-seq:
|
20
|
-
# Scanner.new { |events| block }
|
21
|
-
#
|
22
|
-
# Create a thread-based scanner that will generate file events and pass
|
23
|
-
# those events (as an array) to the given _block_.
|
24
|
-
#
|
25
|
-
def initialize( &block )
|
26
|
-
@events = []
|
27
|
-
@thread = nil
|
28
|
-
@notify = block;
|
29
|
-
end
|
30
|
-
|
31
|
-
# Returns +true+ if the scanner is currently running. Returns +false+ if
|
32
|
-
# this is not the case.
|
33
|
-
#
|
34
|
-
def running?
|
35
|
-
!@thread.nil?
|
36
|
-
end
|
37
|
-
|
38
|
-
# Start the scanner thread. If the scanner is already running, this method
|
39
|
-
# will return without taking any action.
|
40
|
-
#
|
41
|
-
def start
|
42
|
-
return if running?
|
43
|
-
|
44
|
-
@stop = false
|
45
|
-
@thread = Thread.new(self) {|scanner| scanner.__send__ :run_loop}
|
46
|
-
self
|
47
|
-
end
|
48
|
-
|
49
|
-
# Stop the scanner thread. If the scanner is already stopped, this method
|
50
|
-
# will return without taking any action.
|
51
|
-
#
|
52
|
-
def stop
|
53
|
-
return unless running?
|
54
|
-
|
55
|
-
@stop = true
|
56
|
-
@thread.wakeup if @thread.status == 'sleep'
|
57
|
-
@thread.join
|
58
|
-
self
|
59
|
-
ensure
|
60
|
-
@thread = nil
|
61
|
-
end
|
62
|
-
|
63
|
-
# call-seq:
|
64
|
-
# reset( pre_load = false )
|
65
|
-
#
|
66
|
-
# Reset the scanner state by clearing the stored file list. Passing +true+
|
67
|
-
# to this method will cause the file list to be pre-loaded after it has
|
68
|
-
# been cleared effectively skipping the initial round of file added events
|
69
|
-
# that would normally be generated.
|
70
|
-
#
|
71
|
-
def reset( pre_load = false )
|
72
|
-
@events.clear
|
73
|
-
@files = (pre_load ? scan_files : Hash.new)
|
74
|
-
end
|
75
|
-
|
76
|
-
# call-seq:
|
77
|
-
# join( limit = nil )
|
78
|
-
#
|
79
|
-
# If the scanner thread is running, the calling thread will suspend
|
80
|
-
# execution and run the scanner thread. This method does not return until
|
81
|
-
# the scanner thread is stopped or until _limit_ seconds have passed.
|
82
|
-
#
|
83
|
-
# If the scanner thread is not running, this method returns immediately
|
84
|
-
# with +nil+.
|
85
|
-
#
|
86
|
-
def join( limit = nil )
|
87
|
-
return unless running?
|
88
|
-
@thread.join limit
|
89
|
-
end
|
90
|
-
|
91
|
-
# Performs exactly one scan of the directory for file changes and notifies
|
92
|
-
# the observers.
|
93
|
-
#
|
94
|
-
def run_once
|
95
|
-
files = scan_files
|
96
|
-
keys = [files.keys, @files.keys] # current files, previous files
|
97
|
-
|
98
|
-
find_added(files, *keys)
|
99
|
-
find_modified(files, *keys)
|
100
|
-
find_removed(*keys)
|
101
|
-
|
102
|
-
notify
|
103
|
-
@files = files # store the current file list for the next iteration
|
104
|
-
self
|
105
|
-
end
|
106
|
-
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
# Using the configured glob pattern, scan the directory for all files and
|
111
|
-
# return a hash with the filenames as keys and +FileStat+ objects as the
|
112
|
-
# values. The +FileStat+ objects contain the mtime and size of the file.
|
113
|
-
#
|
114
|
-
def scan_files
|
115
|
-
files = {}
|
116
|
-
@glob.each do |glob|
|
117
|
-
Dir.glob(glob).each do |fn|
|
118
|
-
begin
|
119
|
-
stat = File.stat fn
|
120
|
-
next unless stat.file?
|
121
|
-
files[fn] = ::DirectoryWatcher::FileStat.new(stat.mtime, stat.size)
|
122
|
-
rescue SystemCallError; end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
files
|
126
|
-
end
|
127
|
-
|
128
|
-
# Using the configured glob pattern, scan the directory for all files and
|
129
|
-
# return an array of the filenames found.
|
130
|
-
#
|
131
|
-
def list_files
|
132
|
-
files = []
|
133
|
-
@glob.each do |glob|
|
134
|
-
Dir.glob(glob).each {|fn| files << fn if test ?f, fn}
|
135
|
-
end
|
136
|
-
files
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
# Calling this method will enter the scanner's run loop. The
|
141
|
-
# calling thread will not return until the +stop+ method is called.
|
142
|
-
#
|
143
|
-
# The run loop is responsible for scanning the directory for file changes,
|
144
|
-
# and then dispatching events to registered listeners.
|
145
|
-
#
|
146
|
-
def run_loop
|
147
|
-
until @stop
|
148
|
-
start = Time.now.to_f
|
149
|
-
|
150
|
-
run_once
|
151
|
-
|
152
|
-
nap_time = @interval - (Time.now.to_f - start)
|
153
|
-
sleep nap_time if nap_time > 0
|
154
|
-
end
|
155
|
-
end
|
11
|
+
include DirectoryWatcher::Threaded
|
12
|
+
include DirectoryWatcher::Logable
|
156
13
|
|
157
14
|
# call-seq:
|
158
|
-
#
|
15
|
+
# Scanner.new( configuration )
|
159
16
|
#
|
160
|
-
#
|
161
|
-
# previously, _prev_, figure out which files have been added and generate
|
162
|
-
# a new file added event for each.
|
17
|
+
# From the Configuration instance passed in Scanner uses:
|
163
18
|
#
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
@events << ::DirectoryWatcher::Event.new(:added, fn)
|
169
|
-
end
|
170
|
-
self
|
171
|
-
end
|
172
|
-
|
173
|
-
# call-seq:
|
174
|
-
# find_removed( cur, prev )
|
19
|
+
# glob - Same as that in DirectoryWatcher
|
20
|
+
# interval - Same as that in DirectoryWatcher
|
21
|
+
# collection_queue - The Queue to send the Scans too.
|
22
|
+
# the other end of this queue is connected to a Collector
|
175
23
|
#
|
176
|
-
#
|
177
|
-
#
|
178
|
-
# generate a new file removed event for each.
|
24
|
+
# The Scanner is not generally used out side of a DirectoryWatcher so this is
|
25
|
+
# more of an internal API
|
179
26
|
#
|
180
|
-
def
|
181
|
-
|
182
|
-
|
183
|
-
|
27
|
+
#def initialize( glob, interval, collection_queue )
|
28
|
+
def initialize( config )
|
29
|
+
@config = config
|
30
|
+
@scan_and_queue = ::DirectoryWatcher::ScanAndQueue.new( @config.glob, @config.collection_queue )
|
184
31
|
end
|
185
32
|
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
|
190
|
-
|
191
|
-
# if any have been modified. Generate a new file modified event for each
|
192
|
-
# modified file. Also, by looking at the stable count in the _files_ hash,
|
193
|
-
# figure out if any files have become stable since being added or modified.
|
194
|
-
# Generate a new stable event for each stabilized file.
|
195
|
-
#
|
196
|
-
def find_modified( files, cur, prev )
|
197
|
-
(cur & prev).each do |key|
|
198
|
-
cur_stat, prev_stat = files[key], @files[key]
|
199
|
-
|
200
|
-
# if the modification time or the file size differs from the last
|
201
|
-
# time it was seen, then create a :modified event
|
202
|
-
if cur_stat != prev_stat
|
203
|
-
@events << ::DirectoryWatcher::Event.new(:modified, key)
|
204
|
-
cur_stat.stable = @stable
|
205
|
-
|
206
|
-
# otherwise, if the count is not nil see if we need to create a
|
207
|
-
# :stable event
|
208
|
-
elsif !prev_stat.stable.nil?
|
209
|
-
cur_stat.stable = prev_stat.stable - 1
|
210
|
-
if cur_stat.stable <= 0
|
211
|
-
@events << ::DirectoryWatcher::Event.new(:stable, key)
|
212
|
-
cur_stat.stable = nil
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
self
|
33
|
+
# Set the interval before starting the loop.
|
34
|
+
# This allows for interval to be set AFTER the DirectoryWatcher instance is
|
35
|
+
# allocated but before it is started.
|
36
|
+
def before_starting
|
37
|
+
self.interval = @config.interval
|
217
38
|
end
|
218
39
|
|
219
|
-
#
|
220
|
-
#
|
221
|
-
# end of this method call.
|
40
|
+
# Performs exactly one scan of the directory and sends the
|
41
|
+
# results to the Collector
|
222
42
|
#
|
223
|
-
def
|
224
|
-
@
|
225
|
-
ensure
|
226
|
-
@events.clear
|
43
|
+
def run
|
44
|
+
@scan_and_queue.scan_and_queue
|
227
45
|
end
|
228
|
-
|
229
|
-
end # class DirectoryWatcher::Scanner
|
46
|
+
end
|