filewatch 0.6.7 → 0.6.8

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.
@@ -5,7 +5,47 @@ end
5
5
 
6
6
  module FileWatch
7
7
  class Watch
8
- attr_accessor :logger
8
+ class WatchedFile
9
+ def self.new_initial(path, inode)
10
+ new(path, inode, true)
11
+ end
12
+
13
+ def self.new_ongoing(path, inode)
14
+ new(path, inode, false)
15
+ end
16
+
17
+ attr_reader :size, :inode
18
+ attr_writer :create_sent, :initial, :timeout_sent
19
+
20
+ attr_reader :path
21
+
22
+ def initialize(path, inode, initial)
23
+ @path = path
24
+ @size, @create_sent, @timeout_sent = 0, false, false
25
+ @inode, @initial = inode, initial
26
+ end
27
+
28
+ def update(stat, inode = nil)
29
+ @size = stat.size
30
+ @inode = inode if inode
31
+ end
32
+
33
+ def create_sent?
34
+ @create_sent
35
+ end
36
+
37
+ def initial?
38
+ @initial
39
+ end
40
+
41
+ def timeout_sent?
42
+ @timeout_sent
43
+ end
44
+
45
+ def to_s() inspect; end
46
+ end
47
+
48
+ attr_accessor :logger, :ignore_after
9
49
 
10
50
  public
11
51
  def initialize(opts={})
@@ -18,20 +58,18 @@ module FileWatch
18
58
  end
19
59
  @watching = []
20
60
  @exclude = []
21
- @files = Hash.new { |h, k| h[k] = Hash.new }
61
+ @files = Hash.new { |h, k| h[k] = WatchedFile.new(k, false, false) }
22
62
  @unwatched = Hash.new
23
63
  # we need to be threadsafe about the mutation
24
64
  # of the above 2 ivars because the public
25
65
  # methods each, discover, watch and unwatch
26
66
  # can be called from different threads.
27
67
  @lock = Mutex.new
68
+ # we need to be threadsafe about the quit mutation
69
+ @quit = false
70
+ @quit_lock = Mutex.new
28
71
  end # def initialize
29
72
 
30
- public
31
- def logger=(logger)
32
- @logger = logger
33
- end
34
-
35
73
  public
36
74
  def exclude(path)
37
75
  path.to_a.each { |p| @exclude << p }
@@ -42,7 +80,9 @@ module FileWatch
42
80
  synchronized do
43
81
  if !@watching.member?(path)
44
82
  @watching << path
45
- _discover_file(path, true)
83
+ _discover_file(path) do |filepath, stat|
84
+ WatchedFile.new_initial(filepath, inode(filepath, stat))
85
+ end
46
86
  end
47
87
  end
48
88
  return true
@@ -86,18 +126,18 @@ module FileWatch
86
126
  def each(&block)
87
127
  synchronized do
88
128
  # Send any creates.
89
- @files.keys.each do |path|
90
- if ! @files[path][:create_sent]
91
- if @files[path][:initial]
129
+ @files.each do |path, watched_file|
130
+ if !watched_file.create_sent?
131
+ if watched_file.initial?
92
132
  yield(:create_initial, path)
93
133
  else
94
134
  yield(:create, path)
95
135
  end
96
- @files[path][:create_sent] = true
136
+ watched_file.create_sent = true
97
137
  end
98
138
  end
99
139
 
100
- @files.keys.each do |path|
140
+ @files.each do |path, watched_file|
101
141
  begin
102
142
  stat = File::Stat.new(path)
103
143
  rescue Errno::ENOENT
@@ -108,23 +148,33 @@ module FileWatch
108
148
  next
109
149
  end
110
150
 
151
+ if expired?(stat, watched_file)
152
+ if !watched_file.timeout_sent?
153
+ @logger.debug? && @logger.debug("#{path}: file expired")
154
+ yield(:timeout, path)
155
+ watched_file.timeout_sent = true
156
+ end
157
+ next
158
+ end
159
+
111
160
  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}")
161
+ old_size = watched_file.size
162
+
163
+ if inode != watched_file.inode
164
+ @logger.debug? && @logger.debug("#{path}: old inode was #{watched_file.inode.inspect}, new is #{inode.inspect}")
114
165
  yield(:delete, path)
115
166
  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]}")
167
+ elsif stat.size < old_size
168
+ @logger.debug? && @logger.debug("#{path}: file rolled, new size is #{stat.size}, old size #{old_size}")
118
169
  yield(:delete, path)
119
170
  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}")
171
+ elsif stat.size > old_size
172
+ @logger.debug? && @logger.debug("#{path}: file grew, old size #{old_size}, new size #{stat.size}")
122
173
  yield(:modify, path)
