filewatcher 1.0.0 → 1.0.1

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
- SHA1:
3
- metadata.gz: 025e651355a5bbbae353527112232253ee4f682d
4
- data.tar.gz: 95ca7990a7e002768ac0a7a814a4129841efe3d6
2
+ SHA256:
3
+ metadata.gz: 9221be161e73cc2f85ea6f9706a2e558c31ff314664bc31817e9715439a31927
4
+ data.tar.gz: bae2bc1ba2f5b88693fe1c8093b135652885094c69adc3da51193916ada96b44
5
5
  SHA512:
6
- metadata.gz: b8576d1ff169c063a664ae9c538c51ad079ccfae4a604e0c277b84df261dc438b25f912863e5bd9b2ae542bac48026ea8f5a9a0990ccb81265206751b6138a1a
7
- data.tar.gz: 25b04df22331575a7ffbfce5c1acc5168c0ee58e9d15fa4d4f7f3dff309fe1382be5920c315015309217bbf108882d994777cda67376c511c1197d6840952e7e
6
+ metadata.gz: 0aec2e10e03aad69fd0e4ccda0a6f488efc7c908abe31640319971dda6ed71ca8873588d87693f91309d13ac996bb62a952e3c7430c41b462d663e0064185e8e
7
+ data.tar.gz: ebf1581b4e3e56c77c4a18867ce55d2cb9a6fbf97d7804da305192dd9622a39d9b16cfe1392dd880c6dd1c43f47539a66a10f61956de7fad0202f2c43222b64d
data/lib/filewatcher.rb CHANGED
@@ -8,7 +8,8 @@ require_relative 'filewatcher/cycles'
8
8
  class Filewatcher
9
9
  include Filewatcher::Cycles
10
10
 
11
- attr_writer :interval
11
+ attr_accessor :interval
12
+ attr_reader :keep_watching
12
13
 
13
14
  def update_spinner(label)
14
15
  return unless @show_spinner
@@ -28,7 +29,7 @@ class Filewatcher
28
29
  end
29
30
 
30
31
  def watch(&on_update)
31
- trap('SIGINT') { return }
32
+ %w[HUP INT TERM].each { |signal| trap(signal) { exit } }
32
33
  @on_update = on_update
33
34
  @keep_watching = true
34
35
  yield('', '') if @immediate
@@ -10,8 +10,12 @@ class Filewatcher
10
10
  @filename = filename
11
11
  @event = event
12
12
  @path = Pathname.new(@filename)
13
- @realpath = @path.realpath
13
+ @realpath = @path.exist? ? @path.realpath : @path
14
14
  @current_dir = Pathname.new(Dir.pwd)
15
+ # For safely `immediate` option with empty-strings arguments
16
+ @relative_path =
17
+ (@realpath.to_s.empty? ? @current_dir : @realpath)
18
+ .relative_path_from(@current_dir)
15
19
  end
16
20
 
17
21
  def to_h
@@ -22,7 +26,7 @@ class Filewatcher
22
26
  'EVENT' => @event.to_s,
23
27
  'DIRNAME' => @path.parent.realpath.to_s,
24
28
  'ABSOLUTE_FILENAME' => @realpath.to_s,
25
- 'RELATIVE_FILENAME' => @realpath.relative_path_from(@current_dir).to_s
29
+ 'RELATIVE_FILENAME' => @relative_path.to_s
26
30
  }
27
31
  end
28
32
  end
@@ -3,5 +3,5 @@
3
3
  require_relative '../filewatcher'
4
4
 
5
5
  class Filewatcher
6
- VERSION = '1.0.0'.freeze
6
+ VERSION = '1.0.1'.freeze
7
7
  end
data/test/helper.rb CHANGED
@@ -12,50 +12,93 @@ end
12
12
  class WatchRun
13
13
  TMP_DIR = File.join(__dir__, 'tmp')
14
14
 
15
- attr_reader :filename, :filewatcher, :thread, :watched, :processed
15
+ attr_reader :filename
16
16
 
17
17
  def initialize(
18
18
  filename: 'tmp_file.txt',
19
19
  directory: false,
20
- every: false,
21
- filewatcher: Filewatcher.new(
22
- File.join(TMP_DIR, '**', '*'), interval: 0.1, every: every
23
- ),
24
20
  action: :update
25
21
  )
26
22
  @filename =
27
23
  filename.start_with?('/', '~') ? filename : File.join(TMP_DIR, filename)
