filewatch 0.6.6 → 0.6.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5838846b40579f933b08efa2679143c4d6c3a653
4
- data.tar.gz: a0e819627f6fdb49a4aaceeac7e8393596772205
3
+ metadata.gz: 3c7e0e544aadf722674dd85b5ba0441dce6ecfc4
4
+ data.tar.gz: 4f6d362df8d9d5f105bcbf12f576e0cf8db69966
5
5
  SHA512:
6
- metadata.gz: 27da5e65f6aeb55e0cb1b2633c9dc2e8eac7d585d16c9efbc2002e1e01df2260a502d8586783d73beefc9b2dc6a8f70945101592c78eafa221e62e20328b1d14
7
- data.tar.gz: 916bfe75bc6b4f1805159ca004497c5a3ca68960f953b2f607fc9469aca77c525e04654c64bd39b612b63b42b684e99722a29277af28e27f2aa48bd9380048c4
6
+ metadata.gz: 5fa00a10ea8b093e6484aabbd9f653a7d2f06b97dafdcf2a2f2795f750cf2c3f0d8c13b19f2c41809e5284ed44316e7b448eda1499f7c9b817309a53a34d33c9
7
+ data.tar.gz: 0c8f460f91f9830deb1b096c22facc1106ea99a73ec297ad41384787bfa29f5302760696411d6b2c3517bb9b3eaa226343a0895ac78eb298ee1a04309b16a51a
@@ -155,7 +155,7 @@ module FileWatch
155
155
  @logger.debug? && @logger.debug("#{path}: initial create, no sincedb, seeking to beginning of file")
156
156
  @files[path].sysseek(0, IO::SEEK_SET)
157
157
  @sincedb[sincedb_record_uid] = 0
158
- else
158
+ else
159
159
  # seek to end
160
160
  @logger.debug? && @logger.debug("#{path}: initial create, no sincedb, seeking to end #{stat.size}")
161
161
  @files[path].sysseek(stat.size, IO::SEEK_SET)
@@ -229,7 +229,7 @@ module FileWatch
229
229
  private
230
230
  def _sincedb_write
231
231
  path = @opts[:sincedb_path]
232
- if File.device?(path)
232
+ if @iswindows || File.device?(path)
233
233
  IO.write(path, serialize_sincedb, 0)
234
234
  else
235
235
  File.atomic_write(path) {|file| file.write(serialize_sincedb) }
@@ -237,11 +237,31 @@ module FileWatch
237
237
  end # def _sincedb_write
238
238
 
239
239
  public
240
+ # quit is a sort-of finalizer,
241
+ # it should be called for clean up
242
+ # before the instance is disposed of.
240
243
  def quit
241
244
  _sincedb_write
242
245
  @watch.quit
246
+ @files.each {|path, file| file.close }
247
+ @files.clear
243
248
  end # def quit
244
249
 
250
+ public
251
+ # close_file(path) is to be used by external code
252
+ # when it knows that it is completely done with a file.
253
+ # Other files or folders may still be being watched.
254
+ # Caution, once unwatched, a file can't be watched again
255
+ # unless a new instance of this class begins watching again.
256
+ # The sysadmin should rename, move or delete the file.
257
+ def close_file(path)
258
+ @watch.unwatch(path)
259
+ file = @files.delete(path)
260
+ return if file.nil?
261
+ _sincedb_write
262
+ file.close
263
+ end
264
+
245
265
  private
246
266
  def serialize_sincedb
247
267
  @sincedb.map do |inode, pos|
@@ -19,6 +19,12 @@ module FileWatch
19
19
  @watching = []
20
20
  @exclude = []
21
21
  @files = Hash.new { |h, k| h[k] = Hash.new }
22
+ @unwatched = Hash.new
23
+ # we need to be threadsafe about the mutation
24
+ # of the above 2 ivars because the public
25
+ # methods each, discover, watch and unwatch
26
+ # can be called from different threads.
27
+ @lock = Mutex.new
22
28
  end # def initialize
23
29
 
24
30
  public
@@ -33,19 +39,37 @@ module FileWatch
33
39
 
34
40
  public
35
41
  def watch(path)
36
- if ! @watching.member?(path)
37
- @watching << path
38
- _discover_file(path, true)
42
+ synchronized do
43
+ if !@watching.member?(path)
44
+ @watching << path
45
+ _discover_file(path, true)
46
+ end
39
47
  end
40
-
41
48
  return true
42
49
  end # def watch
43
50
 
