filemonitor 0.0.1 → 0.0.2

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/History.txt CHANGED
@@ -1,4 +1,16 @@
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
+
1
13
  === 0.0.1 2009-09-15
2
14
 
3
15
  * 1 major enhancement:
4
- * Initial release
16
+ * Initial release
data/Manifest.txt CHANGED
@@ -3,10 +3,12 @@ Manifest.txt
3
3
  README.markdown
4
4
  Rakefile
5
5
  lib/FileMonitor.rb
6
+ lib/FileMonitor/store.rb
6
7
  script/console
7
8
  script/destroy
8
9
  script/generate
9
10
  spec/FileMonitor_spec.rb
11
+ spec/MonitoredItem_spec.rb
10
12
  spec/spec.opts
11
13
  spec/spec_helper.rb
12
14
  tasks/rspec.rake
data/README.markdown CHANGED
@@ -5,7 +5,11 @@
5
5
 
6
6
  ## DESCRIPTION
7
7
 
8
- Watches the file system for changes.
8
+ Watches the file system for changes.
9
+
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.
9
13
 
10
14
  ## Usage
11
15
  require 'filemonitor'
@@ -0,0 +1,45 @@
1
+ module MonitoredItems #:nodoc:
2
+ class Nothing #:nodoc:
3
+ end
4
+
5
+
6
+ # A Store object is much like a hash but instead of getting an setting keys you get and set instance variables.
7
+ # The following examples are using methods used by the FileMonitor object, however the store object is not limited to these methods.
8
+ #
9
+ # Example:
10
+ #
11
+ # s = MonitoredItems::Store.new
12
+ # s.path = '/tmp' # => "/tmp"
13
+ # s.callback Proc.new {'Hello World'} # => #<Proc:0x0000000100317508@(irb):18>
14
+ # s.callback.call # => "Hello World"
15
+ # s.path # => "/tmp"
16
+ #
17
+ # # OR send a hash when initializing the store
18
+ # i = MonitoredItems::Store.new({:path => '/tmp'})
19
+ # i.path #=> "/tmp"
20
+ class Store
21
+ # Supports initialization with a hash of methods & values. It makes no difference if
22
+ # the keys of the hash are strings or symbols, but they are case sensitive.
23
+ def initialize(hsh = {})
24
+ hsh.map {|k,v| self.send(k, v)} if Hash === hsh
25
+ end
26
+
27
+ # Gets or sets instance variables based upon the methods called.
28
+ def method_missing(mth, arg=Nothing)
29
+ # append the @ symbol and remove the equal symbol (if exists):
30
+ mth = "@#{mth}".chomp('=').to_sym
31
+ # get or set instnace variable
32
+ arg == Nothing ? self.instance_variable_get(mth) : self.instance_variable_set(mth, arg)
33
+ end
34
+
35
+ # Return Hash representation of Store object
36
+ def to_h
37
+ Hash[*self.instance_variables.collect {|m| [m.slice(1..-1).to_sym, self.instance_variable_get(m)] }.flatten]
38
+ end
39
+
40
+ # Return inspection of hash representation of Store object
41
+ def to_s
42
+ self.to_h.inspect
43
+ end
44
+ end
45
+ end
data/lib/FileMonitor.rb CHANGED
@@ -1,111 +1,288 @@
1
+ require File.dirname(__FILE__) + '/FileMonitor/store'
2
+ require 'find' # needed for the :files_recursive method
3
+
1
4
  # Purpose:
2
5
  # Watches the file system for changes
3
6
  #
4
7
  # Usage:
