listen 2.0.0 → 2.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 +4 -4
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/.travis.yml +17 -0
- data/.yardopts +11 -0
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +16 -0
- data/Guardfile +6 -0
- data/{LICENSE → LICENSE.txt} +2 -0
- data/README.md +4 -2
- data/Rakefile +5 -0
- data/lib/listen/listener.rb +1 -2
- data/lib/listen/version.rb +1 -1
- data/listen.gemspec +30 -0
- data/spec/acceptance/listen_spec.rb +204 -0
- data/spec/lib/listen/adapter/base_spec.rb +44 -0
- data/spec/lib/listen/adapter/bsd_spec.rb +39 -0
- data/spec/lib/listen/adapter/darwin_spec.rb +39 -0
- data/spec/lib/listen/adapter/linux_spec.rb +39 -0
- data/spec/lib/listen/adapter/polling_spec.rb +38 -0
- data/spec/lib/listen/adapter/windows_spec.rb +37 -0
- data/spec/lib/listen/adapter_spec.rb +77 -0
- data/spec/lib/listen/change_spec.rb +97 -0
- data/spec/lib/listen/directory_spec.rb +153 -0
- data/spec/lib/listen/file_spec.rb +112 -0
- data/spec/lib/listen/listener_spec.rb +269 -0
- data/spec/lib/listen/record_spec.rb +92 -0
- data/spec/lib/listen/silencer_spec.rb +62 -0
- data/spec/lib/listen_spec.rb +10 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/acceptance_helper.rb +45 -0
- data/spec/support/actor_helper.rb +13 -0
- data/spec/support/fixtures_helper.rb +27 -0
- data/spec/support/platform_helper.rb +15 -0
- metadata +75 -12
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Listen::File do
|
4
|
+
let(:record) { double(Listen::Record, async: double(set_path: true, unset_path: true)) }
|
5
|
+
let(:path) { Pathname.new(Dir.pwd) }
|
6
|
+
around { |example| fixtures { |path| example.run } }
|
7
|
+
before { Celluloid::Actor.stub(:[]).with(:listen_record) { record } }
|
8
|
+
|
9
|
+
describe "#change" do
|
10
|
+
let(:file_path) { path.join('file.rb') }
|
11
|
+
let(:file) { Listen::File.new(file_path) }
|
12
|
+
let(:expected_data) {
|
13
|
+
if darwin?
|
14
|
+
{ type: 'File', mtime: kind_of(Float), mode: kind_of(Integer), md5: kind_of(String) }
|
15
|
+
else
|
16
|
+
{ type: 'File', mtime: kind_of(Float), mode: kind_of(Integer) }
|
17
|
+
end
|
18
|
+
}
|
19
|
+
|
20
|
+
context "path present in record" do
|
21
|
+
let(:record_mtime) { nil }
|
22
|
+
let(:record_md5) { nil }
|
23
|
+
let(:record_mode) { nil }
|
24
|
+
let(:record_data) { { type: 'File', mtime: record_mtime, md5: record_md5, mode: record_mode } }
|
25
|
+
before { record.stub_chain(:future, :file_data) { double(value: record_data) } }
|
26
|
+
|
27
|
+
context "non-existing path" do
|
28
|
+
it "returns added" do
|
29
|
+
expect(file.change).to eq :removed
|
30
|
+
end
|
31
|
+
it "sets path in record" do
|
32
|
+
expect(record.async).to receive(:unset_path).with(file_path)
|
33
|
+
file.change
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "existing path" do
|
38
|
+
around { |example| touch file_path; example.run }
|
39
|
+
|
40
|
+
context "old record path mtime" do
|
41
|
+
let(:record_mtime) { (Time.now - 1).to_f }
|
42
|
+
|
43
|
+
it "returns modified" do
|
44
|
+
expect(file.change).to eq :modified
|
45
|
+
end
|
46
|
+
|
47
|
+
it "sets path in record with expected data" do
|
48
|
+
expect(record.async).to receive(:set_path).with(file_path, expected_data)
|
49
|
+
file.change
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "same record path mtime" do
|
54
|
+
let(:record_mtime) { ::File.lstat(file_path).mtime.to_f }
|
55
|
+
let(:record_mode) { ::File.lstat(file_path).mode }
|
56
|
+
let(:record_md5) { Digest::MD5.file(file_path).digest }
|
57
|
+
|
58
|
+
context "same record path mode" do
|
59
|
+
it "returns nil" do
|
60
|
+
expect(file.change).to be_nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "diferent record path mode" do
|
65
|
+
let(:record_mode) { 'foo' }
|
66
|
+
|
67
|
+
it "returns modified" do
|
68
|
+
expect(file.change).to eq :modified
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "same record path md5" do
|
73
|
+
it "returns nil" do
|
74
|
+
expect(file.change).to be_nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "different record path md5" do
|
79
|
+
let(:record_md5) { 'foo' }
|
80
|
+
|
81
|
+
it "returns modified" do
|
82
|
+
expect(file.change).to eq :modified
|
83
|
+
end
|
84
|
+
it "sets path in record with expected data" do
|
85
|
+
expect(record.async).to receive(:set_path).with(file_path, expected_data)
|
86
|
+
file.change
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "path not present in record" do
|
95
|
+
before { record.stub_chain(:future, :file_data) { double(value: {}) } }
|
96
|
+
|
97
|
+
context "existing path" do
|
98
|
+
around { |example| touch file_path; example.run }
|
99
|
+
|
100
|
+
it "returns added" do
|
101
|
+
expect(file.change).to eq :added
|
102
|
+
end
|
103
|
+
|
104
|
+
it "sets path in record with expected data" do
|
105
|
+
expect(record.async).to receive(:set_path).with(file_path, expected_data)
|
106
|
+
file.change
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Listen::Listener do
|
4
|
+
let(:listener) { Listen::Listener.new(options) }
|
5
|
+
let(:options) { {} }
|
6
|
+
let(:record) { double(Listen::Record, terminate: true, build: true) }
|
7
|
+
let(:silencer) { double(Listen::Silencer, terminate: true) }
|
8
|
+
let(:adapter) { double(Listen::Adapter::Base) }
|
9
|
+
let(:change_pool) { double(Listen::Change, terminate: true) }
|
10
|
+
let(:change_pool_async) { double('ChangePoolAsync') }
|
11
|
+
before {
|
12
|
+
Celluloid::Actor.stub(:[]).with(:listen_silencer) { silencer }
|
13
|
+
Celluloid::Actor.stub(:[]).with(:listen_adapter) { adapter }
|
14
|
+
Celluloid::Actor.stub(:[]).with(:listen_record) { record }
|
15
|
+
Celluloid::Actor.stub(:[]).with(:listen_change_pool) { change_pool }
|
16
|
+
}
|
17
|
+
|
18
|
+
describe "initialize" do
|
19
|
+
it "sets paused to false" do
|
20
|
+
expect(listener).not_to be_paused
|
21
|
+
end
|
22
|
+
|
23
|
+
it "sets block" do
|
24
|
+
block = Proc.new { |modified, added, removed| }
|
25
|
+
listener = Listen::Listener.new('lib', &block)
|
26
|
+
expect(listener.block).not_to be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sets directories with realpath" do
|
30
|
+
listener = Listen::Listener.new('lib', 'spec')
|
31
|
+
expect(listener.directories).to eq [Pathname.new("#{Dir.pwd}/lib"), Pathname.new("#{Dir.pwd}/spec")]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "options" do
|
36
|
+
it "sets default options" do
|
37
|
+
expect(listener.options).to eq({
|
38
|
+
debug: false,
|
39
|
+
latency: nil,
|
40
|
+
force_polling: false,
|
41
|
+
polling_fallback_message: nil })
|
42
|
+
end
|
43
|
+
|
44
|
+
it "sets new options on initialize" do
|
45
|
+
listener = Listen::Listener.new('lib', latency: 1.234)
|
46
|
+
expect(listener.options).to eq({
|
47
|
+
debug: false,
|
48
|
+
latency: 1.234,
|
49
|
+
force_polling: false,
|
50
|
+
polling_fallback_message: nil })
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#start" do
|
55
|
+
before {
|
56
|
+
Listen::Silencer.stub(:new)
|
57
|
+
Listen::Change.stub(:pool)
|
58
|
+
Listen::Adapter.stub(:new)
|
59
|
+
Listen::Record.stub(:new)
|
60
|
+
Celluloid::Actor.stub(:[]=)
|
61
|
+
Celluloid.stub(:cores) { 1 }
|
62
|
+
adapter.stub_chain(:async, :start)
|
63
|
+
}
|
64
|
+
|
65
|
+
it "traps INT signal" do
|
66
|
+
expect(Signal).to receive(:trap).with('INT')
|
67
|
+
listener.start
|
68
|
+
end
|
69
|
+
|
70
|
+
it "registers silencer" do
|
71
|
+
expect(Listen::Silencer).to receive(:new).with(listener.options) { silencer }
|
72
|
+
expect(Celluloid::Actor).to receive(:[]=).with(:listen_silencer, silencer)
|
73
|
+
listener.start
|
74
|
+
end
|
75
|
+
|
76
|
+
it "registers change_pool" do
|
77
|
+
expect(Listen::Change).to receive(:pool).with(args: listener) { change_pool }
|
78
|
+
expect(Celluloid::Actor).to receive(:[]=).with(:listen_change_pool, change_pool)
|
79
|
+
listener.start
|
80
|
+
end
|
81
|
+
|
82
|
+
it "registers adaper" do
|
83
|
+
expect(Listen::Adapter).to receive(:new).with(listener) { adapter }
|
84
|
+
expect(Celluloid::Actor).to receive(:[]=).with(:listen_adapter, adapter)
|
85
|
+
listener.start
|
86
|
+
end
|
87
|
+
|
88
|
+
it "registers record" do
|
89
|
+
expect(Listen::Record).to receive(:new).with(listener) { record }
|
90
|
+
expect(Celluloid::Actor).to receive(:[]=).with(:listen_record, record)
|
91
|
+
listener.start
|
92
|
+
end
|
93
|
+
|
94
|
+
it "builds record" do
|
95
|
+
expect(record).to receive(:build)
|
96
|
+
listener.start
|
97
|
+
end
|
98
|
+
|
99
|
+
it "sets paused to false" do
|
100
|
+
listener.start
|
101
|
+
expect(listener.paused).to be_false
|
102
|
+
end
|
103
|
+
|
104
|
+
it "starts adapter asynchronously" do
|
105
|
+
async_stub = double
|
106
|
+
expect(adapter).to receive(:async) { async_stub }
|
107
|
+
expect(async_stub).to receive(:start)
|
108
|
+
listener.start
|
109
|
+
end
|
110
|
+
|
111
|
+
it "starts adapter asynchronously" do
|
112
|
+
async_stub = double
|
113
|
+
expect(adapter).to receive(:async) { async_stub }
|
114
|
+
expect(async_stub).to receive(:start)
|
115
|
+
listener.start
|
116
|
+
end
|
117
|
+
|
118
|
+
it "calls block on changes" do
|
119
|
+
listener.changes = [{ modified: 'foo' }]
|
120
|
+
block_stub = double('block')
|
121
|
+
listener.block = block_stub
|
122
|
+
expect(block_stub).to receive(:call).with(['foo'], [], [])
|
123
|
+
listener.start
|
124
|
+
sleep 0.01
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#stop" do
|
129
|
+
let(:thread) { double(kill: true) }
|
130
|
+
before {
|
131
|
+
Celluloid::Actor.stub(:kill)
|
132
|
+
listener.stub(:thread) { thread }
|
133
|
+
}
|
134
|
+
|
135
|
+
it "kills thread" do
|
136
|
+
expect(thread).to receive(:kill)
|
137
|
+
listener.stop
|
138
|
+
end
|
139
|
+
|
140
|
+
it "terminates silencer" do
|
141
|
+
expect(silencer).to receive(:terminate)
|
142
|
+
listener.stop
|
143
|
+
end
|
144
|
+
|
145
|
+
it "kills adapter" do
|
146
|
+
expect(Celluloid::Actor).to receive(:kill).with(adapter)
|
147
|
+
listener.stop
|
148
|
+
end
|
149
|
+
|
150
|
+
it "terminates change_pool" do
|
151
|
+
expect(change_pool).to receive(:terminate)
|
152
|
+
listener.stop
|
153
|
+
end
|
154
|
+
|
155
|
+
it "terminates record" do
|
156
|
+
expect(record).to receive(:terminate)
|
157
|
+
listener.stop
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "#pause" do
|
162
|
+
it "sets paused to true" do
|
163
|
+
listener.pause
|
164
|
+
expect(listener.paused).to be_true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "#unpause" do
|
169
|
+
it "builds record" do
|
170
|
+
expect(record).to receive(:build)
|
171
|
+
listener.unpause
|
172
|
+
end
|
173
|
+
|
174
|
+
it "sets paused to false" do
|
175
|
+
record.stub(:build)
|
176
|
+
listener.unpause
|
177
|
+
expect(listener.paused).to be_false
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "#paused?" do
|
182
|
+
it "returns true when paused" do
|
183
|
+
listener.paused = true
|
184
|
+
expect(listener).to be_paused
|
185
|
+
end
|
186
|
+
it "returns false when not paused (nil)" do
|
187
|
+
listener.paused = nil
|
188
|
+
expect(listener).not_to be_paused
|
189
|
+
end
|
190
|
+
it "returns false when not paused (false)" do
|
191
|
+
listener.paused = false
|
192
|
+
expect(listener).not_to be_paused
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "#paused?" do
|
197
|
+
it "returns true when not paused (false)" do
|
198
|
+
listener.paused = false
|
199
|
+
expect(listener.listen?).to be_true
|
200
|
+
end
|
201
|
+
it "returns false when not paused (nil)" do
|
202
|
+
listener.paused = nil
|
203
|
+
expect(listener.listen?).to be_false
|
204
|
+
end
|
205
|
+
it "returns false when paused" do
|
206
|
+
listener.paused = true
|
207
|
+
expect(listener.listen?).to be_false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "#ignore" do
|
212
|
+
let(:new_silencer) { double(Listen::Silencer) }
|
213
|
+
before { Celluloid::Actor.stub(:[]=) }
|
214
|
+
|
215
|
+
it "resets silencer actor with new pattern" do
|
216
|
+
expect(Listen::Silencer).to receive(:new).with(hash_including(ignore: [nil, /foo/])) { new_silencer }
|
217
|
+
expect(Celluloid::Actor).to receive(:[]=).with(:listen_silencer, new_silencer)
|
218
|
+
listener.ignore(/foo/)
|
219
|
+
end
|
220
|
+
|
221
|
+
context "with existing ignore options" do
|
222
|
+
let(:options) { { ignore: /bar/ } }
|
223
|
+
|
224
|
+
it "adds up to existing ignore options" do
|
225
|
+
expect(Listen::Silencer).to receive(:new).with(hash_including(ignore: [/bar/, /foo/]))
|
226
|
+
listener.ignore(/foo/)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context "with existing ignore options (array)" do
|
231
|
+
let(:options) { { ignore: [/bar/] } }
|
232
|
+
|
233
|
+
it "adds up to existing ignore options" do
|
234
|
+
expect(Listen::Silencer).to receive(:new).with(hash_including(ignore: [[/bar/], /foo/]))
|
235
|
+
listener.ignore(/foo/)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
describe "#ignore!" do
|
241
|
+
let(:new_silencer) { double(Listen::Silencer) }
|
242
|
+
before { Celluloid::Actor.stub(:[]=) }
|
243
|
+
|
244
|
+
it "resets silencer actor with new pattern" do
|
245
|
+
expect(Listen::Silencer).to receive(:new).with(hash_including(ignore!: /foo/)) { new_silencer }
|
246
|
+
expect(Celluloid::Actor).to receive(:[]=).with(:listen_silencer, new_silencer)
|
247
|
+
listener.ignore!(/foo/)
|
248
|
+
end
|
249
|
+
|
250
|
+
context "with existing ignore! options" do
|
251
|
+
let(:options) { { ignore!: /bar/ } }
|
252
|
+
|
253
|
+
it "overwrites existing ignore options" do
|
254
|
+
expect(Listen::Silencer).to receive(:new).with(hash_including(ignore!: [/foo/]))
|
255
|
+
listener.ignore!([/foo/])
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context "with existing ignore options" do
|
260
|
+
let(:options) { { ignore: /bar/ } }
|
261
|
+
|
262
|
+
it "deletes ignore options" do
|
263
|
+
expect(Listen::Silencer).to receive(:new).with(hash_not_including(ignore: /bar/))
|
264
|
+
listener.ignore!([/foo/])
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Listen::Record do
|
4
|
+
let(:listener) { double(Listen::Listener, options: {}) }
|
5
|
+
let(:record) { Listen::Record.new(listener) }
|
6
|
+
let(:path) { '/dir/path/file.rb' }
|
7
|
+
let(:data) { { type: 'File' } }
|
8
|
+
|
9
|
+
describe "#set_path" do
|
10
|
+
it "sets path by spliting direname and basename" do
|
11
|
+
record.set_path(path, data)
|
12
|
+
expect(record.paths).to eq({ '/dir/path' => { 'file.rb' => data } })
|
13
|
+
end
|
14
|
+
|
15
|
+
it "sets path and keeps old data not overwritten" do
|
16
|
+
record.set_path(path, data.merge(foo: 1, bar: 2))
|
17
|
+
record.set_path(path, data.merge(foo: 3))
|
18
|
+
expect(record.paths).to eq({ '/dir/path' => { 'file.rb' => data.merge(foo: 3, bar: 2) } })
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#unset_path" do
|
23
|
+
context "path is present" do
|
24
|
+
before { record.set_path(path, data) }
|
25
|
+
|
26
|
+
it "unsets path" do
|
27
|
+
record.unset_path(path)
|
28
|
+
expect(record.paths).to eq({ '/dir/path' => {} })
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "path not present" do
|
33
|
+
it "unsets path" do
|
34
|
+
record.unset_path(path)
|
35
|
+
expect(record.paths).to eq({ '/dir/path' => {} })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#file_data" do
|
41
|
+
context "path is present" do
|
42
|
+
before { record.set_path(path, data) }
|
43
|
+
|
44
|
+
it "returns file data" do
|
45
|
+
expect(record.file_data(path)).to eq data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "path not present" do
|
50
|
+
it "return empty hash" do
|
51
|
+
expect(record.file_data(path)).to be_empty
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#dir_entries" do
|
57
|
+
context "path is present" do
|
58
|
+
before { record.set_path(path, data) }
|
59
|
+
|
60
|
+
it "returns file path" do
|
61
|
+
expect(record.dir_entries('/dir/path')).to eq({ 'file.rb' => data })
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "path not present" do
|
66
|
+
it "unsets path" do
|
67
|
+
expect(record.dir_entries('/dir/path')).to eq({})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#build" do
|
73
|
+
let(:directories) { ['dir_path'] }
|
74
|
+
let(:change_pool) { double(Listen::Change, terminate: true) }
|
75
|
+
before {
|
76
|
+
change_pool.stub(:change)
|
77
|
+
Celluloid::Actor.stub(:[]).with(:listen_change_pool) { change_pool }
|
78
|
+
listener.stub(:directories) { directories }
|
79
|
+
}
|
80
|
+
|
81
|
+
it "re-inits paths" do
|
82
|
+
record.set_path(path, data)
|
83
|
+
record.build
|
84
|
+
expect(record.file_data(path)).to be_empty
|
85
|
+
end
|
86
|
+
|
87
|
+
it "calls change asynchronously on all directories to build record" do
|
88
|
+
expect(change_pool).to receive(:change).with('dir_path', type: 'Dir', recursive: true, silence: true)
|
89
|
+
record.build
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|