filewatcher 0.5.4 → 1.0.0
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 +4 -4
- data/bin/banner.txt +17 -0
- data/bin/filewatcher +61 -124
- data/lib/filewatcher.rb +69 -122
- data/lib/filewatcher/cycles.rb +47 -0
- data/lib/filewatcher/env.rb +29 -0
- data/lib/filewatcher/runner.rb +33 -0
- data/lib/filewatcher/version.rb +7 -0
- data/test/dumpers/env_dumper.rb +10 -0
- data/test/dumpers/watched_dumper.rb +5 -0
- data/test/filewatcher/test_env.rb +70 -0
- data/test/filewatcher/test_runner.rb +75 -0
- data/test/filewatcher/test_version.rb +13 -0
- data/test/helper.rb +134 -0
- data/test/test_filewatcher.rb +266 -142
- metadata +30 -22
- data/LICENSE +0 -20
- data/README.md +0 -269
- data/Rakefile +0 -19
- data/test/fixtures/file1.txt +0 -1
- data/test/fixtures/file2.txt +0 -1
- data/test/fixtures/file3.rb +0 -1
- data/test/fixtures/file4.rb +0 -1
- data/test/fixtures/subdir/file5.rb +0 -1
- data/test/fixtures/subdir/file6.rb +0 -1
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Filewatcher
|
4
|
+
# Module for all cycles in `Filewatcher#watch`
|
5
|
+
module Cycles
|
6
|
+
private
|
7
|
+
|
8
|
+
def main_cycle
|
9
|
+
while @keep_watching
|
10
|
+
@end_snapshot = mtime_snapshot if @pausing
|
11
|
+
|
12
|
+
pausing_cycle
|
13
|
+
|
14
|
+
watching_cycle
|
15
|
+
|
16
|
+
# test and clear @changes to prevent yielding the last
|
17
|
+
# changes twice if @keep_watching has just been set to false
|
18
|
+
trigger_changes
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def pausing_cycle
|
23
|
+
while @keep_watching && @pausing
|
24
|
+
update_spinner('Pausing')
|
25
|
+
sleep @interval
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def watching_cycle
|
30
|
+
while @keep_watching && !filesystem_updated? && !@pausing
|
31
|
+
update_spinner('Watching')
|
32
|
+
sleep @interval
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def trigger_changes(on_update = @on_update)
|
37
|
+
thread = Thread.new do
|
38
|
+
changes = @every ? @changes : @changes.first(1)
|
39
|
+
changes.each do |filename, event|
|
40
|
+
on_update.call(filename, event)
|
41
|
+
end
|
42
|
+
@changes.clear
|
43
|
+
end
|
44
|
+
thread.join
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require_relative '../filewatcher'
|
5
|
+
|
6
|
+
class Filewatcher
|
7
|
+
# Class for building ENV variables for executable
|
8
|
+
class Env
|
9
|
+
def initialize(filename, event)
|
10
|
+
@filename = filename
|
11
|
+
@event = event
|
12
|
+
@path = Pathname.new(@filename)
|
13
|
+
@realpath = @path.realpath
|
14
|
+
@current_dir = Pathname.new(Dir.pwd)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
{
|
19
|
+
'FILEPATH' => (@realpath.to_s if @event != :deleted),
|
20
|
+
'FILENAME' => @filename,
|
21
|
+
'BASENAME' => @path.basename.to_s,
|
22
|
+
'EVENT' => @event.to_s,
|
23
|
+
'DIRNAME' => @path.parent.realpath.to_s,
|
24
|
+
'ABSOLUTE_FILENAME' => @realpath.to_s,
|
25
|
+
'RELATIVE_FILENAME' => @realpath.relative_path_from(@current_dir).to_s
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Filewatcher
|
4
|
+
## Get runner command by filename
|
5
|
+
class Runner
|
6
|
+
## Define runners for `--exec` option
|
7
|
+
RUNNERS = {
|
8
|
+
python: %w[py],
|
9
|
+
node: %w[js],
|
10
|
+
ruby: %w[rb],
|
11
|
+
perl: %w[pl],
|
12
|
+
awk: %w[awk],
|
13
|
+
php: %w[php phtml php4 php3 php5 phps]
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize(filename)
|
17
|
+
@filename = filename
|
18
|
+
@ext = File.extname(filename).delete('.')
|
19
|
+
end
|
20
|
+
|
21
|
+
def command
|
22
|
+
"env #{runner} #{@filename}" if runner
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def runner
|
28
|
+
return @runner if defined?(@runner)
|
29
|
+
@runner, _exts = RUNNERS.find { |_cmd, exts| exts.include? @ext }
|
30
|
+
@runner
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/filewatcher/env'
|
4
|
+
|
5
|
+
describe Filewatcher::Env do
|
6
|
+
describe '#initialize' do
|
7
|
+
it 'should recieve filename and event' do
|
8
|
+
-> { Filewatcher::Env.new(__FILE__, :updated) }
|
9
|
+
.should.not.raise ArgumentError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#to_h' do
|
14
|
+
before do
|
15
|
+
@init = proc do |file: __FILE__, event: :updated|
|
16
|
+
Filewatcher::Env.new(file, event).to_h
|
17
|
+
end
|
18
|
+
@env = @init.call
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should return Hash' do
|
22
|
+
@env.should.be.kind_of Hash
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return Hash with FILEPATH key for created event' do
|
26
|
+
@init.call(event: :created)['FILEPATH']
|
27
|
+
.should.equal File.join(Dir.pwd, __FILE__)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return Hash with FILEPATH key for updated event' do
|
31
|
+
@init.call(event: :updated)['FILEPATH']
|
32
|
+
.should.equal File.join(Dir.pwd, __FILE__)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should return Hash without FILEPATH key for deleted event' do
|
36
|
+
@init.call(event: :deleted)['FILEPATH']
|
37
|
+
.should.equal nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should return Hash with FILENAME key' do
|
41
|
+
@init.call(file: __FILE__)['FILENAME']
|
42
|
+
.should.equal __FILE__
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should return Hash with BASENAME key' do
|
46
|
+
@init.call(file: __FILE__)['BASENAME']
|
47
|
+
.should.equal File.basename(__FILE__)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return Hash with EVENT key' do
|
51
|
+
@init.call(event: :updated)['EVENT']
|
52
|
+
.should.equal 'updated'
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should return Hash with DIRNAME key' do
|
56
|
+
@init.call(file: __FILE__)['DIRNAME']
|
57
|
+
.should.equal File.dirname(File.join(Dir.pwd, __FILE__))
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should return Hash with ABSOLUTE_FILENAME key' do
|
61
|
+
@init.call(file: __FILE__)['ABSOLUTE_FILENAME']
|
62
|
+
.should.equal File.join(Dir.pwd, __FILE__)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should return Hash with RELATIVE_FILENAME key' do
|
66
|
+
@init.call(file: __FILE__)['RELATIVE_FILENAME']
|
67
|
+
.should.equal __FILE__
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,75 @@
|
|
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
|
@@ -0,0 +1,13 @@
|
|
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
|
data/test/helper.rb
ADDED
@@ -0,0 +1,134 @@
|
|
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, :filewatcher, :thread, :watched, :processed
|
16
|
+
|
17
|
+
def initialize(
|
18
|
+
filename: 'tmp_file.txt',
|
19
|
+
directory: false,
|
20
|
+
every: false,
|
21
|
+
filewatcher: Filewatcher.new(
|
22
|
+
File.join(TMP_DIR, '**', '*'), interval: 0.1, every: every
|
23
|
+
),
|
24
|
+
action: :update
|
25
|
+
)
|
26
|
+
@filename =
|
27
|
+
filename.start_with?('/', '~') ? filename : File.join(TMP_DIR, filename)
|
28
|
+
@directory = directory
|
29
|
+
@filewatcher = filewatcher
|
30
|
+
@action = action
|
31
|
+
end
|
32
|
+
|
33
|
+
def start
|
34
|
+
File.write(@filename, 'content1') unless @action == :create
|
35
|
+
|
36
|
+
@thread = thread_initialize
|
37
|
+
sleep 3 # thread needs a chance to start
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
start
|
42
|
+
|
43
|
+
make_changes
|
44
|
+
# Some OS, filesystems and Ruby interpretators
|
45
|
+
# doesn't catch milliseconds of `File.mtime`
|
46
|
+
sleep 3
|
47
|
+
|
48
|
+
stop
|
49
|
+
end
|
50
|
+
|
51
|
+
def stop
|
52
|
+
FileUtils.rm_r(@filename) if File.exist?(@filename)
|
53
|
+
@thread.exit
|
54
|
+
sleep 3
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def thread_initialize
|
60
|
+
@watched ||= 0
|
61
|
+
Thread.new(
|
62
|
+
@filewatcher, @processed = []
|
63
|
+
) do |filewatcher, processed|
|
64
|
+
filewatcher.watch do |filename, event|
|
65
|
+
increment_watched
|
66
|
+
processed.push([filename, event])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def increment_watched
|
72
|
+
@watched += 1
|
73
|
+
end
|
74
|
+
|
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')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class ShellWatchRun
|
83
|
+
EXECUTABLE = "#{'ruby ' if Gem.win_platform?}" \
|
84
|
+
"#{File.realpath File.join(__dir__, '..', 'bin', 'filewatcher')}".freeze
|
85
|
+
|
86
|
+
attr_reader :output
|
87
|
+
|
88
|
+
def initialize(
|
89
|
+
options: '',
|
90
|
+
dumper: :watched,
|
91
|
+
output: File.join(WatchRun::TMP_DIR, 'env')
|
92
|
+
)
|
93
|
+
@options = options
|
94
|
+
@dumper = dumper
|
95
|
+
@output = output
|
96
|
+
end
|
97
|
+
|
98
|
+
def start
|
99
|
+
@pid = spawn(
|
100
|
+
"#{EXECUTABLE} #{@options} \"#{WatchRun::TMP_DIR}/foo*\"" \
|
101
|
+
" \"ruby #{File.join(__dir__, 'dumpers', "#{@dumper}_dumper.rb")}\""
|
102
|
+
)
|
103
|
+
Process.detach(@pid)
|
104
|
+
sleep 12
|
105
|
+
end
|
106
|
+
|
107
|
+
def run
|
108
|
+
start
|
109
|
+
|
110
|
+
make_changes
|
111
|
+
|
112
|
+
stop
|
113
|
+
end
|
114
|
+
|
115
|
+
def stop
|
116
|
+
Process.kill('KILL', @pid)
|
117
|
+
sleep 6
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def make_changes
|
123
|
+
FileUtils.touch "#{WatchRun::TMP_DIR}/foo.txt"
|
124
|
+
sleep 12
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
custom_matcher :include_all_files do |obj, elements|
|
129
|
+
elements.all? { |element| obj.include? File.expand_path(element) }
|
130
|
+
end
|
131
|
+
|
132
|
+
def dump_to_env_file(content)
|
133
|
+
File.write File.join(WatchRun::TMP_DIR, 'env'), content
|
134
|
+
end
|
data/test/test_filewatcher.rb
CHANGED
@@ -1,181 +1,305 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
3
|
require 'fileutils'
|
4
|
-
|
4
|
+
require_relative '../lib/filewatcher'
|
5
5
|
|
6
|
+
describe Filewatcher do
|
7
|
+
before do
|
8
|
+
FileUtils.mkdir_p WatchRun::TMP_DIR
|
9
|
+
end
|
6
10
|
|
11
|
+
after do
|
12
|
+
FileUtils.rm_r WatchRun::TMP_DIR
|
13
|
+
end
|
7
14
|
|
8
|
-
describe
|
15
|
+
describe '#initialize' do
|
16
|
+
it 'should exclude selected file patterns' do
|
17
|
+
wr = WatchRun.new(
|
18
|
+
filewatcher: Filewatcher.new(
|
19
|
+
File.expand_path('test/tmp/**/*'),
|
20
|
+
exclude: File.expand_path('test/tmp/**/*.txt')
|
21
|
+
)
|
22
|
+
)
|
9
23
|
|
10
|
-
|
11
|
-
%w(test/fixtures/file4.rb
|
12
|
-
test/fixtures/subdir/file6.rb
|
13
|
-
test/fixtures/subdir/file5.rb
|
14
|
-
test/fixtures/file2.txt
|
15
|
-
test/fixtures/file1.txt
|
16
|
-
test/fixtures/file3.rb)
|
24
|
+
wr.run
|
17
25
|
|
18
|
-
|
26
|
+
wr.processed.should.be.empty
|
27
|
+
end
|
19
28
|
|
20
|
-
|
29
|
+
it 'should handle absolute paths with globs' do
|
30
|
+
wr = WatchRun.new(
|
31
|
+
filewatcher: Filewatcher.new(
|
32
|
+
File.expand_path('test/tmp/**/*')
|
33
|
+
)
|
34
|
+
)
|
21
35
|
|
22
|
-
|
23
|
-
FileUtils.rm_rf subfolder
|
24
|
-
end
|
36
|
+
wr.run
|
25
37
|
|
26
|
-
|
27
|
-
|
28
|
-
|
38
|
+
wr.processed.should.equal(
|
39
|
+
[[wr.filename, :updated]]
|
40
|
+
)
|
41
|
+
end
|
29
42
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
test/fixtures/subdir/file6.rb
|
35
|
-
test/fixtures/subdir/file5.rb
|
36
|
-
test/fixtures/file3.rb)
|
37
|
-
filewatcher.filenames.should.satisfy &includes_all(filtered_fixtures.map { |it| File.expand_path(it) })
|
38
|
-
end
|
39
|
-
|
40
|
-
it "should handle absolute paths with globs" do
|
41
|
-
filewatcher = FileWatcher.new(File.expand_path('test/fixtures/**/*'))
|
43
|
+
it 'should handle globs' do
|
44
|
+
wr = WatchRun.new(
|
45
|
+
filewatcher: Filewatcher.new('test/tmp/**/*')
|
46
|
+
)
|
42
47
|
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should handle globs" do
|
47
|
-
filewatcher = FileWatcher.new('test/fixtures/**/*')
|
48
|
+
wr.run
|
48
49
|
|
49
|
-
|
50
|
-
|
50
|
+
wr.processed.should.equal(
|
51
|
+
[[wr.filename, :updated]]
|
52
|
+
)
|
53
|
+
end
|
51
54
|
|
55
|
+
it 'should handle explicit relative paths with globs' do
|
56
|
+
wr = WatchRun.new(
|
57
|
+
filewatcher: Filewatcher.new('./test/tmp/**/*')
|
58
|
+
)
|
52
59
|
|
53
|
-
|
54
|
-
filewatcher = FileWatcher.new('./test/fixtures/**/*')
|
60
|
+
wr.run
|
55
61
|
|
56
|
-
|
57
|
-
|
62
|
+
wr.processed.should.equal(
|
63
|
+
[[wr.filename, :updated]]
|
64
|
+
)
|
65
|
+
end
|
58
66
|
|
59
|
-
|
60
|
-
|
67
|
+
it 'should handle explicit relative paths' do
|
68
|
+
wr = WatchRun.new(
|
69
|
+
filewatcher: Filewatcher.new('./test/tmp')
|
70
|
+
)
|
61
71
|
|
62
|
-
|
63
|
-
|
72
|
+
wr.run
|
73
|
+
|
74
|
+
wr.processed.should.equal(
|
75
|
+
[[wr.filename, :updated]]
|
76
|
+
)
|
77
|
+
end
|
64
78
|
|
65
|
-
|
66
|
-
|
67
|
-
open(filename, 'w') { |f| f.puts 'content1' }
|
79
|
+
it 'should handle tilde expansion' do
|
80
|
+
filename = File.expand_path('~/file_watcher_1.txt')
|
68
81
|
|
69
|
-
|
82
|
+
wr = WatchRun.new(
|
83
|
+
filename: filename,
|
84
|
+
filewatcher: Filewatcher.new('~/file_watcher_1.txt')
|
85
|
+
)
|
70
86
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
87
|
+
wr.run
|
88
|
+
|
89
|
+
wr.processed.should.equal(
|
90
|
+
[[filename, :updated]]
|
91
|
+
)
|
75
92
|
end
|
76
|
-
end
|
77
93
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
filewatcher.filesystem_updated?.should.be.false
|
83
|
-
FileUtils.rm(filename)
|
84
|
-
filewatcher.filesystem_updated?.should.be.true
|
85
|
-
end
|
94
|
+
it 'should immediately run with corresponding option' do
|
95
|
+
wr = WatchRun.new(
|
96
|
+
filewatcher: Filewatcher.new('**/*', immediate: true)
|
97
|
+
)
|
86
98
|
|
87
|
-
|
88
|
-
|
89
|
-
FileUtils.rm(filename) if File.exists?(filename)
|
90
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
91
|
-
filewatcher.filesystem_updated?.should.be.false
|
92
|
-
open(filename,"w") { |f| f.puts "content1" }
|
93
|
-
filewatcher.filesystem_updated?.should.be.true
|
94
|
-
end
|
99
|
+
wr.start
|
100
|
+
wr.stop
|
95
101
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
100
|
-
filewatcher.filesystem_updated?.should.be.false
|
101
|
-
sleep 1
|
102
|
-
open(filename,"w") { |f| f.puts "content2" }
|
103
|
-
filewatcher.filesystem_updated?.should.be.true
|
104
|
-
end
|
102
|
+
wr.processed.should.equal [['', '']]
|
103
|
+
wr.watched.should.be > 0
|
104
|
+
end
|
105
105
|
|
106
|
-
|
107
|
-
|
106
|
+
it 'should not be executed without immediate option and changes' do
|
107
|
+
wr = WatchRun.new(
|
108
|
+
filewatcher: Filewatcher.new('**/*', immediate: false)
|
109
|
+
)
|
108
110
|
|
109
|
-
|
110
|
-
|
111
|
+
wr.start
|
112
|
+
wr.stop
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
+
wr.processed.should.be.empty
|
115
|
+
wr.watched.should.equal 0
|
116
|
+
end
|
114
117
|
end
|
115
118
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
+
describe '#watch' do
|
120
|
+
it 'should detect file deletions' do
|
121
|
+
wr = WatchRun.new(action: :delete)
|
122
|
+
|
123
|
+
wr.run
|
124
|
+
|
125
|
+
wr.processed.should.equal(
|
126
|
+
[[wr.filename, :deleted]]
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should detect file additions' do
|
131
|
+
wr = WatchRun.new(action: :create)
|
132
|
+
|
133
|
+
wr.run
|
134
|
+
|
135
|
+
wr.processed.should.equal(
|
136
|
+
[[wr.filename, :created]]
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should detect file updates' do
|
141
|
+
wr = WatchRun.new(action: :update)
|
142
|
+
|
143
|
+
wr.run
|
144
|
+
|
145
|
+
wr.processed.should.equal(
|
146
|
+
[[wr.filename, :updated]]
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should detect new files in subfolders' do
|
151
|
+
FileUtils.mkdir_p subfolder = File.expand_path('test/tmp/new_sub_folder')
|
152
|
+
|
153
|
+
wr = WatchRun.new(
|
154
|
+
filename: File.join(subfolder, 'file.txt'),
|
155
|
+
action: :create,
|
156
|
+
every: true
|
157
|
+
)
|
158
|
+
wr.run
|
159
|
+
wr.processed.should.equal(
|
160
|
+
[
|
161
|
+
[subfolder, :updated],
|
162
|
+
[wr.filename, :created]
|
163
|
+
]
|
164
|
+
)
|
165
|
+
end
|
119
166
|
|
120
|
-
|
121
|
-
|
167
|
+
it 'should detect new subfolders' do
|
168
|
+
subfolder = 'new_sub_folder'
|
169
|
+
|
170
|
+
wr = WatchRun.new(
|
171
|
+
filename: subfolder,
|
172
|
+
directory: true,
|
173
|
+
action: :create
|
174
|
+
)
|
175
|
+
|
176
|
+
wr.run
|
177
|
+
|
178
|
+
wr.processed.should.equal(
|
179
|
+
[[wr.filename, :created]]
|
180
|
+
)
|
181
|
+
end
|
122
182
|
end
|
123
183
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
184
|
+
describe '#stop' do
|
185
|
+
it 'should work' do
|
186
|
+
wr = WatchRun.new
|
187
|
+
|
188
|
+
wr.start
|
189
|
+
|
190
|
+
wr.filewatcher.stop
|
191
|
+
|
192
|
+
# Proves thread successfully joined
|
193
|
+
wr.thread.join.should.equal wr.thread
|
194
|
+
end
|
130
195
|
end
|
131
196
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
197
|
+
describe '#pause, #resume' do
|
198
|
+
it 'should work' do
|
199
|
+
wr = WatchRun.new(action: :create, every: true)
|
200
|
+
|
201
|
+
wr.start
|
202
|
+
|
203
|
+
wr.filewatcher.pause
|
204
|
+
|
205
|
+
(1..4).each do |n|
|
206
|
+
File.write("test/tmp/file#{n}.txt", "content#{n}")
|
207
|
+
end
|
208
|
+
sleep 0.2 # Give filewatcher time to respond
|
209
|
+
|
210
|
+
# update block should not have been called
|
211
|
+
wr.processed.should.be.empty
|
212
|
+
|
213
|
+
wr.filewatcher.resume
|
214
|
+
sleep 0.2 # Give filewatcher time to respond
|
215
|
+
|
216
|
+
# update block still should not have been called
|
217
|
+
wr.processed.should.be.empty
|
218
|
+
|
219
|
+
added_files = (5..7).to_a.map do |n|
|
220
|
+
File.write(file = "test/tmp/file#{n}.txt", "content#{n}")
|
221
|
+
file
|
222
|
+
end
|
223
|
+
sleep 0.2 # Give filewatcher time to respond
|
224
|
+
|
225
|
+
wr.filewatcher.stop
|
226
|
+
wr.stop
|
227
|
+
wr.processed.map(&:first).should include_all_files(added_files)
|
228
|
+
end
|
157
229
|
end
|
158
230
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
puts "Actual: #{processed.inspect}"
|
178
|
-
# processed.should.satisfy &includes_all(added_files)
|
231
|
+
describe '#finalize' do
|
232
|
+
it 'should process all remaining changes' do
|
233
|
+
wr = WatchRun.new(action: :create, every: true)
|
234
|
+
|
235
|
+
wr.start
|
236
|
+
|
237
|
+
wr.filewatcher.stop
|
238
|
+
wr.thread.join
|
239
|
+
|
240
|
+
added_files = (1..4).to_a.map do |n|
|
241
|
+
File.write(file = "test/tmp/file#{n}.txt", "content#{n}")
|
242
|
+
file
|
243
|
+
end
|
244
|
+
|
245
|
+
wr.filewatcher.finalize
|
246
|
+
|
247
|
+
wr.processed.map(&:first).should include_all_files(added_files)
|
248
|
+
end
|
179
249
|
end
|
180
250
|
|
251
|
+
describe 'executable' do
|
252
|
+
tmp_dir = WatchRun::TMP_DIR
|
253
|
+
|
254
|
+
it 'should run' do
|
255
|
+
null_output = Gem.win_platform? ? 'NUL' : '/dev/null'
|
256
|
+
system("#{ShellWatchRun::EXECUTABLE} > #{null_output}")
|
257
|
+
.should.be.true
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'should set correct ENV variables' do
|
261
|
+
swr = ShellWatchRun.new(
|
262
|
+
dumper: :env
|
263
|
+
)
|
264
|
+
|
265
|
+
swr.run
|
266
|
+
|
267
|
+
File.read(swr.output)
|
268
|
+
.should.equal(
|
269
|
+
%W[
|
270
|
+
#{tmp_dir}/foo.txt
|
271
|
+
foo.txt
|
272
|
+
created
|
273
|
+
#{tmp_dir}
|
274
|
+
#{tmp_dir}/foo.txt
|
275
|
+
test/tmp/foo.txt
|
276
|
+
].join(', ')
|
277
|
+
)
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'should be executed immediately with corresponding option' do
|
281
|
+
swr = ShellWatchRun.new(
|
282
|
+
options: '--immediate',
|
283
|
+
dumper: :watched
|
284
|
+
)
|
285
|
+
|
286
|
+
swr.start
|
287
|
+
swr.stop
|
288
|
+
|
289
|
+
File.exist?(swr.output).should.be.true
|
290
|
+
File.read(swr.output).should.equal 'watched'
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'should not be executed without immediate option and changes' do
|
294
|
+
swr = ShellWatchRun.new(
|
295
|
+
options: '',
|
296
|
+
dumper: :watched
|
297
|
+
)
|
298
|
+
|
299
|
+
swr.start
|
300
|
+
swr.stop
|
301
|
+
|
302
|
+
File.exist?(swr.output).should.be.false
|
303
|
+
end
|
304
|
+
end
|
181
305
|
end
|