5
- # > require 'file_monitor'
6
- # > file_spy = FileMonitor.new
7
- # > file_spy.add(Dir.pwd) do |i|
8
- # > puts "you probably should handle the change in #{i.file}"
9
- # > end
10
- # > file_spy.spawn
11
-
12
- require 'digest/md5'
13
-
14
- $:.unshift(File.dirname(__FILE__)) unless
15
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
16
-
17
- module HashAccess
18
- def method_missing(mth, *args)
19
- return args.empty? ? self[mth] : self[mth.to_s.chomp('=').to_sym]=args.first
20
- end
21
- end
22
-
8
+ #
9
+ # require 'filemonitor'
10
+ #
11
+ # # Create a FileMonitor instance and assign the callback to the entire object.
12
+ # # In the following example, "watched_item" is a MonitoredItems::Store, see the new
13
+ # # method for a better example of working in your block.
14
+ # file_spy = FileMonitor.new do |watched_item| ... end
15
+ #
16
+ # # Any files in the working directory (Dir.pwd) and its sub-directories will
17
+ # # be watched for changes. If a change is found the callback assigned above
18
+ # # will be enacted.
19
+ # file_spy << Dir.pwd
20
+ # file_spy << "/path/to/other/file.rb"
21
+ #
22
+ # # launch an independent process to do the monitoring:
23
+ # file_spy.spawn
24
+ #
25
+ # # Alternatively you can do all of the above in one line:
26
+ # FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") do |watched_item| ... end
23
27
  class FileMonitor
24
- VERSION = '0.0.1'
25
- attr_accessor :spawns
26
- def initialize()
28
+ VERSION = '0.0.2'
29
+ attr_accessor :callback, :pid, :watched
30
+ # The new method may be called with an optional callback which must be a block
31
+ # either do...end or {...}.
32
+ # The block may consiste of upto arguments ie {|watched_item, monitored|}, in which case, the
33
+ # watched_item is an instance of FileMonitor::Store and monitored is the FileMonitor instances self.
34
+ #
35
+ # The first argument of the block, 'watched_item' in this case, will respond to: (:path, :modified & :callback).
36
+ # The second argument of the block, 'monitored' in this case, will respond any FileMonitor method.
37
+ #
38
+ # Example:
39
+ # 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"
42
+ # end
43
+ #
44
+ # FileMonitor.new do |watched_item, monitored|
45
+ #
46
+ # # add files from a file that is a list of files to watch... Note: There
47
+ # 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 '' }
51
+ # end
52
+ def initialize(&callback)
27
53
  @watched = []
54
+ @callback = callback unless callback.nil?
28
55
  end
29
-
30
- def add(item, &callback)
31
- if File.file? item
32
- h = {:file=>File.expand_path(item), :callback=>callback}
33
- h.extend(HashAccess)
34
- @watched << h
35
- elsif File.directory? item
36
- files_recursive(item).each {|f| add(f, &callback) }
56
+
57
+ # Returns a spawned FileMonitor instance. The independent process automatically calls the given
58
+ # callback when changes are found.
59
+ #
60
+ # Example:
61
+ # fm = FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") {|watched_item, file_monitor| ... }
62
+ # fm.pid # => 23994
63
+ # fm.callback.nil? # => false
64
+ # fm.watched.size # => 28
65
+ def self.when_modified(*paths, &callback)
66
+ fm = FileMonitor.new &callback
67
+ paths.each {|path| fm << path}
68
+ fm.spawn
69
+ return fm
70
+ end
71
+
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
+ # 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.
75
+ #
76
+ # Example:
77
+ # fm = FileMonitor.new do |path|
78
+ # puts "Detected a change on #{path}"
79
+ # end
80
+ #
81
+ # # The following will run the default callback when changes are found in the /tmp folder:
82
+ # fm.add '/tmp'
83
+ #
84
+ # # The following will run its own callback on changed files in the /home folder:
85
+ # fm.add '/home' do |path|
86
+ # puts "A users file has changed: #{path}"
87
+ # 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)})
92
+ return true
93
+ elsif File.directory? path
94
+ files_recursive(path).each {|f| add(f, &callback) }
95
+ return true
37
96
  end
97
+ false
38
98
  end
39
-
40
- def changes?
41
- return first_changed
99
+
100
+ # The '<<' method works the same way as the 'add' method but does not support a callback.
101
+ #
102
+ # Example:
103
+ # fm = FileMonitor.new do |path|
104
+ # puts "Detected a change on #{path}"
105
+ # end
106
+ #
107
+ # # The following will run the default callback when changes are found in the /tmp folder:
108
+ # fm << '/tmp'
109
+ def <<(path)
110
+ add path
42
111
  end
43
112
 
44
- def process
45
- # itterates watched files and runs callbacks when changes are detected.
113
+ # Itterates watched files and runs callbacks when changes are detected. This is the semi-automatic way to run the FileMonitor.
114
+ #
115
+ # Example:
116
+ # changed_files = []
117
+ # fm = FileMonitor.new() {|watched_item| changed_files = watched_item.path}
118
+ # fm << '/tmp'
119
+ # 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={})
46
121
  @watched.each do |i|