51
+ def unwatch(path)
52
+ synchronized do
53
+ result = false
54
+ if @watching.delete(path)
55
+ _globbed_files(path).each do |file|
56
+ deleted = @files.delete(file)
57
+ @unwatched[file] = deleted if deleted
58
+ end
59
+ result = true
60
+ else
61
+ result = @files.delete(path)
62
+ @unwatched[path] = result if result
63
+ end
64
+ return !!result
65
+ end
66
+ end
67
+
44
68
  public
45
69
  def inode(path,stat)
46
70
  if @iswindows
47
71
  fileId = Winhelper.GetWindowsUniqueFileIdentifier(path)
48
- inode = [fileId, stat.dev_major, stat.dev_minor]
72
+ inode = [fileId, 0, 0] # dev_* doesn't make sense on Windows
49
73
  else
50
74
  inode = [stat.ino.to_s, stat.dev_major, stat.dev_minor]
51
75
  end
@@ -60,52 +84,56 @@ module FileWatch
60
84
  # :delete - file is deleted
61
85
  public
62
86
  def each(&block)
63
- # Send any creates.
64
- @files.keys.each do |path|
65
- if ! @files[path][:create_sent]
66
- if @files[path][:initial]
67
- yield(:create_initial, path)
68
- else
69
- yield(:create, path)
87
+ synchronized do
88
+ # Send any creates.
89
+ @files.keys.each do |path|
90
+ if ! @files[path][:create_sent]
91
+ if @files[path][:initial]
92
+ yield(:create_initial, path)
93
+ else
94
+ yield(:create, path)
95
+ end
96
+ @files[path][:create_sent] = true
70
97
  end
71
- @files[path][:create_sent] = true
72
98
  end
73
- end
74
99
 
75
- @files.keys.each do |path|
76
- begin
77
- stat = File::Stat.new(path)
78
- rescue Errno::ENOENT
79
- # file has gone away or we can't read it anymore.
80
- @files.delete(path)
81
- @logger.debug? && @logger.debug("#{path}: stat failed (#{$!}), deleting from @files")
82
- yield(:delete, path)
83
- next
84
- end
100
+ @files.keys.each do |path|
101
+ begin
102
+ stat = File::Stat.new(path)
103
+ rescue Errno::ENOENT
104
+ # file has gone away or we can't read it anymore.
105
+ @files.delete(path)
106
+ @logger.debug? && @logger.debug("#{path}: stat failed (#{$!}), deleting from @files")
107
+ yield(:delete, path)
108
+ next
109
+ end
85
110
 
86
- inode = inode(path,stat)
87
- if inode != @files[path][:inode]
88
- @logger.debug? && @logger.debug("#{path}: old inode was #{@files[path][:inode].inspect}, new is #{inode.inspect}")
89
- yield(:delete, path)
90
- yield(:create, path)
91
- elsif stat.size < @files[path][:size]
92
- @logger.debug? && @logger.debug("#{path}: file rolled, new size is #{stat.size}, old size #{@files[path][:size]}")
93
- yield(:delete, path)
94
- yield(:create, path)
95
- elsif stat.size > @files[path][:size]
96
- @logger.debug? && @logger.debug("#{path}: file grew, old size #{@files[path][:size]}, new size #{stat.size}")
97
- yield(:modify, path)
98
- end
111
+ inode = inode(path,stat)
112
+ if inode != @files[path][:inode]
113
+ @logger.debug? && @logger.debug("#{path}: old inode was #{@files[path][:inode].inspect}, new is #{inode.inspect}")
114
+ yield(:delete, path)
115
+ yield(:create, path)
116
+ elsif stat.size < @files[path][:size]
117
+ @logger.debug? && @logger.debug("#{path}: file rolled, new size is #{stat.size}, old size #{@files[path][:size]}")
118
+ yield(:delete, path)
119
+ yield(:create, path)
120
+ elsif stat.size > @files[path][:size]
121
+ @logger.debug? && @logger.debug("#{path}: file grew, old size #{@files[path][:size]}, new size #{stat.size}")
122
+ yield(:modify, path)
123
+ end
99
124
 
100
- @files[path][:size] = stat.size
101
- @files[path][:inode] = inode
102
- end # @files.keys.each
125
+ @files[path][:size] = stat.size
126
+ @files[path][:inode] = inode
127
+ end # @files.keys.each
128
+ end
103
129
  end # def each
104
130
 
105
131
  public
106
132
  def discover
