filewatcher 1.1.1 → 2.0.0.beta1

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.
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../lib/filewatcher/runner'
4
-
5
- describe Filewatcher::Runner do
6
- before do
7
- @init = proc do |filename|
8
- Filewatcher::Runner.new(filename)
9
- end
10
- end
11
-
12
- describe '#initialize' do
13
- it 'should recieve filename' do
14
- -> { @init.call('file.txt') }
15
- .should.not.raise ArgumentError
16
- end
17
- end
18
-
19
- describe '#command' do
20
- it 'should return correct command for file with .py extension' do
21
- @init.call('file.py').command
22
- .should.equal 'env python file.py'
23
- end
24
-
25
- it 'should return correct command for file with .js extension' do
26
- @init.call('file.js').command
27
- .should.equal 'env node file.js'
28
- end
29
-
30
- it 'should return correct command for file with .rb extension' do
31
- @init.call('file.rb').command
32
- .should.equal 'env ruby file.rb'
33
- end
34
-
35
- it 'should return correct command for file with .pl extension' do
36
- @init.call('file.pl').command
37
- .should.equal 'env perl file.pl'
38
- end
39
-
40
- it 'should return correct command for file with .awk extension' do
41
- @init.call('file.awk').command
42
- .should.equal 'env awk file.awk'
43
- end
44
-
45
- it 'should return correct command for file with .php extension' do
46
- @init.call('file.php').command
47
- .should.equal 'env php file.php'
48
- end
49
-
50
- it 'should return correct command for file with .phtml extension' do
51
- @init.call('file.phtml').command
52
- .should.equal 'env php file.phtml'
53
- end
54
-
55
- it 'should return correct command for file with .php4 extension' do
56
- @init.call('file.php4').command
57
- .should.equal 'env php file.php4'
58
- end
59
-
60
- it 'should return correct command for file with .php3 extension' do
61
- @init.call('file.php3').command
62
- .should.equal 'env php file.php3'
63
- end
64
-
65
- it 'should return correct command for file with .php5 extension' do
66
- @init.call('file.php5').command
67
- .should.equal 'env php file.php5'
68
- end
69
-
70
- it 'should return correct command for file with .phps extension' do
71
- @init.call('file.phps').command
72
- .should.equal 'env php file.phps'
73
- end
74
- end
75
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../lib/filewatcher/version'
4
-
5
- describe Filewatcher::VERSION do
6
- it 'should exist as constant' do
7
- Filewatcher.const_defined?(:VERSION).should.be.true
8
- end
9
-
10
- it 'should be an instance of String' do
11
- Filewatcher::VERSION.class.should.equal String
12
- end
13
- end
@@ -1,238 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bacon'
4
- require 'bacon/custom_matchers_messages'
5
-
6
- begin
7
- require 'pry-byebug'
8
- rescue LoadError
9
- nil
10
- end
11
-
12
- class WatchRun
13
- TMP_DIR = File.join(__dir__, 'tmp')
14
-
15
- attr_reader :filename
16
-
17
- def initialize(
18
- filename: 'tmp_file.txt',
19
- directory: false,
20
- action: :update
21
- )
22
- @filename =
23
- filename =~ %r{^(/|~|[A-Z]:)} ? filename : File.join(TMP_DIR, filename)
24
- @directory = directory
25
- @action = action
26
- end
27
-
28
- def start
29
- File.write(@filename, 'content1') unless @action == :create
30
- end
31
-
32
- def run
33
- start
34
-
35
- make_changes
36
-
37
- stop
38
- end
39
-
40
- def stop
41
- FileUtils.rm_r(@filename) if File.exist?(@filename)
42
- end
43
-
44
- private
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
- SLEEP_MULTIPLIER = Gem::Platform.local.os == 'darwin' ? 5 : 1
59
-
60
- attr_reader :filewatcher, :thread, :watched, :processed
61
-
62
- def initialize(
63
- every: false,
64
- filewatcher: Filewatcher.new(
65
- File.join(TMP_DIR, '**', '*'), interval: 0.1, every: every
66
- ),
67
- **args
68
- )
69
- super(**args)
70
- @filewatcher = filewatcher
71
- end
72
-
73
- def start
74
- super
75
- @thread = thread_initialize
76
- # thread needs a chance to start
77
- wait 12 do
78
- filewatcher.keep_watching
79
- end
80
-
81
- # a little more time
82
- sleep 1 * SLEEP_MULTIPLIER
83
- end
84
-
85
- def stop
86
- thread.exit
87
-
88
- wait 12 do
89
- thread.stop?
90
- end
91
-
92
- # a little more time
93
- sleep 1 * SLEEP_MULTIPLIER
94
-
95
- super
96
- end
97
-
98
- private
99
-
100
- def make_changes
101
- super
102
-
103
- # Some OS, filesystems and Ruby interpretators
104
- # doesn't catch milliseconds of `File.mtime`
105
- wait 12 do
106
- processed.any?
107
- end
108
- end
109
-
110
- def thread_initialize
111
- @watched ||= 0
112
- Thread.new(
113
- @filewatcher, @processed = []
114
- ) do |filewatcher, processed|
115
- filewatcher.watch do |filename, event|
116
- increment_watched
117
- processed.push([filename, event])
118
- end
119
- end
120
- end
121
-
122
- def increment_watched
123
- @watched += 1
124
- end
125
-
126
- def wait(seconds)
127
- max_count = seconds / filewatcher.interval
128
- count = 0
129
- while count < max_count && !yield
130
- sleep filewatcher.interval
131
- count += 1
132
- end
133
- end
134
- end
135
-
136
- class ShellWatchRun < WatchRun
137
- EXECUTABLE = "#{'ruby ' if Gem.win_platform?}" \
138
- "#{File.realpath File.join(__dir__, '..', 'bin', 'filewatcher')}".freeze
139
-
140
- SLEEP_MULTIPLIER = RUBY_PLATFORM == 'java' ? 5 : 1
141
-
142
- ENV_FILE = File.join(TMP_DIR, 'env')
143
-
144
- def initialize(
145
- options: {},
146
- dumper: :watched,
147
- **args
148
- )
149
- super(**args)
150
- @options = options
151
- @options[:interval] ||= 0.1
152
- @options_string =
153
- @options.map { |key, value| "--#{key}=#{value}" }.join(' ')
154
- @dumper = dumper
155
- end
156
-
157
- def start
158
- super
159
-
160
- @pid = spawn_filewatcher
161
-
162
- Process.detach(@pid)
163
-
164
- wait 12 do
165
- pid_state == 'S' && (!@options[:immediate] || File.exist?(ENV_FILE))
166
- end
167
-
168
- # a little more time
169
- sleep 1 * SLEEP_MULTIPLIER
170
- end
171
-
172
- def stop
173
- kill_filewatcher
174
-
175
- wait 12 do
176
- pid_state.empty?
177
- end
178
-
179
- # a little more time
180
- sleep 1 * SLEEP_MULTIPLIER
181
-
182
- super
183
- end
184
-
185
- private
186
-
187
- SPAWN_OPTIONS = Gem.win_platform? ? {} : { pgroup: true }
188
-
189
- def spawn_filewatcher
190
- spawn(
191
- "#{EXECUTABLE} #{@options_string} \"#{@filename}\"" \
192
- " \"ruby #{File.join(__dir__, "dumpers/#{@dumper}_dumper.rb")}\"",
193
- **SPAWN_OPTIONS
194
- )
195
- end
196
-
197
- def make_changes
198
- super
199
-
200
- wait 12 do
201
- File.exist?(ENV_FILE)
202
- end
203
- end
204
-
205
- def kill_filewatcher
206
- if Gem.win_platform?
207
- Process.kill('KILL', @pid)
208
- else
209
- ## Problems: https://github.com/thomasfl/filewatcher/pull/83
210
- ## Solution: https://stackoverflow.com/a/45032252/2630849
211
- Process.kill('TERM', -Process.getpgid(@pid))
212
- Process.waitall
213
- end
214
- end
215
-
216
- def pid_state
217
- ## For macOS output:
218
- ## https://travis-ci.org/thomasfl/filewatcher/jobs/304433538
219
- `ps -ho state -p #{@pid}`.sub('STAT', '').strip
220
- end
221
-
222
- def wait(seconds)
223
- max_count = seconds / @options[:interval]
224
- count = 0
225
- while count < max_count && !yield
226
- sleep @options[:interval]
227
- count += 1
228
- end
229
- end
230
- end
231
-
232
- custom_matcher :include_all_files do |obj, elements|
233
- elements.all? { |element| obj.include? File.expand_path(element) }
234
- end
235
-
236
- def dump_to_env_file(content)
237
- File.write File.join(ShellWatchRun::ENV_FILE), content
238
- end
@@ -1,354 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'fileutils'
4
- require_relative '../lib/filewatcher'
5
-
6
- describe Filewatcher do
7
- before do
8
- FileUtils.mkdir_p WatchRun::TMP_DIR
9
- end
10
-
11
- after do
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
20
- end
21
-
22
- describe '#initialize' do
23
- it 'should exclude selected file patterns' do
24
- wr = RubyWatchRun.new(
25
- filewatcher: Filewatcher.new(
26
- File.expand_path('test/tmp/**/*'),
27
- exclude: File.expand_path('test/tmp/**/*.txt')
28
- )
29
- )
30
-
31
- wr.run
32
-
33
- wr.processed.should.be.empty
34
- end
35
-
36
- it 'should handle absolute paths with globs' do
37
- wr = RubyWatchRun.new(
38
- filewatcher: Filewatcher.new(
39
- File.expand_path('test/tmp/**/*')
40
- )
41
- )
42
-
43
- wr.run
44
-
45
- wr.processed.should.equal(
46
- [[wr.filename, :updated]]
47
- )
48
- end
49
-
50
- it 'should handle globs' do
51
- wr = RubyWatchRun.new(
52
- filewatcher: Filewatcher.new('test/tmp/**/*')
53
- )
54
-
55
- wr.run
56
-
57
- wr.processed.should.equal(
58
- [[wr.filename, :updated]]
59
- )
60
- end
61
-
62
- it 'should handle explicit relative paths with globs' do
63
- wr = RubyWatchRun.new(
64
- filewatcher: Filewatcher.new('./test/tmp/**/*')
65
- )
66
-
67
- wr.run
68
-
69
- wr.processed.should.equal(
70
- [[wr.filename, :updated]]
71
- )
72
- end
73
-
74
- it 'should handle explicit relative paths' do
75
- wr = RubyWatchRun.new(
76
- filewatcher: Filewatcher.new('./test/tmp')
77
- )
78
-
79
- wr.run
80
-
81
- wr.processed.should.equal(
82
- [[wr.filename, :updated]]
83
- )
84
- end
85
-
86
- it 'should handle tilde expansion' do
87
- filename = File.expand_path('~/file_watcher_1.txt')
88
-
89
- wr = RubyWatchRun.new(
90
- filename: filename,
91
- filewatcher: Filewatcher.new('~/file_watcher_1.txt')
92
- )
93
-
94
- wr.run
95
-
96
- wr.processed.should.equal(
97
- [[filename, :updated]]
98
- )
99
- end
100
-
101
- it 'should immediately run with corresponding option' do
102
- wr = RubyWatchRun.new(
103
- filewatcher: Filewatcher.new('**/*', immediate: true)
104
- )
105
-
106
- wr.start
107
- wr.stop
108
-
109
- wr.processed.should.equal [['', '']]
110
- wr.watched.should.be > 0
111
- end
112
-
113
- it 'should not be executed without immediate option and changes' do
114
- wr = RubyWatchRun.new(
115
- filewatcher: Filewatcher.new('**/*', immediate: false)
116
- )
117
-
118
- wr.start
119
- wr.stop
120
-
121
- wr.processed.should.be.empty
122
- wr.watched.should.equal 0
123
- end
124
- end
125
-
126
- describe '#watch' do
127
- it 'should detect file deletions' do
128
- wr = RubyWatchRun.new(action: :delete)
129
-
130
- wr.run
131
-
132
- wr.processed.should.equal(
133
- [[wr.filename, :deleted]]
134
- )
135
- end
136
-
137
- it 'should detect file additions' do
138
- wr = RubyWatchRun.new(action: :create)
139
-
140
- wr.run
141
-
142
- wr.processed.should.equal(
143
- [[wr.filename, :created]]
144
- )
145
- end
146
-
147
- it 'should detect file updates' do
148
- wr = RubyWatchRun.new(action: :update)
149
-
150
- wr.run
151
-
152
- wr.processed.should.equal(
153
- [[wr.filename, :updated]]
154
- )
155
- end
156
-
157
- it 'should detect new files in subfolders' do
158
- FileUtils.mkdir_p subfolder = File.expand_path('test/tmp/new_sub_folder')
159
-
160
- wr = RubyWatchRun.new(
161
- filename: File.join(subfolder, 'file.txt'),
162
- action: :create,
163
- every: true
164
- )
165
- wr.run
166
- wr.processed.should.equal(
167
- [
168
- [subfolder, :updated],
169
- [wr.filename, :created]
170
- ]
171
- )
172
- end
173
-
174
- it 'should detect new subfolders' do
175
- subfolder = 'new_sub_folder'
176
-
177
- wr = RubyWatchRun.new(
178
- filename: subfolder,
179
- directory: true,
180
- action: :create
181
- )
182
-
183
- wr.run
184
-
185
- wr.processed.should.equal(
186
- [[wr.filename, :created]]
187
- )
188
- end
189
- end
190
-
191
- describe '#stop' do
192
- it 'should work' do
193
- wr = RubyWatchRun.new
194
-
195
- wr.start
196
-
197
- wr.filewatcher.stop
198
-
199
- # Proves thread successfully joined
200
- wr.thread.join.should.equal wr.thread
201
- end
202
- end
203
-
204
- describe '#pause, #resume' do
205
- it 'should work' do
206
- wr = RubyWatchRun.new(action: :create, every: true)
207
-
208
- wr.start
209
-
210
- wr.filewatcher.pause
211
-
212
- (1..4).each do |n|
213
- File.write("test/tmp/file#{n}.txt", "content#{n}")
214
- end
215
- # Give filewatcher time to respond
216
- sleep 1 * RubyWatchRun::SLEEP_MULTIPLIER
217
-
218
- # update block should not have been called
219
- wr.processed.should.be.empty
220
-
221
- wr.filewatcher.resume
222
- # Give filewatcher time to respond
223
- sleep 1 * RubyWatchRun::SLEEP_MULTIPLIER
224
-
225
- # update block still should not have been called
226
- wr.processed.should.be.empty
227
-
228
- added_files = (5..7).to_a.map do |n|
229
- File.write(file = "test/tmp/file#{n}.txt", "content#{n}")
230
- file
231
- end
232
- # Give filewatcher time to respond
233
- sleep 1 * RubyWatchRun::SLEEP_MULTIPLIER
234
-
235
- wr.filewatcher.stop
236
- wr.stop
237
- wr.processed.map(&:first).should include_all_files(added_files)
238
- end
239
- end
240
-
241
- describe '#finalize' do
242
- it 'should process all remaining changes' do
243
- wr = RubyWatchRun.new(action: :create, every: true)
244
-
245
- wr.start
246
-
247
- wr.filewatcher.stop
248
- wr.thread.join
249
-
250
- added_files = (1..4).to_a.map do |n|
251
- File.write(file = "test/tmp/file#{n}.txt", "content#{n}")
252
- file
253
- end
254
-
255
- wr.filewatcher.finalize
256
-
257
- wr.processed.map(&:first).should include_all_files(added_files)
258
- end
259
- end
260
-
261
- describe 'executable' do
262
- tmp_dir = ShellWatchRun::TMP_DIR
263
-
264
- it 'should run' do
265
- null_output = Gem.win_platform? ? 'NUL' : '/dev/null'
266
- system("#{ShellWatchRun::EXECUTABLE} > #{null_output}")
267
- .should.be.true
268
- end
269
-
270
- it 'should set correct ENV variables for file creation' do
271
- filename = 'foo.txt'
272
-
273
- swr = ShellWatchRun.new(
274
- filename: filename,
275
- action: :create,
276
- dumper: :env
277
- )
278
-
279
- swr.run
280
-
281
- File.read(ShellWatchRun::ENV_FILE)
282
- .should.equal(
283
- %W[
284
- #{tmp_dir}/#{filename}
285
- #{filename}
286
- created
287
- #{tmp_dir}
288
- #{tmp_dir}/#{filename}
289
- test/tmp/#{filename}
290
- ].join(', ')
291
- )
292
- end
293
-
294
- it 'should set correct ENV variables for file deletion' do
295
- filename = 'foo.txt'
296
-
297
- swr = ShellWatchRun.new(
298
- filename: filename,
299
- action: :delete,
300
- dumper: :env
301
- )
302
-
303
- swr.run
304
-
305
- File.read(ShellWatchRun::ENV_FILE)
306
- .should.equal(
307
- %W[
308
- #{tmp_dir}/#{filename}
309
- #{filename}
310
- deleted
311
- #{tmp_dir}
312
- #{tmp_dir}/#{filename}
313
- test/tmp/#{filename}
314
- ].join(', ')
315
- )
316
- end
317
-
318
- it 'should be executed immediately with corresponding option' do
319
- swr = ShellWatchRun.new(
320
- options: { immediate: true },
321
- dumper: :watched
322
- )
323
-
324
- swr.start
325
- swr.stop
326
-
327
- File.exist?(ShellWatchRun::ENV_FILE).should.be.true
328
- File.read(ShellWatchRun::ENV_FILE).should.equal 'watched'
329
- end
330
-
331
- it 'should not be executed without immediate option and changes' do
332
- swr = ShellWatchRun.new(
333
- options: {},
334
- dumper: :watched
335
- )
336
-
337
- swr.start
338
- swr.stop
339
-
340
- File.exist?(ShellWatchRun::ENV_FILE).should.be.false
341
- end
342
-
343
- it 'should work with restart option' do
344
- swr = ShellWatchRun.new(
345
- options: { restart: true }
346
- )
347
-
348
- swr.run
349
-
350
- File.exist?(ShellWatchRun::ENV_FILE).should.be.true
351
- File.read(ShellWatchRun::ENV_FILE).should.equal 'watched'
352
- end
353
- end
354
- end