47
- key = digest(i.file)
48
- i.digest = key if i.digest.nil? # skip first change detection, its always unknown on first run
49
- update(i, key) unless i.digest == key
122
+ key = digest(i.path)
123
+ # i.digest = key if i.digest.nil? # skip first change detection, its always unknown on first run
124
+
125
+ unless i.digest == key
126
+ respond_to_change(i, key, options)
127
+ end
50
128
  end
51
129
  end
52
130
 
53
- def watching
54
- # Returns an Array of files being watched
55
- @watched
56
- end
57
-
131
+ # Runs an endless loop watching for changes. It will sleep for the given interval between looking for changes. This method
132
+ # is intended to be run in a subprocess or threaded environment. The spawn method calls this method and takes care of
133
+ # the forking and pid management for you.
134
+ #
135
+ # Example:
136
+ # fm = FileMonitor.new
137
+ # fm << '/tmp'
138
+ # fm.monitor
139
+ # puts "will not get here unless a signal is sent to the process which interrupts the loop."
58
140
  def monitor(interval = 1)
59
141
  trap("INT") do
60
- STDERR.puts " Interrupted by Control-C"
61
- exit 2
142
+ puts " FileMonitor was interrupted by Control-C... exiting gracefully"
143
+ # exit
144
+ @shutdown = true
145
+ end
146
+
147
+ trap("USR1") do
148
+ puts " FileMonitor was asked nicely to stop."
149
+ @shutdown = true
150
+ pid = nil
62
151
  end
63
152
 
153
+ trap("USR2") do
154
+ puts " FileMonitor was halted."
155
+ pid = nil
156
+ exit
157
+ end
158
+
159
+
64
160
  while true
161
+ exit if @shutdown
65
162
  process
66
- sleep interval
163
+ sleep interval unless @shutdown
67
164
  end
68
165
  end
69
166
 
167
+ # Returns index of watched item or false if non existant.
168
+ #
169
+ # Example:
170
+ # fm = FileMonitor.new
171
+ # fm << '/tmp/first.txt'
172
+ # fm << '/tmp/second.txt'
173
+ # fm.index_of '/tmp/first.txt' # => 0
174
+ # fm.index_of '/tmp/first.txt' # => 1
175
+ # fm.index_of '/tmp/woops.txt' # => false
176
+ def index_of(path)
177
+ watched.each_with_index {|watched,i| return i if watched.path == path}
178
+ false
179
+ end
180
+
181
+ # Spauns a child process that is looking for changes at every given interval.
182
+ # The interval is in seconds and defaults to 1 second.
183
+ #
184
+ # Example:
185
+ # fm = FileMonitor.new {|watched_item| puts 'do something when file is changed'}
186
+ # fm << @app_root + '/lib'
187
+ # fm.spawn # and now its doing its job...
70
188
  def spawn(interval = 1)
71
- if @spawns.nil?
72
- @spawns = fork {monitor interval}
73
- Process.detach(@spawns)
189
+ if pid.nil?
190
+ @pid = fork {monitor interval}
191
+ Process.detach(pid)
192
+
74
193
  Kernel.at_exit do
75
- Process.kill('HUP', @spawns)
76
- @spawns = nil
194
+ # sends the kill command unless the pid is not found on the system
195
+ Process.kill('HUP', pid) if process_running?
196
+ pid = nil
77
197
  end
78
- true
79
- else
80
- @spawns
81
198
  end
82
- @spawns ||= []
199
+ pid
200
+ end
201
+ alias_method :start, :spawn
202
+ # Stops a spawned FileMonitor instance. The FileMonitor will finish the the currnet iteration and exit gracefully. See Also: Halt
203
+ #
204
+ # Example:
205
+ # fm = FileMonitor.new {|watched_item| puts 'do something when file is changed'}
206
+ # fm.spawn # and now its doing its job...
207
+ # fm.stop
208
+ def stop()
209
+ # Send user defined signal USR1 to process. This is trapped in spauned processes and tells the process to Ctrl+C
210
+ # The user defined signial is sent as a safty percausion because the process id is not tracked through a pid file
211
+ # 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
215
+ end
216
+ end
217
+
218
+ # Halts a spawned FileMonitor Instance. The current iteration will be halted in its tracks. See Also: stop
219
+ #
220
+ # Example:
221
+ # fm = FileMonitor.new {|watched_item| puts 'do something when file is changed'}
222
+ # fm.spawn # and now its doing its job...
223
+ # fm.stop
224
+ def halt()
225
+ if Fixnum === pid
226
+ Process.kill('USR2', pid)
227
+ Process.wait pid
228
+ end
83
229
  end