107
- @watching.each do |path|
108
- _discover_file(path)
133
+ synchronized do
134
+ @watching.each do |path|
135
+ _discover_file(path)
136
+ end
109
137
  end
110
138
  end
111
139
 
@@ -128,14 +156,9 @@ module FileWatch
128
156
 
129
157
  private
130
158
  def _discover_file(path, initial=false)
131
- globbed_dirs = Dir.glob(path)
132
- @logger.debug? && @logger.debug("_discover_file_glob: #{path}: glob is: #{globbed_dirs}")
133
- if globbed_dirs.empty? && File.file?(path)
134
- globbed_dirs = [path]
135
- @logger.debug? && @logger.debug("_discover_file_glob: #{path}: glob is: #{globbed_dirs} because glob did not work")
136
- end
137
- globbed_dirs.each do |file|
159
+ _globbed_files(path).each do |file|
138
160
  next if @files.member?(file)
161
+ next if @unwatched.member?(file)
139
162
  next unless File.file?(file)
140
163
 
141
164
  @logger.debug? && @logger.debug("_discover_file: #{path}: new: #{file} (exclude is #{@exclude.inspect})")
@@ -161,6 +184,23 @@ module FileWatch
161
184
  end
162
185
  end # def _discover_file
163
186
 
187
+ private
188
+ def _globbed_files(path)
189
+ globbed_dirs = Dir.glob(path)
190
+ @logger.debug? && @logger.debug("_globbed_files: #{path}: glob is: #{globbed_dirs}")
191
+ if globbed_dirs.empty? && File.file?(path)
192
+ globbed_dirs = [path]
193
+ @logger.debug? && @logger.debug("_globbed_files: #{path}: glob is: #{globbed_dirs} because glob did not work")
194
+ end
195
+ # return Enumerator
196
+ globbed_dirs.to_enum
197
+ end
198
+
199
+ private
200
+ def synchronized(&block)
201
+ @lock.synchronize { block.call }
202
+ end
203
+
164
204
  public
165
205
  def quit
166
206
  @quit = true