28
24
  @directory = directory
29
- @filewatcher = filewatcher
30
25
  @action = action
31
26
  end
32
27
 
33
28
  def start
34
29
  File.write(@filename, 'content1') unless @action == :create
35
-
36
- @thread = thread_initialize
37
- sleep 3 # thread needs a chance to start
38
30
  end
39
31
 
40
32
  def run
41
33
  start
42
34
 
43
35
  make_changes
44
- # Some OS, filesystems and Ruby interpretators
45
- # doesn't catch milliseconds of `File.mtime`
46
- sleep 3
47
36
 
48
37
  stop
49
38
  end
50
39
 
51
40
  def stop
52
41
  FileUtils.rm_r(@filename) if File.exist?(@filename)
53
- @thread.exit
54
- sleep 3
55
42
  end
56
43
 
57
44
  private
58
45
 
46
+ def make_changes
47
+ if @action == :delete
48
+ FileUtils.remove(@filename)
49
+ elsif @directory
50
+ FileUtils.mkdir_p(@filename)
51
+ else
52
+ File.write(@filename, 'content2')
53
+ end
54
+ end
55
+ end
56
+
57
+ class RubyWatchRun < WatchRun
58
+ attr_reader :filewatcher, :thread, :watched, :processed
59
+
60
+ def initialize(
61
+ every: false,
62
+ filewatcher: Filewatcher.new(
63
+ File.join(TMP_DIR, '**', '*'), interval: 0.1, every: every
64
+ ),
65
+ **args
66
+ )
67
+ super(**args)
68
+ @filewatcher = filewatcher
69
+ end
70
+
71
+ def start
72
+ super
73
+ @thread = thread_initialize
74
+ # thread needs a chance to start
75
+ wait 3 do
76
+ filewatcher.keep_watching
77
+ end
78
+ end
79
+
80
+ def stop
81
+ thread.exit
82
+
83
+ wait 3 do
84
+ thread.stop?
85
+ end
86
+
87
+ super
88
+ end
89
+
90
+ private
91
+
92
+ def make_changes
93
+ super
94
+
95
+ # Some OS, filesystems and Ruby interpretators
96
+ # doesn't catch milliseconds of `File.mtime`
97
+ wait 3 do
98
+ processed.any?
99
+ end
100
+ end
101
+
59
102
  def thread_initialize
60
103
  @watched ||= 0