230
+
84
231
  private
85
- def update(item, key)
86
- item.callback.call(item) unless item.callback.nil?
87
- item.digest= key
232
+ # Returns true or false
233
+ def process_running?
234
+ pid ? !`ps -p #{pid}|grep ^[0-9]`.split().empty? : false
88
235
  end
89
236
 
90
- def first_changed
91
- # returns first changed item or false
92
- @watched.each {|i| return i if changed? i}
93
- false
237
+ # Call callback and update digest with given key.
238
+ def respond_to_change(item, key, options)
239
+ if Proc === item.callback # Use watched instance callback if possible.
240
+ call item.callback, item, self, options
241
+ elsif Proc === self.callback # Use object level callback if possible.
242
+ call self.callback, item, self, options
243
+ end
244
+ item.digest(key)
245
+ end
246
+
247
+ # Calls a proc with no more then the needed arguments. This avoids an error when
248
+ # calling a proc with two arguments when the proc only handels one argument.
249
+ def call(proc, *args)
250
+ if proc.arity > 0
251
+ proc.call(*args[0..(proc.arity-1)])
252
+ else
253
+ proc.call
254
+ end
94
255
  end
95
256
 
257
+ # Returns new digest if changed or false if unchanged.
96
258
  def changed?(item)
97
- # returns md5 if changed, returns false when not changed
98
- md5 = digest(item.file)
99
- md5 == item.digest ? false : md5
259
+ d = digest(item.path) # get current digets
260
+ d == item.digest ? false : d # return new digest or false if unchanged
100
261
  end
101
262
 
263
+ # Returns a string representation of the file state.
102
264
  def digest(file)
103
- # returns the md5 of a file
104
- Digest::MD5.hexdigest( File.read(file) )
265
+ begin
266
+ File.mtime(file).to_f
267
+ rescue Errno::ENOENT
268
+ nil
269
+ end
105
270
  end
106
271
 
107
- def files_recursive(dirname)
108
- # return an array of files from this (dirname) point forth.
109
- Dir["#{dirname}/**/**"].collect {|f| f if File.file? f }.compact
272
+ # Returns an array of all files in dirname recursively. Accepts an optional file name regexp filter.
273
+ # The following will find files ending in ".rb" recursively beginning in the working dir:
274
+ # files_recursive Dir.pwd, /\.rb$/
275
+ def files_recursive(dirname, file_name_regexp=/.*/)
276
+ paths = []
277
+
278
+ Find.find(dirname) do |path|
279
+ if FileTest.directory?(path)
280
+ Find.prune if File.basename(path)[0] == ?. # Don't look any further into directies beginning with a dot.
281
+ else
282
+ paths << path if file_name_regexp === path # Amend the return array if the file found matches the regexp
283
+ end
284
+ end
285
+
286
+ return paths
110
287
  end
111
288
  end
@@ -2,10 +2,171 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
2
2
 
3
3
  # Time to add your specs!
4
4
  # http://rspec.info/