@@ -0,0 +1,18 @@
1
+ require 'filewatch/buftok'
2
+
3
+ describe FileWatch::BufferedTokenizer do
4
+
5
+ context "when using the default delimiter" do
6
+ it "splits the lines correctly" do
7
+ expect(subject.extract("hello\nworld\n")).to eq ["hello", "world"]
8
+ end
9
+ end
10
+
11
+ context "when passing a custom delimiter" do
12
+ subject { FileWatch::BufferedTokenizer.new("\r\n") }
13
+
14
+ it "splits the lines correctly" do
15
+ expect(subject.extract("hello\r\nworld\r\n")).to eq ["hello", "world"]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,232 @@
1
+ require 'filewatch/tail'
2
+ require 'stud/temporary'
3
+ require "rbconfig"
4
+
5
+ describe FileWatch::Tail do
6
+ before(:all) do
7
+ @thread_abort = Thread.abort_on_exception
8
+ Thread.abort_on_exception = true
9
+ end
10
+
11
+ after(:all) do
12
+ Thread.abort_on_exception = @thread_abort
13
+ end
14
+
15
+ let(:file_path) { f = Stud::Temporary.pathname }
16
+ let(:sincedb_path) { Stud::Temporary.pathname }
17
+
18
+ before :each do
19
+ Thread.new(subject) { sleep 0.5; subject.quit } # force the subscribe loop to exit
20
+ end
21
+
22
+ context "when watching a new file" do
23
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning, :stat_interval => 0) }
24
+
25
+ before :each do
26
+ subject.tail(file_path)
27
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
28
+ end
29
+
30
+ it "reads new lines off the file" do
31
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line1"], [file_path, "line2"])
32
+ end
33
+ end
34
+
35
+ context "when watching a file" do
36
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning, :stat_interval => 0) }
37
+
38
+ before :each do
39
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
40
+ subject.tail(file_path)
41
+ end
42
+
43
+ it "reads new lines off the file" do
44
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line1"], [file_path, "line2"])
45
+ end
46
+
47
+ end
48
+
49
+ context "when watching a CRLF file" do
50
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path,
51
+ :start_new_files_at => :beginning,
52
+ :delimiter => "\r\n") }
53
+
54
+ before :each do
55
+ File.open(file_path, "wb") { |file| file.write("line1\r\nline2\r\n") }
56
+ subject.tail(file_path)
57
+ end
58
+
59
+ it "reads new lines off the file" do
60
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line1"], [file_path, "line2"])
61
+ end
62
+ end
63
+
64
+ context "when a file is deleted" do
65
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning) }
66
+
67
+ before :each do
68
+ File.open(file_path, "w") { |file| file.write("line1\nline2\n") }
69
+ subject.tail(file_path)
70
+ File.unlink file_path
71
+ end
72
+
73
+ it "should not raise exception" do
74
+ Thread.new(subject) { sleep 0.1; subject.quit } # force the subscribe loop to exit
75
+ expect { subject.subscribe {|p,l| } }.to_not raise_exception
76
+ end
77
+ end
78
+
79
+ describe "sincedb" do
80
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning, :stat_interval => 0) }
81
+
82
+ before :each do
83
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
84
+ subject.tail(file_path)
85
+ end
86
+
87
+ context "when reading a new file" do
88
+ it "updates sincedb after subscribe" do
89
+ subject.subscribe {|_,_| }
90
+ stat = File::Stat.new(file_path)
91
+ sincedb_id = subject.sincedb_record_uid(file_path,stat).join(' ')
92
+ expect(File.read(sincedb_path)).to eq("#{sincedb_id} #{stat.size}\n")
93
+ end
94
+ end
95
+
96
+ context "when restarting tail" do
97
+ before :each do
98
+ subject.subscribe {|_,_| }
99
+ sleep 0.6 # wait for tail.quit
100
+ subject.tail(file_path) # re-tail file
101
+ File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
102
+ Thread.new(subject) { sleep 0.5; subject.quit }
103
+ end
104
+
105
+ it "picks off from where it stopped" do
106
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line3"], [file_path, "line4"])
107
+ end
108
+
109
+ it "updates on tail.quit" do
110
+ subject.subscribe {|_,_| }
111
+ stat = File::Stat.new(file_path)
112
+ sincedb_id = subject.sincedb_record_uid(file_path,stat).join(' ')
113
+ expect(File.read(sincedb_path)).to eq("#{sincedb_id} #{stat.size}\n")
114
+ end
115
+ end
116
+ end
117
+
118
+ context "ingesting files bigger than 32k" do
119
+ let(:lineA) { "a" * 12000 }
120
+ let(:lineB) { "b" * 25000 }
121
+ let(:lineC) { "c" * 8000 }
122
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning) }
123
+
124
+ before :each do
125
+ IO.write(file_path, "#{lineA}\n#{lineB}\n#{lineC}\n")
126
+ end
127
+
128
+ context "when restarting after stopping at the first line" do
129
+
130
+ let(:new_subject) { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning) }
131
+
132
+ before :each do
133
+ subject.tail(file_path)
134
+ subject.subscribe {|f, l| break if @test; @test = 1}
135
+ subject.sincedb_write
136
+ subject.quit
137
+ Thread.new(new_subject) { sleep 0.5; new_subject.quit } # force the subscribe loop to exit
138
+ end
139
+
140
+ it "should store in sincedb the position up until the first string" do
141
+ device, dev_major, dev_minor, pos = *IO.read(sincedb_path).split(" ").map {|n| n.to_i }
142
+ expect(pos).to eq(12001) # string.bytesize + "\n".bytesize
143
+ end
144
+
145
+ it "should read the second and third lines entirely" do
146
+ new_subject.tail(file_path) # re-tail file
147
+ expect { |b| new_subject.subscribe(&b) }.to yield_successive_args([file_path, lineB], [file_path, lineC])
148
+ end
149
+ end
150
+ end
151
+
152
+ context "when watching a directory" do
153
+
154
+ let(:directory) { Stud::Temporary.directory }
155
+ let(:file_path) { File.join(directory, "1.log") }
156
+
157
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning, :stat_interval => 0) }
158
+
159
+ before :each do
160
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
161
+ subject.tail(File.join(directory, "*"))
162
+ end
163
+
164
+ after :each do
165
+ FileUtils.rm_rf(directory)
166
+ end
167
+
168
+ it "reads new lines from the beginning" do
169
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line1"], [file_path, "line2"])
170
+ end
171
+
172
+ context "when a file is renamed" do
173
+
174
+ before :each do
175
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line1"], [file_path, "line2"])
176
+ File.rename(file_path, file_path + ".bak")
177
+ end
178
+
179
+ it "should not re-read the file" do
180
+ Thread.new(subject) { |s| sleep 1; s.quit }
181
+ expect { |b| subject.subscribe(&b) }.not_to yield_control
182
+ end
183
+ end
184
+
185
+ let(:new_file_path) { File.join(directory, "2.log") }
186
+
187
+ context "when a new file is later added to the directory" do
188
+ # Note tests in this context rely on FileWatch::Watch reading
189
+ # file 1.log first then 2.log and that depends on how Dir.glob is implemented
190
+ # in different rubies on different operating systems
191
+ before do
192
+ File.open(new_file_path, "wb") { |file| file.write("line2.1\nline2.2\n") }
193
+ end
194
+
195
+ it "reads new lines from the beginning for all files" do
196
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line1"], [file_path, "line2"],
197
+ [new_file_path, "line2.1"], [new_file_path, "line2.2"])
198
+ end
199
+
200
+ context "and when the sincedb path is not given" do
201
+ subject { FileWatch::Tail.new(:start_new_files_at => :beginning, :stat_interval => 0) }
202
+
203
+ it "reads new lines from the beginning for all files" do
204
+ expect { |b| subject.subscribe(&b) }.to yield_successive_args([file_path, "line1"], [file_path, "line2"],
205
+ [new_file_path, "line2.1"], [new_file_path, "line2.2"])
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ if RbConfig::CONFIG['host_os'] !~ /mswin|mingw|cygwin/
212
+ context "when quiting" do
213
+ subject { FileWatch::Tail.new(:sincedb_path => sincedb_path, :start_new_files_at => :beginning, :stat_interval => 0) }
214
+
215
+ before :each do
216
+ subject.tail(file_path)
217
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
218
+ end
219
+
220
+ it "closes the file handles" do
221
+ buffer = []
222
+ subject.subscribe do |path, line|
223
+ buffer.push([path, line])
224
+ end
225
+ subject.sincedb_write
226
+ subject.quit
227
+ lsof = `lsof -p #{Process.pid} | grep #{file_path}`
228
+ expect(lsof).to be_empty
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,120 @@
1
+ require 'filewatch/watch'
2
+ require 'stud/temporary'
3
+
4
+ describe FileWatch::Watch do
5
+ before(:all) do
6
+ @thread_abort = Thread.abort_on_exception
7
+ Thread.abort_on_exception = true
8
+ end
9
+
10
+ after(:all) do
11
+ Thread.abort_on_exception = @thread_abort
12
+ end
13
+
14
+ let(:directory) { Stud::Temporary.directory }
15
+ let(:file_path) { File.join(directory, "1.log") }
16
+ let(:loggr) { double("loggr", :debug? => true) }
17
+ let(:results) { [] }
18
+ let(:quit_proc) do
19
+ lambda do
20
+ Thread.new do
21
+ sleep 1
22
+ subject.quit
23
+ end
24
+ end
25
+ end
26
+ let(:subscribe_proc) do
27
+ lambda do
28
+ subject.subscribe(0.1, 4) do |event, path|
29
+ results.push([event, path])
30
+ end
31
+ end
32
+ end
33
+ let(:write_lines_1_and_2_proc) do
34
+ lambda do
35
+ File.open(file_path, "wb") { |file| file.write("line1\nline2\n") }
36
+ end
37
+ end
38
+
39
+ subject { FileWatch::Watch.new(:logger => loggr) }
40
+
41
+ before do
42
+ allow(loggr).to receive(:debug)
43
+ end
44
+
45
+ after do
46
+ FileUtils.rm_rf(directory)
47
+ end
48
+
49
+ context "when watching a directory with files" do
50
+ it "yields create_initial and one modify file events" do
51
+ write_lines_1_and_2_proc.call
52
+ subject.watch(File.join(directory, "*"))
53
+ quit_proc.call
54
+ subscribe_proc.call
55
+ expect(results).to eq([[:create_initial, file_path], [:modify, file_path]])
56
+ end
57
+ end
58
+
59
+ context "when watching a directory without files and one is added" do
60
+ it "yields create and one modify file events" do
61
+ subject.watch(File.join(directory, "*"))
62
+ write_lines_1_and_2_proc.call
63
+
64
+ quit_proc.call
65
+ subscribe_proc.call
66
+
67
+ expect(results).to eq([[:create, file_path], [:modify, file_path]])
68
+ end
69
+ end
70
+
71
+ context "when watching a directory with files and data is appended" do
72
+ let(:write_lines_3_and_4_proc) do
73
+ lambda do
74
+ Thread.new do
75
+ sleep 0.5
76
+ File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
77
+ end
78
+ end
79
+ end
80
+
81
+ it "yields create_initial and two modified file events" do
82
+ write_lines_1_and_2_proc.call
83
+ subject.watch(File.join(directory, "*"))
84
+
85
+ write_lines_3_and_4_proc.call # asynchronous
86
+
87
+ quit_proc.call
88
+ subscribe_proc.call
89
+
90
+ expect(results).to eq([[:create_initial, file_path], [:modify, file_path], [:modify, file_path]])
91
+ end
92
+ end
93
+
94
+ context "when unwatching a file and data is appended" do
95
+ let(:write_lines_3_and_4_proc) do
96
+ lambda do
97
+ Thread.new do
98
+ sleep 0.2
99
+ results.clear
100
+ subject.unwatch(file_path)
101
+ sleep 0.2
102
+ File.open(file_path, "ab") { |file| file.write("line3\nline4\n") }
103
+ end
104
+ end
105
+ end
106
+
107
+ it "does not yield events after unwatching" do
108
+ write_lines_1_and_2_proc.call
109
+ subject.watch(File.join(directory, "*"))
110
+
111
+ write_lines_3_and_4_proc.call # asynchronous
112
+
113
+ quit_proc.call
114
+ subscribe_proc.call
115
+
116
+ expect(results).to eq([])
117
+ end
118
+ end
119
+
120
+ end
@@ -0,0 +1,22 @@
1
+ require "stud/temporary"
2
+ require "fileutils"
3
+
4
+ if Gem.win_platform?
5
+ require "lib/filewatch/winhelper"
6
+
7
+ describe Winhelper do
8
+ let(:path) { Stud::Temporary.file.path }
9
+
10
+ after do
11
+ FileUtils.rm_rf(path)
12
+ end
13
+
14
+ it "return a unique file identifier" do
15
+ volume_serial, file_index_low, file_index_high = Winhelper.GetWindowsUniqueFileIdentifier(path).split("").map(&:to_i)
16
+
17
+ expect(volume_serial).not_to eq(0)
18
+ expect(file_index_low).not_to eq(0)
19
+ expect(file_index_high).not_to eq(0)
20
+ end
21
+ end
22
+ end
metadata CHANGED
@@ -1,31 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filewatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.6
4
+ version: 0.6.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Sissel
8
8
  - Pete Fritchman
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-10-28 00:00:00.000000000 Z
12
+ date: 2015-12-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
+ name: stud
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - '>='
18
+ - - ">="
18
19
  - !ruby/object:Gem::Version
