filewatcher 1.1.1 → 2.0.0.beta1

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