5
- describe "Place your specs here" do
5
+ describe "FileMonitor" do
6
+ before :all do
7
+ @app_root = File.expand_path(File.dirname(__FILE__)+"/..")
8
+ end
9
+
10
+ it 'should be able to instantize' do
11
+ fm = FileMonitor.new
12
+ FileMonitor.should === fm
13
+ end
14
+
15
+ it 'should support adding files' do
16
+ fm = FileMonitor.new
17
+ fm.watched.size.should == 0
18
+ (fm << @app_root + '/lib/FileMonitor.rb').should be_true # Should add single file using the '<<' method
19
+ fm.watched.size.should == 1
20
+ (fm.add @app_root + '/lib/FileMonitor/store.rb').should be_true # Should add single file using the 'add' method
21
+ fm.watched.size.should == 2
22
+ end
23
+
24
+ it 'should add files recursively when given a directory as a path' do
25
+ fm = FileMonitor.new
26
+ fm.watched.size.should == 0
27
+ fm.add(@app_root + '/lib').should be_true # Should add single file
28
+ fm.watched.size.should > 1
29
+ end
30
+
31
+ it 'should respond to index_of' do
32
+ fm = FileMonitor.new
33
+ fm.add(@app_root + '/lib').should be_true # Should add single file
34
+ fm.watched.size.should > 1
35
+ fm.index_of(@app_root + '/lib/FileMonitor.rb').should < fm.index_of(@app_root + '/lib/FileMonitor/store.rb')
36
+ fm.index_of(@app_root + '/lib/not_here.txt').should be_false
37
+ end
38
+
39
+ it 'should support a FileMonitor object level callback' do
40
+ fm = FileMonitor.new {true}
41
+ fm.callback.arity.should == -1
42
+
43
+ fm = FileMonitor.new {|watched_item| true}
44
+ fm.callback.arity.should == 1
45
+
46
+ fm = FileMonitor.new {|watched_item, file_monitor| true}
47
+ fm.callback.arity.should == 2
48
+ end
49
+
50
+ it 'should support adding files with individual callbacks' do
51
+ proc1 = Proc.new {return "hello from proc 1"}
52
+ proc2 = Proc.new {return "hello from proc 2"}
53
+ fm = FileMonitor.new &proc1
54
+ (fm << @app_root + '/lib/FileMonitor.rb').should be_true # Should add single file
55
+ (fm.add @app_root + '/lib/FileMonitor/store.rb', &proc2).should be_true # Should add single file
56
+ fm.watched.first.callback.should be_nil
57
+ fm.watched.last.callback.should == proc2
58
+ fm.watched.last.callback.should_not == proc1
59
+ end
60
+
61
+ it 'should overwrite existing file watches with successive additions' do
62
+ 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
66
+ fm.watched.size.should == 1
67
+ end
68
+
69
+ it 'should spawn processes' do
70
+ fm = FileMonitor.new
71
+ fm << @app_root + '/lib'
72
+ pid = fm.spawn
73
+ 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
+ end
76
+
77
+ it 'should stop spawn' do
78
+ fm = FileMonitor.new
79
+ fm << @app_root + '/lib'
80
+ pid = fm.spawn
81
+ `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should == [pid.to_s, Process.pid.to_s]
82
+ t=Time.now
83
+ fm.stop
84
+ puts "Time to stop: #{Time.now-t}"
85
+ `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should be_empty
86
+ end
87
+
88
+ it 'should halt spawn' do
89
+ fm = FileMonitor.new
90
+ fm << @app_root + '/lib'
91
+ pid = fm.spawn
92
+ `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should == [pid.to_s, Process.pid.to_s]
93
+ t=Time.now
94
+ fm.halt
95
+ puts "Time to halt: #{Time.now-t}"
96
+ `ps -p #{fm.spawn} -o 'pid ppid'|grep ^[0-9]`.split().should be_empty
97
+ end
98
+
99
+ it 'should setup & spawn using the when_modified class method' do
100
+ fm = FileMonitor.when_modified(Dir.pwd, "/path/to/other/file.rb") {|watched_item| true}
101
+ fm.callback.arity.should == 1
102
+ fm.pid.should > 1
103
+ fm.pid.should == fm.spawn
104
+ end
105
+
106
+ it 'should run callback on change' do
107
+ changed_files = nil
108
+ filename = @app_root + '/spec/temp.txt'
109
+ File.open(filename, 'w') {|f| f.write('hello') }
110
+
111
+ fm = FileMonitor.new() {|watched_item| changed_files = watched_item.path}
112
+ fm << filename
113
+
114
+ original = fm.watched.first.digest
115
+ sleep 1
116
+ File.open(filename, 'w') {|f| f.write('hello world') }
117
+ fm.process
118
+ fm.watched.first.digest.should_not == original
119
+ File.delete filename
120
+ end
6
121
 
