filewatcher 1.0.0 → 1.0.1

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
- 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.