123
174
  end
124
175
 
125
- @files[path][:size] = stat.size
126
- @files[path][:inode] = inode
127
- end # @files.keys.each
176
+ watched_file.update(stat, inode)
177
+ end
128
178
  end
129
179
  end # def each
130
180
 
@@ -132,7 +182,9 @@ module FileWatch
132
182
  def discover
133
183
  synchronized do
134
184
  @watching.each do |path|
135
- _discover_file(path)
185
+ _discover_file(path) do |filepath, stat|
186
+ WatchedFile.new_ongoing(filepath, inode(filepath, stat))
187
+ end
136
188
  end
137
189
  end
138
190
  end
@@ -140,8 +192,8 @@ module FileWatch
140
192
  public
141
193
  def subscribe(stat_interval = 1, discover_interval = 5, &block)
142
194
  glob = 0
143
- @quit = false
144
- while !@quit
195
+ reset_quit
196
+ while !quit?
145
197
  each(&block)
146
198
 
147
199
  glob += 1
@@ -155,7 +207,24 @@ module FileWatch
155
207
  end # def subscribe
156
208
 
157
209
  private
158
- def _discover_file(path, initial=false)
210
+ def expired?(stat, watched_file)
211
+ file_expired?(stat) && watched_file.size == stat.size
212
+ end
213
+
214
+ def discover_expired?(stat)
215
+ file_expired?(stat)
216
+ end
217
+
218
+ def file_expired?(stat)
219
+ return false unless expiry_enabled?
220
+ # (Time.now - stat.mtime) <- in jruby, this does int and float
221
+ # conversions before the subtraction and returns a float.
222
+ # so use all ints instead
223
+ (Time.now.to_i - stat.mtime.to_i) > @ignore_after
224
+ end
225
+
226
+ private
227
+ def _discover_file(path)
159
228
  _globbed_files(path).each do |file|
160
229
  next if @files.member?(file)
161
230
  next if @unwatched.member?(file)
@@ -175,15 +244,24 @@ module FileWatch
175
244
  next if skip
176
245
 
177
246
  stat = File::Stat.new(file)
178
- @files[file] = {
179
- :size => 0,
180
- :inode => inode(file,stat),
181
- :create_sent => false,
182
- :initial => initial
183
- }
247
+ # let the caller build the object in its context
248
+ watched_file = yield(file, stat)
249
+
250
+ if discover_expired?(stat)
251
+ msg = "_discover_file: #{file}: skipping because it was last modified more than #{@ignore_after} seconds ago"
252
+ @logger.debug? && @logger.debug(msg)
253
+ watched_file.update(stat)
254
+ end
255
+
256
+ @files[file] = watched_file
184
257
  end
185
258
  end # def _discover_file
186
259
 
260
+ private
261
+ def expiry_enabled?
262
+ !@ignore_after.nil?
263
+ end
264
+
187
265
  private
188
266
  def _globbed_files(path)
189
267
  globbed_dirs = Dir.glob(path)
@@ -201,9 +279,19 @@ module FileWatch
201
279
  @lock.synchronize { block.call }
202
280
  end
203
281
 
282
+ private
283
+ def quit?
284
+ @quit_lock.synchronize { @quit }
285
+ end
286
+
287
+ private
288
+ def reset_quit
289
+ @quit_lock.synchronize { @quit = false }
290
+ end
291
+
204
292
  public
205
293
  def quit
206
- @quit = true
294
+ @quit_lock.synchronize { @quit = true }
207
295
  end # def quit
208
296
  end # class Watch
209
297
  end # module FileWatch