7
- it "find this spec in spec directory" do
8
- # violated "Be sure to write your specs"
122
+ it 'should use item callback if possible, otherwise object callback' do
123
+ @global_callback = []
124
+ @files_callback = []
125
+ file1 = @app_root + '/spec/temp1.txt'
126
+ file2 = @app_root + '/spec/temp2.txt'
127
+ file3 = @app_root + '/spec/temp3.txt'
128
+
129
+ [file1,file2,file3].each {|file| File.open(file, 'w') {|f| f.write('hello') }}
130
+
131
+ fm = FileMonitor.new() {|watched_item| @global_callback << watched_item.path }
132
+ fm << file1
133
+ fm.add(file2) {|watched_item| @files_callback << watched_item.path }
134
+ fm << file3
135
+
136
+ sleep 1
137
+ [file1,file2,file3].each {|file| File.open(file, 'w') {|f| f.write('Hello World') }}
138
+ fm.process
139
+
140
+ @global_callback.should == [file1,file3]
141
+ @files_callback.should == [file2]
142
+ [file1,file2,file3].each {|file| File.delete(file)}
9
143
  end
10
144
 
11
- end
145
+ it 'should have access to local, FileMonitor & Store contexts' do
146
+ filename = @app_root + '/spec/temp.txt'
147
+ File.open(filename, 'w') {|f| f.write('hello') }
148
+
149
+ fm = FileMonitor.new() do |watched_item, file_monitor|
150
+ MonitoredItems::Store.should === watched_item
151
+ Spec::Example::ExampleGroup::Subclass_1.should === self
152
+ FileMonitor.should === file_monitor
153
+ end
154
+ fm << filename
155
+ sleep 1
156
+ File.open(filename, 'w') {|f| f.write('hello world') }
157
+ fm.process
158
+
159
+ File.delete filename
160
+ end
161
+
162
+ it 'should handel missing files without throwing errors' do
163
+ filename = @app_root + '/spec/temp.txt'
164
+ File.open(filename, 'w') {|f| f.write('hello') }
165
+ fm = FileMonitor.new() {true}
166
+ fm << filename
167
+ fm.process
168
+ File.delete(filename)
169
+ fm.process
170
+ fm.watched.first.digest.should be_nil
171
+ end
172
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ # Time to add your specs!
4
+ # http://rspec.info/
5
+ describe "FileMonitor::Store" do
6
+ before :all do
7
+ @item = MonitoredItems::Store.new
8
+ end
9
+
10
+ it "should store data and be accessable via mehtod() calls" do
11
+ pth = Dir.pwd
12
+ @item.file(pth).should == pth
13
+ @item.file.should == pth
14
+ end
15
+
16
+ it "should store data and be accessable via mehtod()= calls" do
17
+ pth = Dir.pwd
18
+ (@item.file = pth).should == pth
19
+ @item.file.should == pth
20
+ end
21
+
22
+ it 'should store keys as methods & values as data when initialized a hash' do
23
+ i = MonitoredItems::Store.new({'string' => true, :symbol => true, :path => Dir.pwd})
24
+ i.string.should be_true
25
+ i.symbol.should be_true
26
+ i.path.should == Dir.pwd
27
+ end
28
+
29
+ it 'should know how to to_h' do
30
+ @item = MonitoredItems::Store.new
31
+ @item.test true
32
+ @item.hello = 'world'
33
+ @item.to_h.should == {:test => true, :hello => 'world'}
34
+ end
35
+
36
+ it 'should know how to to_s' do
37
+ @item = MonitoredItems::Store.new
38
+ @item.test true
39
+ @item.hello = 'world'
40
+ @item.to_s.should == '{:test=>true, :hello=>"world"}'
41
+ end
42
+ 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.1
4
+ version: 0.0.2
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-15 00:00:00 -04:00
12
+ date: 2009-09-24 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -38,10 +38,12 @@ files:
38
38
  - README.markdown
39
39
  - Rakefile
40
40
  - lib/FileMonitor.rb
41
+ - lib/FileMonitor/store.rb
41
42
  - script/console
42
43
  - script/destroy
43
44
  - script/generate
44
45
  - spec/FileMonitor_spec.rb
46
+ - spec/MonitoredItem_spec.rb
45
47
  - spec/spec.opts
46
48
  - spec/spec_helper.rb
47
49
  - tasks/rspec.rake