filewatch 0.6.6 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.
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: []