61
104
  Thread.new(
@@ -72,56 +115,95 @@ class WatchRun
72
115
  @watched += 1
73
116
  end
74
117
 
75
- def make_changes
76
- return FileUtils.remove(@filename) if @action == :delete
77
- return FileUtils.mkdir_p(@filename) if @directory
78
- File.write(@filename, 'content2')
118
+ def wait(seconds)
119
+ max_count = seconds / filewatcher.interval
120
+ count = 0
121
+ while count < max_count && !yield
122
+ sleep filewatcher.interval
123
+ count += 1
124
+ end
79
125
  end
80
126
  end
81
127
 
82
- class ShellWatchRun
128
+ class ShellWatchRun < WatchRun
83
129
  EXECUTABLE = "#{'ruby ' if Gem.win_platform?}" \
84
130
  "#{File.realpath File.join(__dir__, '..', 'bin', 'filewatcher')}".freeze
85
131
 
86
- attr_reader :output
132
+ SLEEP_MULTIPLIER = RUBY_PLATFORM == 'java' ? 5 : 1
133
+
134
+ ENV_FILE = File.join(TMP_DIR, 'env')
87
135
 
88
136
  def initialize(
89
- options: '',
137
+ options: {},
90
138
  dumper: :watched,
91
- output: File.join(WatchRun::TMP_DIR, 'env')
139
+ **args
92
140
  )
141
+ super(**args)
93
142
  @options = options
143
+ @options[:interval] ||= 0.1
144
+ @options_string =
145
+ @options.map { |key, value| "--#{key}=#{value}" }.join(' ')
94
146
  @dumper = dumper
95
- @output = output
96
147
  end
97
148
 
98
149
  def start
150
+ super
151
+
99
152
  @pid = spawn(
100
- "#{EXECUTABLE} #{@options} \"#{WatchRun::TMP_DIR}/foo*\"" \
101
- " \"ruby #{File.join(__dir__, 'dumpers', "#{@dumper}_dumper.rb")}\""
153
+ "#{EXECUTABLE} #{@options_string} \"#{@filename}\"" \
154
+ " \"ruby #{File.join(__dir__, 'dumpers', "#{@dumper}_dumper.rb")}\"",
155
+ pgroup: true
102
156
  )
103
- Process.detach(@pid)
104
- sleep 12
105
- end
106
157
 
107
- def run
108
- start
158
+ Process.detach(@pid)
109
159
 
110
- make_changes
160
+ wait 12 do
161
+ pid_state == 'S' && (!@options[:immediate] || File.exist?(ENV_FILE))
162
+ end
111
163
 
112
- stop
164
+ # a little more time
165
+ sleep 1 * SLEEP_MULTIPLIER
113
166
  end
114
167
 
115
168
  def stop
116
- Process.kill('KILL', @pid)
117
- sleep 6
169
+ ## Problems: https://github.com/thomasfl/filewatcher/pull/83
170
+ ## Solution: https://stackoverflow.com/a/45032252/2630849
171
+ Process.kill('TERM', -Process.getpgid(@pid))
172
+ Process.waitall
173
+
174
+ wait 12 do
175
+ pid_state.empty?
176
+ end
177
+
178
+ # a little more time
179
+ sleep 1 * SLEEP_MULTIPLIER
180
+
181
+ super
118
182
  end
119
183
 
120
184
  private
121
185
 
122
186
  def make_changes
123
- FileUtils.touch "#{WatchRun::TMP_DIR}/foo.txt"
124
- sleep 12
187
+ super
188
+
189
+ wait 12 do
190
+ File.exist?(ENV_FILE)
191
+ end
192
+ end
193
+
194
+ def pid_state
195
+ ## For macOS output:
196
+ ## https://travis-ci.org/thomasfl/filewatcher/jobs/304433538
197
+ `ps -ho state -p #{@pid}`.sub('STAT', '').strip
198
+ end
199
+
200
+ def wait(seconds)
201
+ max_count = seconds / @options[:interval]
202
+ count = 0
203
+ while count < max_count && !yield
204
+ sleep @options[:interval]
205
+ count += 1
206
+ end
125
207
  end
126
208
  end
127
209
 
@@ -130,5 +212,5 @@ custom_matcher :include_all_files do |obj, elements|
130
212
  end
131
213
 
132
214
  def dump_to_env_file(content)
133
- File.write File.join(WatchRun::TMP_DIR, 'env'), content
215
+ File.write File.join(ShellWatchRun::ENV_FILE), content
134
216
  end
@@ -10,11 +10,18 @@ describe Filewatcher do
10
10
 
11
11
  after do
12
12
  FileUtils.rm_r WatchRun::TMP_DIR
13
+
14
+ interval = 0.1
15
+ wait = 5
16
+ count = 0
17
+ while File.exist?(WatchRun::TMP_DIR) && count < (wait / interval)
18
+ sleep interval
19
+ end
13
20
  end
14
21
 
15
22
  describe '#initialize' do
16
23
  it 'should exclude selected file patterns' do
17
- wr = WatchRun.new(
24
+ wr = RubyWatchRun.new(
18
25
  filewatcher: Filewatcher.new(
19
26
  File.expand_path('test/tmp/**/*'),
20
27
  exclude: File.expand_path('test/tmp/**/*.txt')
@@ -27,7 +34,7 @@ describe Filewatcher do
27
34
  end
28
35
 
29
36
  it 'should handle absolute paths with globs' do
30
- wr = WatchRun.new(
37
+ wr = RubyWatchRun.new(
31
38
  filewatcher: Filewatcher.new(
32
39
  File.expand_path('test/tmp/**/*')
33
40
  )
@@ -41,7 +48,7 @@ describe Filewatcher do
41
48
  end
42
49
 
43
50
  it 'should handle globs' do
44
- wr = WatchRun.new(
51
+ wr = RubyWatchRun.new(
45
52
  filewatcher: Filewatcher.new('test/tmp/**/*')
46
53
  )
47
54
 
@@ -53,7 +60,7 @@ describe Filewatcher do
53
60
  end
54
61
 
55
62
  it 'should handle explicit relative paths with globs' do
56
- wr = WatchRun.new(
63
+ wr = RubyWatchRun.new(
57
64
  filewatcher: Filewatcher.new('./test/tmp/**/*')
58
65
  )
59
66
 
@@ -65,7 +72,7 @@ describe Filewatcher do
65
72
  end
66
73
 
67
74
  it 'should handle explicit relative paths' do
68
- wr = WatchRun.new(
75
+ wr = RubyWatchRun.new(
69
76
  filewatcher: Filewatcher.new('./test/tmp')
70
77
  )
71
78
 
@@ -79,7 +86,7 @@ describe Filewatcher do
79
86
  it 'should handle tilde expansion' do
80
87
  filename = File.expand_path('~/file_watcher_1.txt')
81
88
 
82
- wr = WatchRun.new(
89
+ wr = RubyWatchRun.new(
83
90
  filename: filename,
84
91
  filewatcher: Filewatcher.new('~/file_watcher_1.txt')
85
92
  )
@@ -92,7 +99,7 @@ describe Filewatcher do
92
99
  end
93
100
 
94
101
  it 'should immediately run with corresponding option' do
95
- wr = WatchRun.new(
102
+ wr = RubyWatchRun.new(
96
103
  filewatcher: Filewatcher.new('**/*', immediate: true)
97
104
  )
98
105
 
@@ -104,7 +111,7 @@ describe Filewatcher do
104
111
  end
105
112
 
106
113
  it 'should not be executed without immediate option and changes' do
107
- wr = WatchRun.new(
114
+ wr = RubyWatchRun.new(
108
115
  filewatcher: Filewatcher.new('**/*', immediate: false)
109
116
  )
110
117
 
@@ -118,7 +125,7 @@ describe Filewatcher do
118
125
 
119
126
  describe '#watch' do
120
127
  it 'should detect file deletions' do
121
- wr = WatchRun.new(action: :delete)
128
+ wr = RubyWatchRun.new(action: :delete)
122
129
 
123
130
  wr.run
124
131
 
@@ -128,7 +135,7 @@ describe Filewatcher do
128
135
  end
129
136
 
130
137
  it 'should detect file additions' do
131
- wr = WatchRun.new(action: :create)
138
+ wr = RubyWatchRun.new(action: :create)
132
139
 
133
140
  wr.run
134
141
 
@@ -138,7 +145,7 @@ describe Filewatcher do
138
145
  end
139
146
 
140
147
  it 'should detect file updates' do
141
- wr = WatchRun.new(action: :update)
148
+ wr = RubyWatchRun.new(action: :update)
142
149
 
143
150
  wr.run
144
151
 
@@ -150,7 +157,7 @@ describe Filewatcher do
150
157
  it 'should detect new files in subfolders' do
151
158
  FileUtils.mkdir_p subfolder = File.expand_path('test/tmp/new_sub_folder')
152
159
 
153
- wr = WatchRun.new(
160
+ wr = RubyWatchRun.new(
154
161
  filename: File.join(subfolder, 'file.txt'),
155
162
  action: :create,
156
163
  every: true
@@ -167,7 +174,7 @@ describe Filewatcher do
167
174
  it 'should detect new subfolders' do
168
175
  subfolder = 'new_sub_folder'
169
176
 
170
- wr = WatchRun.new(
177
+ wr = RubyWatchRun.new(
171
178
  filename: subfolder,
172
179
  directory: true,
173
180
  action: :create
@@ -183,7 +190,7 @@ describe Filewatcher do
183
190
 
184
191
  describe '#stop' do
185
192
  it 'should work' do
186
- wr = WatchRun.new
193
+ wr = RubyWatchRun.new
187
194
 
188
195
  wr.start
189
196
 
@@ -196,7 +203,7 @@ describe Filewatcher do
196
203
 
197
204
  describe '#pause, #resume' do
198
205
  it 'should work' do
199
- wr = WatchRun.new(action: :create, every: true)
206
+ wr = RubyWatchRun.new(action: :create, every: true)
200
207
 
201
208
  wr.start
202
209
 
@@ -230,7 +237,7 @@ describe Filewatcher do
230
237
 
231
238
  describe '#finalize' do
232
239
  it 'should process all remaining changes' do
233
- wr = WatchRun.new(action: :create, every: true)
240
+ wr = RubyWatchRun.new(action: :create, every: true)
234
241
 
235
242
  wr.start
236
243
 
@@ -249,7 +256,7 @@ describe Filewatcher do
249
256
  end
250
257
 
251
258
  describe 'executable' do
252
- tmp_dir = WatchRun::TMP_DIR
259
+ tmp_dir = ShellWatchRun::TMP_DIR
253
260
 
254
261
  it 'should run' do
255
262
  null_output = Gem.win_platform? ? 'NUL' : '/dev/null'
@@ -257,49 +264,77 @@ describe Filewatcher do
257
264
  .should.be.true
258
265
  end
259
266
 
260
- it 'should set correct ENV variables' do
267
+ it 'should set correct ENV variables for file creation' do
268
+ filename = 'foo.txt'
269
+
261
270
  swr = ShellWatchRun.new(
271
+ filename: filename,
272
+ action: :create,
262
273
  dumper: :env
263
274
  )
264
275
 
265
276
  swr.run
266
277
 
267
- File.read(swr.output)
278
+ File.read(ShellWatchRun::ENV_FILE)
268
279
  .should.equal(
269
280
  %W[
270
- #{tmp_dir}/foo.txt
271
- foo.txt
281
+ #{tmp_dir}/#{filename}
282
+ #{filename}
272
283
  created
273
284
  #{tmp_dir}
274
- #{tmp_dir}/foo.txt
275
- test/tmp/foo.txt
285
+ #{tmp_dir}/#{filename}
286
+ test/tmp/#{filename}
287
+ ].join(', ')
288
+ )
289
+ end
290
+
291
+ it 'should set correct ENV variables for file deletion' do
292
+ filename = 'foo.txt'
293
+
294
+ swr = ShellWatchRun.new(
295
+ filename: filename,
296
+ action: :delete,
297
+ dumper: :env
298
+ )
299
+
300
+ swr.run
301
+
302
+ File.read(ShellWatchRun::ENV_FILE)
303
+ .should.equal(
304
+ %W[
305
+ #{tmp_dir}/#{filename}
306
+ #{filename}
307
+ deleted
308
+ #{tmp_dir}
309
+ #{tmp_dir}/#{filename}
310
+ test/tmp/#{filename}
276
311
  ].join(', ')
277
312
  )
278
313
  end
279
314
 
280
315
  it 'should be executed immediately with corresponding option' do
281
316
  swr = ShellWatchRun.new(
282
- options: '--immediate',
317
+ options: { immediate: true },
283
318
  dumper: :watched
284
319
  )
285
320
 
286
321
  swr.start
287
322
  swr.stop
288
323
 
289
- File.exist?(swr.output).should.be.true
290
- File.read(swr.output).should.equal 'watched'
324
+ File.exist?(ShellWatchRun::ENV_FILE).should.be.true
325
+ File.read(ShellWatchRun::ENV_FILE).should.equal 'watched'
291
326
  end
292
327
 
293
328
  it 'should not be executed without immediate option and changes' do
294
329
  swr = ShellWatchRun.new(
295
- options: '',
330
+ options: {},
296
331
  dumper: :watched
297
332
  )
298
333
 
299
334
  swr.start
300
335
  swr.stop
301
336
 
302
- File.exist?(swr.output).should.be.false
337
+ File.exist?(ShellWatchRun::ENV_FILE).should.be.false
303
338
  end
304
339
  end
305
340
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filewatcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Flemming
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-25 00:00:00.000000000 Z
11
+ date: 2017-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bacon
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: trollop
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -45,19 +59,19 @@ dependencies:
45
59
  - !ruby/object:Gem::Version
46
60
  version: '12.0'
47
61
  - !ruby/object:Gem::Dependency
48
- name: bacon
62
+ name: rubocop
49
63
  requirement: !ruby/object:Gem::Requirement
50
64
  requirements:
51
65
  - - "~>"
52
66
  - !ruby/object:Gem::Version
53
- version: '1.2'
67
+ version: '0.51'
54
68
  type: :development
55
69
  prerelease: false
56
70
  version_requirements: !ruby/object:Gem::Requirement
57
71
  requirements:
58
72
  - - "~>"
59
73
  - !ruby/object:Gem::Version
60
- version: '1.2'
74
+ version: '0.51'
61
75
  description: Detect changes in filesystem. Works anywhere.
62
76
  email:
63
77
  - thomas.flemming@gmail.com
@@ -100,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
114
  version: '0'
101
115
  requirements: []
102
116
  rubyforge_project:
103
- rubygems_version: 2.6.13
117
+ rubygems_version: 2.7.2
104
118
  signing_key:
105
119
  specification_version: 4
106
120
  summary: Lighweight filewatcher.