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 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)