filemonitor 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
- History.txt
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
- * http://github.com/joshaven/FileMonitor
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
- ## DESCRIPTION
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
- Watches the file system for changes.
12
+ ## Installation
13
+ gem install filemonitor # you may need to run: sudo gem install filemonitor
9
14
 
10
- ## Notice
11
- This project has only been tested on posix compatible systems, sorry for
12
- you windows users. If you get this to run under windows, let me know and I'll release a windows compatible version.
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
- file_spy = FileMonitor.new
17
- # Add one or more files or directories with a callback block
18
- file_spy.add(Dir.pwd) do |i|
19
- # This callback will be executed in the context of a single file, even if you added a directory
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
- ## License
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.2'
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 consiste of upto arguments ie {|watched_item, monitored|}, in which case, the
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 "I am watched this file: #{watched_item.path}"
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
- # # clear watchme.list so we won't add all watch files every time the file changes
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 unless callback.nil?
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 its own callback on changed files in the /home folder:
85
- # fm.add '/home' do |path|
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
- if File.file? path
90
- index = index_of(path) || @watched.size
91
- @watched[index] = MonitoredItems::Store.new({:path=>File.expand_path(path), :callback=>callback, :digest=>digest(path)})
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 {|f| add(f, &callback) }
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(options={})
128
+ def process
129
+ scan_directories if @options[:rescan_directories]
130
+
121
131
  @watched.each do |i|
122
- key = digest(i.path)
123
- # i.digest = key if i.digest.nil? # skip first change detection, its always unknown on first run
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
- unless i.digest == key
126
- respond_to_change(i, key, options)
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, options)
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, options
259
+ call item.callback, item, self
241
260
  elsif Proc === self.callback # Use object level callback if possible.
242
- call self.callback, item, self, options
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
- Find.prune if File.basename(path)[0] == ?. # Don't look any further into directies beginning with a dot.
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
@@ -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):
@@ -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 be_nil
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 overwrite existing file watches with successive additions' do
58
+ it 'should not duplicate files' do
62
59
  fm = FileMonitor.new
63
- fm.watched.size.should == 0
64
- (fm << @app_root + '/lib/FileMonitor.rb').should be_true # Should add single file
65
- (fm << @app_root + '/lib/FileMonitor.rb').should be_true # Should add single file
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'|grep ^[0-9]`.split().should == [pid.to_s, Process.pid.to_s]
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
- `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should == [pid.to_s, Process.pid.to_s]
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'|grep ^[0-9]`.split().should be_empty
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
- `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should == [pid.to_s, Process.pid.to_s]
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'|grep ^[0-9]`.split().should be_empty
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
@@ -0,0 +1,9 @@
1
+ require 'rake/rdoctask'
2
+ require 'rdiscount'
3
+
4
+ Rake::RDocTask.new('rdoc') do |t|
5
+ t.rdoc_files.include('README.markdown', 'lib/**/*.rb')
6
+ t.main = 'README.markdown'
7
+ t.title = "FileMonitor API Documentation"
8
+ t.rdoc_dir = 'doc'
9
+ end
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.2
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: 2009-09-24 00:00:00 -04:00
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: Watches the file system for changes.
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
- - History.txt
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
- - --main
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: Watches the file system for changes.
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)