19
20
  version: '0'
20
- name: stud
21
- prerelease: false
22
21
  type: :development
22
+ prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '>='
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
- description: Watch files and directories in ruby. Also supports tailing and glob file patterns.
28
+ description: Watch files and directories in ruby. Also supports tailing and glob file
29
+ patterns.
29
30
  email:
30
31
  - jls@semicomplete.com
31
32
  - petef@databits.net
@@ -41,6 +42,10 @@ files:
41
42
  - lib/filewatch/tail.rb
42
43
  - lib/filewatch/watch.rb
43
44
  - lib/filewatch/winhelper.rb
45
+ - spec/buftok_spec.rb
46
+ - spec/tail_spec.rb
47
+ - spec/watch_spec.rb
48
+ - spec/winhelper_spec.rb
44
49
  - test/filewatch/tail.rb
45
50
  - test/globtail/Makefile
46
51
  - test/globtail/framework.sh
@@ -67,25 +72,25 @@ files:
67
72
  homepage: https://github.com/jordansissel/ruby-filewatch
68
73
  licenses: []
69
74
  metadata: {}
70
- post_install_message:
75
+ post_install_message:
71
76
  rdoc_options: []
72
77
  require_paths:
73
78
  - lib
74
79
  - lib
75
80
  required_ruby_version: !ruby/object:Gem::Requirement
76
81
  requirements:
77
- - - '>='
82
+ - - ">="
78
83
  - !ruby/object:Gem::Version
79
84
  version: '0'
80
85
  required_rubygems_version: !ruby/object:Gem::Requirement
81
86
  requirements:
82
- - - '>='
87
+ - - ">="
83
88
  - !ruby/object:Gem::Version
84
89
  version: '0'
85
90
  requirements: []
86
- rubyforge_project:
87
- rubygems_version: 2.4.8
88
- signing_key:
91
+ rubyforge_project:
92
+ rubygems_version: 2.4.6
93
+ signing_key:
89
94
  specification_version: 4
90
95
  summary: filewatch - file watching for ruby
91
96
  test_files: []