@@ -0,0 +1,79 @@
1
+ require 'filewatch/tail_base'
2
+
3
+ module FileWatch
4
+ class YieldingTail
5
+ include TailBase
6
+
7
+ public
8
+ def subscribe(&block)
9
+ # subscribe(stat_interval = 1, discover_interval = 5, &block)
10
+ @watch.subscribe(@opts[:stat_interval],
11
+ @opts[:discover_interval]) do |event, path|
12
+ case event
13
+ when :create, :create_initial
14
+ if @files.member?(path)
15
+ @logger.debug? && @logger.debug("#{event} for #{path}: already exists in @files")
16
+ next
17
+ end
18
+ if _open_file(path, event)
19
+ yield_read_file(path, &block)
20
+ end
21
+ when :modify
22
+ if !@files.member?(path)
23
+ @logger.debug? && @logger.debug(":modify for #{path}, does not exist in @files")
24
+ if _open_file(path, event)
25
+ yield_read_file(path, &block)
26
+ end
27
+ else
28
+ yield_read_file(path, &block)
29
+ end
30
+ when :delete
31
+ @logger.debug? && @logger.debug(":delete for: #{path} - closed and deleted from @files")
32
+ if @files[path]
33
+ yield_read_file(path, &block)
34
+ @files[path].close
35
+ end
36
+ @files.delete(path)
37
+ @statcache.delete(path)
38
+ when :timeout
39
+ @logger.debug? && @logger.debug(":timeout for: #{path} - closed and deleted from @files")
40
+ if (deleted = @files.delete(path))
41
+ deleted.close
42
+ end
43
+ @statcache.delete(path)
44
+ else
45
+ @logger.warn("unknown event type #{event} for #{path}")
46
+ end
47
+ end # @watch.subscribe
48
+ end # def subscribe
49
+
50
+ private
51
+ def yield_read_file(path, &block)
52
+ @buffers[path] ||= FileWatch::BufferedTokenizer.new(@opts[:delimiter])
53
+ delimiter_byte_size = @opts[:delimiter].bytesize
54
+ changed = false
55
+ loop do
56
+ begin
57
+ data = @files[path].sysread(32768)
58
+ changed = true
59
+ @buffers[path].extract(data).each do |line|
60
+ yield(path, line)
61
+ @sincedb[@statcache[path]] += (line.bytesize + delimiter_byte_size)
62
+ end
63
+ rescue Errno::EWOULDBLOCK, Errno::EINTR, EOFError
64
+ break
65
+ end
66
+ end
67
+
68
+ if changed
69
+ now = Time.now.to_i
70
+ delta = now - @sincedb_last_write
71
+ if delta >= @opts[:sincedb_write_interval]
72
+ @logger.debug? && @logger.debug("writing sincedb (delta since last write = #{delta})")
73
+ _sincedb_write
74
+ @sincedb_last_write = now
75
+ end
76
+ end
77
+ end
78
+ end # module YieldingTail
79
+ end # module FileWatch
metadata CHANGED
@@ -1,32 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filewatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.7
4
+ version: 0.6.8
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-12-02 00:00:00.000000000 Z
12
+ date: 2015-12-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: stud
16
15
  requirement: !ruby/object:Gem::Requirement
17
16
  requirements:
18
- - - ">="
17
+ - - '>='
19
18
  - !ruby/object:Gem::Version
20
19
  version: '0'
21
- type: :development
20
+ name: stud
22
21
  prerelease: false
22
+ type: :development
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
29
- patterns.
28
+ description: Watch files and directories in ruby. Also supports tailing and glob file patterns.
30
29
  email:
31
30
  - jls@semicomplete.com
32
31
  - petef@databits.net
@@ -39,13 +38,12 @@ files:
39
38
  - lib/JRubyFileExtension.jar
40
39
  - lib/filewatch/buftok.rb
41
40
  - lib/filewatch/helper.rb
41
+ - lib/filewatch/observing_tail.rb
42
42
  - lib/filewatch/tail.rb
43
+ - lib/filewatch/tail_base.rb
43
44
  - lib/filewatch/watch.rb
44
45
  - lib/filewatch/winhelper.rb
45
- - spec/buftok_spec.rb
46
- - spec/tail_spec.rb
47
- - spec/watch_spec.rb
48
- - spec/winhelper_spec.rb
46
+ - lib/filewatch/yielding_tail.rb
49
47
  - test/filewatch/tail.rb
50
48
  - test/globtail/Makefile
51
49
  - test/globtail/framework.sh
@@ -72,25 +70,25 @@ files:
72
70
  homepage: https://github.com/jordansissel/ruby-filewatch
73
71
  licenses: []
74
72
  metadata: {}
75
- post_install_message:
73
+ post_install_message:
76
74
  rdoc_options: []
77
75
  require_paths:
78
76
  - lib
79
77
  - lib
80
78
  required_ruby_version: !ruby/object:Gem::Requirement
81
79
  requirements:
82
- - - ">="
80
+ - - '>='
83
81
  - !ruby/object:Gem::Version
84
82
  version: '0'
85
83
  required_rubygems_version: !ruby/object:Gem::Requirement
86
84
  requirements:
87
- - - ">="
85
+ - - '>='
88
86
  - !ruby/object:Gem::Version
89
87
  version: '0'
90
88
  requirements: []
91
- rubyforge_project:
92
- rubygems_version: 2.4.6
93
- signing_key:
89
+ rubyforge_project:
90
+ rubygems_version: 2.4.8
91
+ signing_key:
94
92
  specification_version: 4
95
93
  summary: filewatch - file watching for ruby
96
94
  test_files: []
data/spec/buftok_spec.rb DELETED
@@ -1,18 +0,0 @@
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
data/spec/tail_spec.rb DELETED
@@ -1,232 +0,0 @@
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