filemonitor 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +30 -0
- data/Manifest.txt +2 -5
- data/README.markdown +57 -16
- data/lib/FileMonitor.rb +80 -45
- data/lib/FileMonitor/store.rb +1 -2
- data/spec/FileMonitor_spec.rb +131 -19
- data/tasks/rdoc.rake +9 -0
- metadata +9 -14
- data/History.txt +0 -16
- data/Rakefile +0 -25
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
data/CHANGELOG
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
=== 0.0.4 2010-02-17
|
2
|
+
* Major Enhancements/Fixes
|
3
|
+
* Automatically appends watched list with new files when they are detected if the watch that was added is a directory
|
4
|
+
* Removes deleted files from the watched files by default when that file is deleted, initiate the FileMonitor with :persistent=>true to override the new behavior.
|
5
|
+
|
6
|
+
=== 0.0.3 2009-09-24
|
7
|
+
* Major Enhancements:
|
8
|
+
* Accept regexp filter when adding files: fm.add(path, regexp=/.*/, &block)
|
9
|
+
* Minor changes
|
10
|
+
* Change this file from History.txt to CHANGELOG
|
11
|
+
* Remove unused options hash from the process method
|
12
|
+
* Remove script/generate script/console, script/destroy & Rakefile from gem
|
13
|
+
* Fixup filemonitor.gemspec to work for github gem creation
|
14
|
+
|
15
|
+
=== 0.0.2 2009-09-24
|
16
|
+
|
17
|
+
* Major Enhancements:
|
18
|
+
* Various API Changes!
|
19
|
+
* RSpec Testing! Oh yah!
|
20
|
+
* RDocs! Oh yah!
|
21
|
+
* Reduced I/O load: Updated method of saving old file state. Changes are not
|
22
|
+
detectable if changed within 1 second of prior digest. If this is a problem
|
23
|
+
for anyone, I can make a md5 digest of the file optional.
|
24
|
+
* Handle duplicates file entries by overwriting the old with the new
|
25
|
+
* Add start / stop & halt methods for handling spawns
|
26
|
+
|
27
|
+
=== 0.0.1 2009-09-15
|
28
|
+
|
29
|
+
* 1 major enhancement:
|
30
|
+
* Initial release
|
data/Manifest.txt
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
|
1
|
+
CHANGELOG
|
2
2
|
Manifest.txt
|
3
3
|
README.markdown
|
4
|
-
Rakefile
|
5
4
|
lib/FileMonitor.rb
|
6
5
|
lib/FileMonitor/store.rb
|
7
|
-
script/console
|
8
|
-
script/destroy
|
9
|
-
script/generate
|
10
6
|
spec/FileMonitor_spec.rb
|
11
7
|
spec/MonitoredItem_spec.rb
|
12
8
|
spec/spec.opts
|
13
9
|
spec/spec_helper.rb
|
14
10
|
tasks/rspec.rake
|
11
|
+
tasks/rdoc.rake
|
data/README.markdown
CHANGED
@@ -1,30 +1,71 @@
|
|
1
1
|
# FileMonitor
|
2
|
-
|
3
|
-
* Joshaven Potter: <yourtech@gmail.com>
|
2
|
+
Calls a Proc when a watched file is changed.
|
4
3
|
|
4
|
+
* Documentation: http://filemonitor.rubyforge.org/rdoc
|
5
|
+
* Code Repository: http://github.com/joshaven/FileMonitor
|
6
|
+
* Joshaven Potter: yourtech@gmail.com
|
5
7
|
|
6
|
-
##
|
8
|
+
## Notice
|
9
|
+
This project has only been tested on posix compatible systems, sorry for you windows users. If you get this
|
10
|
+
to run under windows, let me know and I'll release a windows compatible version.
|
7
11
|
|
8
|
-
|
12
|
+
## Installation
|
13
|
+
gem install filemonitor # you may need to run: sudo gem install filemonitor
|
9
14
|
|
10
|
-
##
|
11
|
-
|
12
|
-
|
15
|
+
## Examples
|
16
|
+
In the following examples, the block passed will be executed when the file changes. These examples only
|
17
|
+
echo strings but I am sure you'll find something more useful to do in the callback proc.
|
13
18
|
|
14
|
-
## Usage
|
15
19
|
require 'filemonitor'
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
puts "you probably should handle the change in #{i.file}"
|
20
|
+
|
21
|
+
# Example 1: Generate a FileMonitor subprocess with one command:
|
22
|
+
fm = FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") do |watched_item|
|
23
|
+
puts "Change detected in #{watched_item.path} "
|
21
24
|
end
|
22
|
-
# run a background process that watches for changed files... which is cloesed along with the parent process.
|
23
|
-
file_spy.spawn
|
24
25
|
|
26
|
+
# thats it... if you want to stop the subprocess, issue: fm.stop
|
27
|
+
|
28
|
+
# Example 2: Generate a manually run FileMonitor:
|
29
|
+
file_spy = FileMonitor.new {|f| puts "Default proc called because #{f.path} changed."}
|
30
|
+
file_spy.add(Dir.pwd) do |f|
|
31
|
+
puts "Monitored Item's proc called because #{f.path} changed."
|
32
|
+
end
|
33
|
+
|
34
|
+
file_spy.process # This will look for changes one time... call this again when you want to find changes.
|
35
|
+
# if you want to launch a background process then do file_spy.spawn or its alias: file_spy.start
|
25
36
|
|
26
|
-
|
37
|
+
### Context Examples
|
38
|
+
Both the FileMonitor.new method and a FileMonitor instance fm.add method can accept a block with 0 to 2 arities.
|
39
|
+
See Also the api docs: <http://filemonitor.rubyforge.org/rdoc>
|
27
40
|
|
41
|
+
This block is working in the context in which it was created:
|
42
|
+
|
43
|
+
changes = false
|
44
|
+
fm.add(Dir.pwd) {changes = true} # The 'changes' variable will be set to true when a change is detected
|
45
|
+
fm.process
|
46
|
+
respond_to_file_changes if changes
|
47
|
+
|
48
|
+
This block is working in the context created and is aware of the watched file instance 'f'. The watched file will
|
49
|
+
respond to :path, :callback, & :digest and is an instance of: MonitoredItems::Store
|
50
|
+
|
51
|
+
changed_ruby_files = []
|
52
|
+
fm.add(Dir.pwd, /\.rb$/) do |f| # Adds all .rb files recursively starting with the working directory
|
53
|
+
changed_ruby_files << f.path # The changed_ruby_files will contain the path of the changed files.
|
54
|
+
end
|
55
|
+
fm.process
|
56
|
+
handel_changes(changed_ruby_files) # Do somehting with the changed files
|
57
|
+
changed_ruby_files = [] # Cleanup changes so the array doesn't keep growing
|
58
|
+
|
59
|
+
|
60
|
+
This block is working in the context created and is aware of the watched_file instance 'f' as well as the
|
61
|
+
file_monitor instance 'fm'. The fm object can be acted on within the block the same way it is outside the block,
|
62
|
+
an example use of this may be to add all files in a folder when a file is changed...
|
63
|
+
|
64
|
+
fm.add(Dir.pwd) do |f, fm|
|
65
|
+
fm.add(APP_ROOT) if f.path == (APP_ROOT + Manifest.txt) # Start watching any new files when added to to the manifest.txt file.
|
66
|
+
end
|
67
|
+
|
68
|
+
## License
|
28
69
|
(The MIT License)
|
29
70
|
|
30
71
|
Copyright (c) 2009 Joshaven Potter
|
data/lib/FileMonitor.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/FileMonitor/store'
|
1
|
+
require ::File.dirname(__FILE__) + '/FileMonitor/store'
|
2
2
|
require 'find' # needed for the :files_recursive method
|
3
|
-
|
4
3
|
# Purpose:
|
5
4
|
# Watches the file system for changes
|
6
5
|
#
|
@@ -25,11 +24,11 @@ require 'find' # needed for the :files_recursive method
|
|
25
24
|
# # Alternatively you can do all of the above in one line:
|
26
25
|
# FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") do |watched_item| ... end
|
27
26
|
class FileMonitor
|
28
|
-
VERSION = '0.0.
|
27
|
+
VERSION = '0.0.4'
|
29
28
|
attr_accessor :callback, :pid, :watched
|
30
29
|
# The new method may be called with an optional callback which must be a block
|
31
30
|
# either do...end or {...}.
|
32
|
-
# The block may
|
31
|
+
# The block may consist of upto two arguments ie {|watched_item, monitored|}, in which case, the
|
33
32
|
# watched_item is an instance of FileMonitor::Store and monitored is the FileMonitor instances self.
|
34
33
|
#
|
35
34
|
# The first argument of the block, 'watched_item' in this case, will respond to: (:path, :modified & :callback).
|
@@ -37,21 +36,22 @@ class FileMonitor
|
|
37
36
|
#
|
38
37
|
# Example:
|
39
38
|
# FileMonitor.new do |watched_item|
|
40
|
-
# puts "
|
41
|
-
# puts "When I find a change I will call watched_item.callback"
|
39
|
+
# puts "My file name & path is: #{watched_item.path}"
|
40
|
+
# puts "When I find a change I will call watched_item.callback which displays this text."
|
42
41
|
# end
|
43
42
|
#
|
44
43
|
# FileMonitor.new do |watched_item, monitored|
|
45
|
-
#
|
46
|
-
# # add files from a file that is a list of files to watch... Note: There
|
44
|
+
# # Add files from a file that is a list of files to watch... Note: There
|
47
45
|
# IO.readlines(watched_item.path).each {|file| monitored << file } if watched_item.path == '/path/to/file/watchme.list'
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# open(watched_item) { |f| puts '' }
|
46
|
+
# # Clear watchme.list so we won't add all watch files every time the file changes
|
47
|
+
# open(watched_item) { |f| puts "This is the callback that is run..." }
|
51
48
|
# end
|
52
|
-
def initialize(&callback)
|
49
|
+
def initialize(options={}, &callback)
|
50
|
+
@options=options
|
51
|
+
# @options[:persistent] ||= false
|
53
52
|
@watched = []
|
54
|
-
@callback = callback
|
53
|
+
@callback = callback if callback.is_a? Proc
|
54
|
+
@options[:rescan_directories] ||= true
|
55
55
|
end
|
56
56
|
|
57
57
|
# Returns a spawned FileMonitor instance. The independent process automatically calls the given
|
@@ -71,7 +71,8 @@ class FileMonitor
|
|
71
71
|
|
72
72
|
# The add method accepts a directory path or file path and optional callback. If a directory path is given all files in that
|
73
73
|
# path are recursively added. If a callback is given then that proc will be called when a change is detected on that file or
|
74
|
-
# group of files. If no proc is given via the add method then the object callback is called.
|
74
|
+
# group of files. If no proc is given via the add method then the object callback is called. If a regexp is given as the
|
75
|
+
# second argument only files matching the regexp will be monitored.
|
75
76
|
#
|
76
77
|
# Example:
|
77
78
|
# fm = FileMonitor.new do |path|
|
@@ -81,18 +82,25 @@ class FileMonitor
|
|
81
82
|
# # The following will run the default callback when changes are found in the /tmp folder:
|
82
83
|
# fm.add '/tmp'
|
83
84
|
#
|
84
|
-
# # The following will run
|
85
|
-
# fm.add
|
85
|
+
# # The following will run the given callback on any files ending in 'txt' in the /home folder when changed:
|
86
|
+
# fm.add('/home', /txt$/) do |path|
|
86
87
|
# puts "A users file has changed: #{path}"
|
87
88
|
# end
|
88
|
-
def add(path, &callback)
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
def add(path, regexp_file_filter=/.*/, &callback)
|
90
|
+
callback = @callback unless callback.is_a? Proc
|
91
|
+
# path = ::File.expand_path(path)
|
92
|
+
if ::File.file?(path) && regexp_file_filter === ::File.split(path).last
|
93
|
+
# Bail out if the file is already being watched.
|
94
|
+
return true if index_of(path)
|
95
|
+
index = @watched.size
|
96
|
+
@watched[index] = MonitoredItems::Store.new({:path=>::File.expand_path(path), :callback=>callback, :digest=>digest(path)})
|
92
97
|
return true
|
93
|
-
elsif File.directory? path
|
94
|
-
files_recursive(path).each
|
98
|
+
elsif ::File.directory? path
|
99
|
+
files_recursive(path, regexp_file_filter, &callback).each do |f|
|
100
|
+
add f, regexp_file_filter, &callback
|
101
|
+
end
|
95
102
|
return true
|
103
|
+
else
|
96
104
|
end
|
97
105
|
false
|
98
106
|
end
|
@@ -106,8 +114,8 @@ class FileMonitor
|
|
106
114
|
#
|
107
115
|
# # The following will run the default callback when changes are found in the /tmp folder:
|
108
116
|
# fm << '/tmp'
|
109
|
-
def <<(path)
|
110
|
-
add path
|
117
|
+
def <<(path, regexp_file_filter=/.*/)
|
118
|
+
add path, regexp_file_filter
|
111
119
|
end
|
112
120
|
|
113
121
|
# Itterates watched files and runs callbacks when changes are detected. This is the semi-automatic way to run the FileMonitor.
|
@@ -117,13 +125,21 @@ class FileMonitor
|
|
117
125
|
# fm = FileMonitor.new() {|watched_item| changed_files = watched_item.path}
|
118
126
|
# fm << '/tmp'
|
119
127
|
# fm.process # this will look for changes in any watched items only once... call this when you want to look for changes.
|
120
|
-
def process
|
128
|
+
def process
|
129
|
+
scan_directories if @options[:rescan_directories]
|
130
|
+
|
121
131
|
@watched.each do |i|
|
122
|
-
|
123
|
-
#
|
132
|
+
# Unless the persistant option is set, this will remove watched file if it has been removed
|
133
|
+
# if the file still exists then it will be processed regardless of the persistent option.
|
134
|
+
unless @options[:persistent] || ::File.exists?(i.path)
|
135
|
+
@watched.delete(i)
|
136
|
+
else
|
137
|
+
key = digest(i.path)
|
138
|
+
# i.digest = key if i.digest.nil? # skip first change detection, its always unknown on first run
|
124
139
|
|
125
|
-
|
126
|
-
|
140
|
+
unless i.digest == key
|
141
|
+
respond_to_change(i, key)
|
142
|
+
end
|
127
143
|
end
|
128
144
|
end
|
129
145
|
end
|
@@ -186,17 +202,17 @@ class FileMonitor
|
|
186
202
|
# fm << @app_root + '/lib'
|
187
203
|
# fm.spawn # and now its doing its job...
|
188
204
|
def spawn(interval = 1)
|
189
|
-
if pid.nil?
|
205
|
+
if @pid.nil?
|
190
206
|
@pid = fork {monitor interval}
|
191
207
|
Process.detach(pid)
|
192
208
|
|
193
209
|
Kernel.at_exit do
|
194
210
|
# sends the kill command unless the pid is not found on the system
|
195
|
-
Process.kill('HUP', pid) if process_running?
|
196
|
-
pid = nil
|
211
|
+
Process.kill('HUP', @pid) if process_running?
|
212
|
+
@pid = nil
|
197
213
|
end
|
198
214
|
end
|
199
|
-
pid
|
215
|
+
@pid
|
200
216
|
end
|
201
217
|
alias_method :start, :spawn
|
202
218
|
# Stops a spawned FileMonitor instance. The FileMonitor will finish the the currnet iteration and exit gracefully. See Also: Halt
|
@@ -209,9 +225,9 @@ class FileMonitor
|
|
209
225
|
# Send user defined signal USR1 to process. This is trapped in spauned processes and tells the process to Ctrl+C
|
210
226
|
# The user defined signial is sent as a safty percausion because the process id is not tracked through a pid file
|
211
227
|
# nor compared with the running command. The FileMonitor spaun will respond to the USR1 signal by exiting properly.*
|
212
|
-
if Fixnum === pid
|
213
|
-
Process.kill('USR1', pid)
|
214
|
-
Process.wait pid
|
228
|
+
if Fixnum === @pid
|
229
|
+
Process.kill('USR1', @pid)
|
230
|
+
Process.wait @pid
|
215
231
|
end
|
216
232
|
end
|
217
233
|
|
@@ -222,12 +238,15 @@ class FileMonitor
|
|
222
238
|
# fm.spawn # and now its doing its job...
|
223
239
|
# fm.stop
|
224
240
|
def halt()
|
225
|
-
if Fixnum === pid
|
226
|
-
Process.kill('USR2', pid)
|
227
|
-
Process.wait pid
|
241
|
+
if Fixnum === @pid
|
242
|
+
Process.kill('USR2', @pid)
|
243
|
+
Process.wait @pid
|
228
244
|
end
|
229
245
|
end
|
230
246
|
|
247
|
+
def directories #:nodoc:
|
248
|
+
@directories ||= []
|
249
|
+
end
|
231
250
|
private
|
232
251
|
# Returns true or false
|
233
252
|
def process_running?
|
@@ -235,11 +254,11 @@ private
|
|
235
254
|
end
|
236
255
|
|
237
256
|
# Call callback and update digest with given key.
|
238
|
-
def respond_to_change(item, key
|
257
|
+
def respond_to_change(item, key)
|
239
258
|
if Proc === item.callback # Use watched instance callback if possible.
|
240
|
-
call item.callback, item, self
|
259
|
+
call item.callback, item, self
|
241
260
|
elsif Proc === self.callback # Use object level callback if possible.
|
242
|
-
call self.callback, item, self
|
261
|
+
call self.callback, item, self
|
243
262
|
end
|
244
263
|
item.digest(key)
|
245
264
|
end
|
@@ -263,7 +282,7 @@ private
|
|
263
282
|
# Returns a string representation of the file state.
|
264
283
|
def digest(file)
|
265
284
|
begin
|
266
|
-
File.mtime(file).to_f
|
285
|
+
::File.mtime(file).to_f
|
267
286
|
rescue Errno::ENOENT
|
268
287
|
nil
|
269
288
|
end
|
@@ -272,12 +291,13 @@ private
|
|
272
291
|
# Returns an array of all files in dirname recursively. Accepts an optional file name regexp filter.
|
273
292
|
# The following will find files ending in ".rb" recursively beginning in the working dir:
|
274
293
|
# files_recursive Dir.pwd, /\.rb$/
|
275
|
-
def files_recursive(dirname, file_name_regexp
|
294
|
+
def files_recursive(dirname, file_name_regexp=/.*/, &callback)
|
276
295
|
paths = []
|
277
296
|
|
278
297
|
Find.find(dirname) do |path|
|
279
298
|
if FileTest.directory?(path)
|
280
|
-
|
299
|
+
directories << MonitoredItems::Store.new({:path => ::File.expand_path(path), :file_name_regexp => file_name_regexp, :callback=>callback})
|
300
|
+
::Find.prune if ::File.basename(path)[0] == ?. # Don't look any further into directies beginning with a dot.
|
281
301
|
else
|
282
302
|
paths << path if file_name_regexp === path # Amend the return array if the file found matches the regexp
|
283
303
|
end
|
@@ -285,4 +305,19 @@ private
|
|
285
305
|
|
286
306
|
return paths
|
287
307
|
end
|
308
|
+
|
309
|
+
# Attempts to add all files in all watched directories that match the watching filter, the add method is responcibale
|
310
|
+
# for managing duplicates.
|
311
|
+
def scan_directories
|
312
|
+
self.directories.each do |dir|
|
313
|
+
::Dir.new(dir.path).each do |file|
|
314
|
+
unless file == '.' || file == '..'
|
315
|
+
if dir.file_name_regexp===file
|
316
|
+
puts dir.callback.class
|
317
|
+
add ::File.join(dir.path, file), dir.file_name_regexp, &dir.callback
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
288
323
|
end
|
data/lib/FileMonitor/store.rb
CHANGED
@@ -2,7 +2,6 @@ module MonitoredItems #:nodoc:
|
|
2
2
|
class Nothing #:nodoc:
|
3
3
|
end
|
4
4
|
|
5
|
-
|
6
5
|
# A Store object is much like a hash but instead of getting an setting keys you get and set instance variables.
|
7
6
|
# The following examples are using methods used by the FileMonitor object, however the store object is not limited to these methods.
|
8
7
|
#
|
@@ -23,7 +22,7 @@ module MonitoredItems #:nodoc:
|
|
23
22
|
def initialize(hsh = {})
|
24
23
|
hsh.map {|k,v| self.send(k, v)} if Hash === hsh
|
25
24
|
end
|
26
|
-
|
25
|
+
|
27
26
|
# Gets or sets instance variables based upon the methods called.
|
28
27
|
def method_missing(mth, arg=Nothing)
|
29
28
|
# append the @ symbol and remove the equal symbol (if exists):
|
data/spec/FileMonitor_spec.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
# Time to add your specs!
|
4
|
-
# http://rspec.info/
|
5
3
|
describe "FileMonitor" do
|
6
4
|
before :all do
|
7
5
|
@app_root = File.expand_path(File.dirname(__FILE__)+"/..")
|
8
6
|
end
|
9
|
-
|
7
|
+
|
10
8
|
it 'should be able to instantize' do
|
11
9
|
fm = FileMonitor.new
|
12
10
|
FileMonitor.should === fm
|
@@ -53,56 +51,65 @@ describe "FileMonitor" do
|
|
53
51
|
fm = FileMonitor.new &proc1
|
54
52
|
(fm << @app_root + '/lib/FileMonitor.rb').should be_true # Should add single file
|
55
53
|
(fm.add @app_root + '/lib/FileMonitor/store.rb', &proc2).should be_true # Should add single file
|
56
|
-
fm.watched.first.callback.should
|
54
|
+
fm.watched.first.callback.should == proc1
|
57
55
|
fm.watched.last.callback.should == proc2
|
58
|
-
fm.watched.last.callback.should_not == proc1
|
59
56
|
end
|
60
57
|
|
61
|
-
it 'should
|
58
|
+
it 'should not duplicate files' do
|
62
59
|
fm = FileMonitor.new
|
63
|
-
|
64
|
-
|
65
|
-
|
60
|
+
filename = @app_root + '/lib/FileMonitor.rb'
|
61
|
+
|
62
|
+
3.times do
|
63
|
+
fm << filename
|
64
|
+
end
|
66
65
|
fm.watched.size.should == 1
|
67
|
-
end
|
66
|
+
end
|
68
67
|
|
69
68
|
it 'should spawn processes' do
|
70
69
|
fm = FileMonitor.new
|
70
|
+
@app_root = "/Users/joshaven/projects/FileMonitor"
|
71
|
+
|
71
72
|
fm << @app_root + '/lib'
|
72
73
|
pid = fm.spawn
|
73
74
|
pid.should_not == Process.pid
|
74
|
-
`ps -p #{fm.spawn} -o 'pid ppid'
|
75
|
+
`ps -p #{fm.spawn} -o 'pid ppid'`.split().should == ["PID", "PPID", pid.to_s, Process.pid.to_s]
|
76
|
+
fm.stop
|
75
77
|
end
|
76
|
-
|
78
|
+
|
77
79
|
it 'should stop spawn' do
|
78
80
|
fm = FileMonitor.new
|
79
81
|
fm << @app_root + '/lib'
|
80
82
|
pid = fm.spawn
|
81
|
-
|
83
|
+
sleep 1
|
84
|
+
# `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should == [pid.to_s, Process.pid.to_s]
|
85
|
+
`ps -p #{fm.spawn} -o 'pid ppid'`.split().should == ["PID", "PPID", pid.to_s, Process.pid.to_s]
|
82
86
|
t=Time.now
|
83
87
|
fm.stop
|
84
88
|
puts "Time to stop: #{Time.now-t}"
|
85
|
-
`ps -p #{fm.spawn} -o 'pid ppid'
|
89
|
+
`ps -p #{fm.spawn} -o 'pid ppid'`.split().should == ["PID", "PPID"]
|
86
90
|
end
|
87
91
|
|
88
92
|
it 'should halt spawn' do
|
89
93
|
fm = FileMonitor.new
|
90
94
|
fm << @app_root + '/lib'
|
91
95
|
pid = fm.spawn
|
92
|
-
|
96
|
+
sleep 1
|
97
|
+
# `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should == [pid.to_s, Process.pid.to_s]
|
98
|
+
`ps -p #{fm.spawn} -o 'pid ppid'`.split().should == ["PID", "PPID", pid.to_s, Process.pid.to_s]
|
93
99
|
t=Time.now
|
94
100
|
fm.halt
|
95
101
|
puts "Time to halt: #{Time.now-t}"
|
96
|
-
`ps -p #{fm.spawn} -o 'pid ppid'
|
102
|
+
`ps -p #{fm.spawn} -o 'pid ppid'`.split().should == ["PID", "PPID"]
|
97
103
|
end
|
98
|
-
|
104
|
+
|
99
105
|
it 'should setup & spawn using the when_modified class method' do
|
100
106
|
fm = FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") {|watched_item| true}
|
101
107
|
fm.callback.arity.should == 1
|
102
108
|
fm.pid.should > 1
|
103
109
|
fm.pid.should == fm.spawn
|
110
|
+
fm.stop
|
104
111
|
end
|
105
|
-
|
112
|
+
|
106
113
|
it 'should run callback on change' do
|
107
114
|
changed_files = nil
|
108
115
|
filename = @app_root + '/spec/temp.txt'
|
@@ -159,14 +166,119 @@ describe "FileMonitor" do
|
|
159
166
|
File.delete filename
|
160
167
|
end
|
161
168
|
|
169
|
+
it 'should not remove a file from the watch list if set to persistent when the file is deleted' do
|
170
|
+
changed_files = []
|
171
|
+
filename = @app_root + '/spec/temp.txt'
|
172
|
+
File.open(filename, 'w') {|f| f.write('Hello World') }
|
173
|
+
|
174
|
+
fm = FileMonitor.new({:persistent => true}) {|watched_item| changed_files << watched_item.path}
|
175
|
+
fm << filename
|
176
|
+
|
177
|
+
File.delete filename
|
178
|
+
File.exists?(filename).should be_false
|
179
|
+
fm.process.should be_true
|
180
|
+
fm.watched.size.should == 1
|
181
|
+
fm.watched.first.path.should == filename
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should remove a file from the watch list if not set to persistent when the file is deleted' do
|
185
|
+
changed_files = []
|
186
|
+
filename1 = @app_root + '/spec/temp1.txt'
|
187
|
+
filename2 = @app_root + '/spec/temp2.txt'
|
188
|
+
File.open(filename1, 'w') {|f| f.write('Hello World 1') }
|
189
|
+
File.open(filename2, 'w') {|f| f.write('Hello World 2') }
|
190
|
+
|
191
|
+
fm = FileMonitor.new() {|watched_item| changed_files << watched_item.path}
|
192
|
+
fm << filename1
|
193
|
+
fm << filename2
|
194
|
+
|
195
|
+
fm.watched.size.should == 2
|
196
|
+
File.delete filename2
|
197
|
+
File.exists?(filename2).should be_false
|
198
|
+
fm.process.should be_true
|
199
|
+
fm.watched.size.should == 1
|
200
|
+
fm.watched.first.path.should == filename1
|
201
|
+
File.delete filename1
|
202
|
+
end
|
203
|
+
|
162
204
|
it 'should handel missing files without throwing errors' do
|
163
205
|
filename = @app_root + '/spec/temp.txt'
|
164
206
|
File.open(filename, 'w') {|f| f.write('hello') }
|
165
|
-
fm = FileMonitor.new() {true}
|
207
|
+
fm = FileMonitor.new(:persistent => true) {true}
|
166
208
|
fm << filename
|
167
209
|
fm.process
|
168
210
|
File.delete(filename)
|
169
211
|
fm.process
|
170
212
|
fm.watched.first.digest.should be_nil
|
171
213
|
end
|
214
|
+
|
215
|
+
it 'should be able to use a filter when specifying files' do
|
216
|
+
fm = FileMonitor.new() {|watched_item| changed_files = watched_item.path}
|
217
|
+
fm.add(@app_root, /\.rb$/) {|f| puts "Change found in: #{f.path}"}
|
218
|
+
fm.watched.each do |i|
|
219
|
+
File.split(i.path).last.should =~ /\.rb$/
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should add new files when they are created if FileMonitor is watching a directory' do
|
224
|
+
fm = FileMonitor.new
|
225
|
+
# Create a test directory and watch it
|
226
|
+
testing_dir = @app_root + '/spec/testing'
|
227
|
+
filename = testing_dir + '/temp.txt'
|
228
|
+
File.delete filename if File.exists?(filename)
|
229
|
+
|
230
|
+
Dir.mkdir testing_dir unless File.directory? testing_dir
|
231
|
+
fm << testing_dir
|
232
|
+
|
233
|
+
fm.watched.should be_empty
|
234
|
+
|
235
|
+
File.open(filename, 'w') {|f| f.write('hello') }
|
236
|
+
|
237
|
+
# The new file should be watched after the fm.process has run
|
238
|
+
fm.process
|
239
|
+
fm.watched.should_not be_empty
|
240
|
+
|
241
|
+
# Cleanup
|
242
|
+
File.delete filename
|
243
|
+
Dir.delete testing_dir
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should not crawl through directories when a file is specified' do
|
247
|
+
fm = FileMonitor.new
|
248
|
+
fm.add(File.join(@app_root, 'CHANGELOG'))
|
249
|
+
fm.directories.should be_empty
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should crawl through directories looking for files when a directory is specified' do
|
253
|
+
fm = FileMonitor.new
|
254
|
+
fm.add(@app_root, /.*\.rb/)
|
255
|
+
fm.directories.size.should > 5
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'should not loose the regexp filter when storing watched directories' do
|
259
|
+
fm = FileMonitor.new
|
260
|
+
fm.add(@app_root, /.*\.rb/)
|
261
|
+
fm.directories.each {|d| d.file_name_regexp.should == /.*\.rb/}
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'should not have inflating directories' do
|
265
|
+
fm = FileMonitor.new
|
266
|
+
fm.add(@app_root, /.*\.rb/)
|
267
|
+
start = fm.directories.size
|
268
|
+
fm.process
|
269
|
+
fm.directories.size.should == start
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should not loose callbacks' do
|
273
|
+
puts "<br />Starting should not loose callbacks test<br /><br />"
|
274
|
+
callback = Proc.new {return "hello from callback"}
|
275
|
+
fm = FileMonitor.new &callback
|
276
|
+
|
277
|
+
fm.add @app_root, /.*\.rb/
|
278
|
+
|
279
|
+
fm.directories.size.should > 1
|
280
|
+
fm.directories.each {|dir| dir.callback.should == callback}
|
281
|
+
fm.watched.size.should > 1
|
282
|
+
fm.watched.each {|file| file.callback.should == callback}
|
283
|
+
end
|
172
284
|
end
|
data/tasks/rdoc.rake
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filemonitor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshaven Potter
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-02-20 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 2.3.3
|
24
24
|
version:
|
25
|
-
description:
|
25
|
+
description: ""
|
26
26
|
email:
|
27
27
|
- yourtech@gmail.com
|
28
28
|
executables: []
|
@@ -30,31 +30,26 @@ executables: []
|
|
30
30
|
extensions: []
|
31
31
|
|
32
32
|
extra_rdoc_files:
|
33
|
-
- History.txt
|
34
33
|
- Manifest.txt
|
35
34
|
files:
|
36
|
-
-
|
35
|
+
- CHANGELOG
|
37
36
|
- Manifest.txt
|
38
37
|
- README.markdown
|
39
|
-
- Rakefile
|
40
38
|
- lib/FileMonitor.rb
|
41
39
|
- lib/FileMonitor/store.rb
|
42
|
-
- script/console
|
43
|
-
- script/destroy
|
44
|
-
- script/generate
|
45
40
|
- spec/FileMonitor_spec.rb
|
46
41
|
- spec/MonitoredItem_spec.rb
|
47
42
|
- spec/spec.opts
|
48
43
|
- spec/spec_helper.rb
|
49
44
|
- tasks/rspec.rake
|
45
|
+
- tasks/rdoc.rake
|
50
46
|
has_rdoc: true
|
51
|
-
homepage: http://github.com/joshaven/FileMonitor
|
47
|
+
homepage: http://github.com/joshaven/FileMonitor/
|
52
48
|
licenses: []
|
53
49
|
|
54
50
|
post_install_message:
|
55
|
-
rdoc_options:
|
56
|
-
|
57
|
-
- README.markdown
|
51
|
+
rdoc_options: []
|
52
|
+
|
58
53
|
require_paths:
|
59
54
|
- lib
|
60
55
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -75,6 +70,6 @@ rubyforge_project: filemonitor
|
|
75
70
|
rubygems_version: 1.3.5
|
76
71
|
signing_key:
|
77
72
|
specification_version: 3
|
78
|
-
summary:
|
73
|
+
summary: Calls a Proc when a watched file is changed.
|
79
74
|
test_files: []
|
80
75
|
|
data/History.txt
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
=== 0.0.2 2009-09-15
|
2
|
-
|
3
|
-
* Major Enhancements:
|
4
|
-
* Various API Changes!
|
5
|
-
* RSpec Testing! Oh yah!
|
6
|
-
* RDocs! Oh yah!
|
7
|
-
* Reduced I/O load: Updated method of saving old file state. Changes are not
|
8
|
-
detectable if changed within 1 second of prior digest. If this is a problem
|
9
|
-
for anyone, I can make a md5 digest of the file optional.
|
10
|
-
* Handle duplicates file entries by overwriting the old with the new
|
11
|
-
* Add start / stop & halt methods for handling spawns
|
12
|
-
|
13
|
-
=== 0.0.1 2009-09-15
|
14
|
-
|
15
|
-
* 1 major enhancement:
|
16
|
-
* Initial release
|
data/Rakefile
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'hoe', '>= 2.1.0'
|
3
|
-
require 'hoe'
|
4
|
-
require 'fileutils'
|
5
|
-
require './lib/FileMonitor'
|
6
|
-
|
7
|
-
Hoe.plugin :newgem
|
8
|
-
# Hoe.plugin :website
|
9
|
-
# Hoe.plugin :cucumberfeatures
|
10
|
-
|
11
|
-
# Generate all the Rake tasks
|
12
|
-
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
-
$hoe = Hoe.spec 'filemonitor' do
|
14
|
-
self.developer 'Joshaven Potter', 'yourtech@gmail.com'
|
15
|
-
self.rubyforge_name = self.name # TODO this is default value
|
16
|
-
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
17
|
-
self.readme_file = "README.markdown"
|
18
|
-
end
|
19
|
-
|
20
|
-
require 'newgem/tasks'
|
21
|
-
Dir['tasks/**/*.rake'].each { |t| load t }
|
22
|
-
|
23
|
-
# TODO - want other tests/tasks run by default? Add them to the list
|
24
|
-
# remove_task :default
|
25
|
-
# task :default => [:spec, :features]
|
data/script/console
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# File: script/console
|
3
|
-
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
-
|
5
|
-
libs = " -r irb/completion"
|
6
|
-
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
-
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
-
libs << " -r #{File.dirname(__FILE__) + '/../lib/FileMonitor.rb'}"
|
9
|
-
puts "Loading FileMonitor gem"
|
10
|
-
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'rubigen'
|
6
|
-
rescue LoadError
|
7
|
-
require 'rubygems'
|
8
|
-
require 'rubigen'
|
9
|
-
end
|
10
|
-
require 'rubigen/scripts/destroy'
|
11
|
-
|
12
|
-
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
-
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
-
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'rubigen'
|
6
|
-
rescue LoadError
|
7
|
-
require 'rubygems'
|
8
|
-
require 'rubigen'
|
9
|
-
end
|
10
|
-
require 'rubigen/scripts/generate'
|
11
|
-
|
12
|
-
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
-
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
-
RubiGen::Scripts::Generate.new.run(ARGV)
|