listen 2.7.11 → 2.7.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +3 -0
- data/.rubocop.yml +20 -232
- data/.rubocop_todo.yml +33 -0
- data/CONTRIBUTING.md +5 -5
- data/Gemfile +1 -0
- data/Guardfile +12 -9
- data/README.md +15 -29
- data/Rakefile +8 -1
- data/TROUBLESHOOTING.md +139 -0
- data/lib/listen.rb +6 -5
- data/lib/listen/adapter.rb +2 -1
- data/lib/listen/adapter/base.rb +7 -3
- data/lib/listen/adapter/darwin.rb +16 -1
- data/lib/listen/adapter/tcp.rb +8 -5
- data/lib/listen/adapter/windows.rb +6 -6
- data/lib/listen/change.rb +3 -2
- data/lib/listen/cli.rb +1 -3
- data/lib/listen/directory.rb +7 -11
- data/lib/listen/file.rb +35 -41
- data/lib/listen/internals/logging.rb +31 -0
- data/lib/listen/internals/thread_pool.rb +19 -0
- data/lib/listen/listener.rb +14 -14
- data/lib/listen/record.rb +2 -2
- data/lib/listen/silencer.rb +3 -3
- data/lib/listen/tcp/broadcaster.rb +8 -5
- data/lib/listen/version.rb +1 -1
- data/listen.gemspec +2 -2
- data/spec/acceptance/listen_spec.rb +1 -2
- data/spec/acceptance/tcp_spec.rb +4 -1
- data/spec/lib/listen/adapter/darwin_spec.rb +108 -0
- data/spec/lib/listen/adapter/polling_spec.rb +1 -0
- data/spec/lib/listen/file_spec.rb +20 -20
- data/spec/lib/listen/listener_spec.rb +1 -1
- data/spec/lib/listen/record_spec.rb +1 -1
- data/spec/lib/listen/silencer_spec.rb +4 -4
- data/spec/spec_helper.rb +4 -0
- data/spec/support/acceptance_helper.rb +2 -4
- data/vendor/hound/config/style_guides/ruby.yml +259 -0
- metadata +8 -2
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'celluloid/logger'
|
2
|
+
|
3
|
+
module Listen
|
4
|
+
module Internals
|
5
|
+
module Logging
|
6
|
+
def _info(*args)
|
7
|
+
_log(:info, *args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def _warn(*args)
|
11
|
+
_log(:warn, *args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def _debug(*args)
|
15
|
+
_log(:debug, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def _log(*args)
|
19
|
+
Celluloid::Logger.send(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def _format_error(fmt)
|
23
|
+
format(fmt, $ERROR_INFO, ", Backtrace: \n" + $ERROR_POSITION * "\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def _error_exception(fmt)
|
27
|
+
_log :error, _format_error(fmt)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Listen
|
2
|
+
# @private api
|
3
|
+
module Internals
|
4
|
+
# Just a wrapper for tests to avoid interfereing with Celluloid's threads
|
5
|
+
module ThreadPool
|
6
|
+
def self.add(&block)
|
7
|
+
(@threads ||= Queue.new) << Thread.new { block.call }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.stop
|
11
|
+
return unless @threads ||= nil
|
12
|
+
|
13
|
+
killed = Queue.new
|
14
|
+
killed << @threads.pop.kill until @threads.empty?
|
15
|
+
killed.pop.join until killed.empty?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/listen/listener.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
|
3
|
+
require 'listen/version'
|
2
4
|
require 'listen/adapter'
|
3
5
|
require 'listen/change'
|
4
6
|
require 'listen/record'
|
@@ -6,6 +8,8 @@ require 'listen/silencer'
|
|
6
8
|
require 'listen/queue_optimizer'
|
7
9
|
require 'English'
|
8
10
|
|
11
|
+
require 'listen/internals/logging'
|
12
|
+
|
9
13
|
module Listen
|
10
14
|
class Listener
|
11
15
|
include Celluloid::FSM
|
@@ -39,7 +43,8 @@ module Listen
|
|
39
43
|
# Setup logging first
|
40
44
|
if Celluloid.logger
|
41
45
|
Celluloid.logger.level = _debug_level
|
42
|
-
|
46
|
+
_info "Celluloid loglevel set to: #{Celluloid.logger.level}"
|
47
|
+
_info "Listen version: #{Listen::VERSION}"
|
43
48
|
end
|
44
49
|
|
45
50
|
@silencer = Silencer.new
|
@@ -172,6 +177,8 @@ module Listen
|
|
172
177
|
|
173
178
|
private
|
174
179
|
|
180
|
+
include Internals::Logging
|
181
|
+
|
175
182
|
def _init_options(options = {})
|
176
183
|
{ debug: false,
|
177
184
|
latency: nil,
|
@@ -239,8 +246,7 @@ module Listen
|
|
239
246
|
_process_changes unless state == :paused
|
240
247
|
end
|
241
248
|
rescue RuntimeError
|
242
|
-
Kernel.warn
|
243
|
-
Kernel.warn "Backtrace:\n\t#{$@.join("\n\t")}"
|
249
|
+
Kernel.warn _format_error('exception while processing events: %s %s')
|
244
250
|
end
|
245
251
|
|
246
252
|
def _silenced?(path, type)
|
@@ -253,10 +259,6 @@ module Listen
|
|
253
259
|
adapter.start
|
254
260
|
end
|
255
261
|
|
256
|
-
def _log(type, message)
|
257
|
-
Celluloid::Logger.send(type, message)
|
258
|
-
end
|
259
|
-
|
260
262
|
def _adapter_class
|
261
263
|
@adapter_class ||= Adapter.select(options)
|
262
264
|
end
|
@@ -268,9 +270,7 @@ module Listen
|
|
268
270
|
@last_queue_event_time = nil
|
269
271
|
|
270
272
|
changes = []
|
271
|
-
|
272
|
-
changes << @queue.pop
|
273
|
-
end
|
273
|
+
changes << @queue.pop until @queue.empty?
|
274
274
|
|
275
275
|
return if block.nil?
|
276
276
|
|
@@ -280,7 +280,7 @@ module Listen
|
|
280
280
|
block_start = Time.now.to_f
|
281
281
|
# TODO: condition not tested, but too complex to test ATM
|
282
282
|
block.call(*result) unless result.all?(&:empty?)
|
283
|
-
|
283
|
+
_debug "Callback took #{Time.now.to_f - block_start} seconds"
|
284
284
|
end
|
285
285
|
|
286
286
|
attr_reader :wait_thread
|
@@ -332,16 +332,16 @@ module Listen
|
|
332
332
|
end
|
333
333
|
|
334
334
|
def _queue_raw_change(type, dir, rel_path, options)
|
335
|
-
|
335
|
+
_debug "raw queue: #{[type, dir, rel_path, options].inspect}"
|
336
336
|
|
337
337
|
unless (worker = async(:change_pool))
|
338
|
-
|
338
|
+
_warn 'Failed to allocate worker from change pool'
|
339
339
|
return
|
340
340
|
end
|
341
341
|
|
342
342
|
worker.change(type, dir, rel_path, options)
|
343
343
|
rescue RuntimeError
|
344
|
-
|
344
|
+
_error_exception "_queue_raw_change exception %s:\n%s:\n"
|
345
345
|
raise
|
346
346
|
end
|
347
347
|
end
|
data/lib/listen/record.rb
CHANGED
@@ -67,7 +67,7 @@ module Listen
|
|
67
67
|
|
68
68
|
Celluloid::Logger.info "Record.build(): #{Time.now.to_f - start} seconds"
|
69
69
|
rescue
|
70
|
-
Celluloid::Logger.warn "build crashed: #{
|
70
|
+
Celluloid::Logger.warn "build crashed: #{$ERROR_INFO.inspect}"
|
71
71
|
raise
|
72
72
|
end
|
73
73
|
|
@@ -105,7 +105,7 @@ module Listen
|
|
105
105
|
left = Queue.new
|
106
106
|
left << '.'
|
107
107
|
|
108
|
-
|
108
|
+
until left.empty?
|
109
109
|
dirname = left.pop
|
110
110
|
add_dir(root, dirname)
|
111
111
|
|
data/lib/listen/silencer.rb
CHANGED
@@ -15,8 +15,8 @@ module Listen
|
|
15
15
|
)(/|$)}x
|
16
16
|
|
17
17
|
# The default list of files that get ignored.
|
18
|
-
DEFAULT_IGNORED_EXTENSIONS =
|
19
|
-
# Kate's tmp
|
18
|
+
DEFAULT_IGNORED_EXTENSIONS = /(?:
|
19
|
+
# Kate's tmp\/swp files
|
20
20
|
\..*\d+\.new
|
21
21
|
| \.kate-swp
|
22
22
|
|
@@ -36,7 +36,7 @@ module Listen
|
|
36
36
|
| \.DS_Store
|
37
37
|
| \.tmp
|
38
38
|
| ~
|
39
|
-
)
|
39
|
+
)$/x
|
40
40
|
|
41
41
|
attr_accessor :only_patterns, :ignore_patterns
|
42
42
|
|
@@ -16,10 +16,12 @@ module Listen
|
|
16
16
|
#
|
17
17
|
def initialize(host, port)
|
18
18
|
@sockets = []
|
19
|
-
_log :debug,
|
19
|
+
_log :debug, format('Broadcaster: tcp server listening on: %s:%s',
|
20
|
+
host, port)
|
20
21
|
@server = TCPServer.new(host, port)
|
21
22
|
rescue
|
22
|
-
_log :error,
|
23
|
+
_log :error, format('Broadcaster.initialize: %s:%s', $ERROR_INFO,
|
24
|
+
$ERROR_POSITION * "\n")
|
23
25
|
raise
|
24
26
|
end
|
25
27
|
|
@@ -48,13 +50,14 @@ module Listen
|
|
48
50
|
|
49
51
|
# Continuously accept and handle incoming connections
|
50
52
|
def run
|
51
|
-
while socket = @server.accept
|
53
|
+
while (socket = @server.accept)
|
52
54
|
@sockets << socket
|
53
55
|
end
|
54
56
|
rescue Celluloid::Task::TerminatedError
|
55
|
-
_log :debug, "TCP adapter was terminated: #{
|
57
|
+
_log :debug, "TCP adapter was terminated: #{$ERROR_INFO}"
|
56
58
|
rescue
|
57
|
-
_log :error,
|
59
|
+
_log :error, format('Broadcaster.run: %s:%s', $ERROR_INFO,
|
60
|
+
$ERROR_POSITION * "\n")
|
58
61
|
raise
|
59
62
|
end
|
60
63
|
|
data/lib/listen/version.rb
CHANGED
data/listen.gemspec
CHANGED
@@ -14,8 +14,8 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.description = 'The Listen gem listens to file modifications and '\
|
15
15
|
'notifies you about the changes. Works everywhere!'
|
16
16
|
|
17
|
-
s.files = `git ls-files`.split(
|
18
|
-
s.test_files = s.files.grep(
|
17
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
18
|
+
s.test_files = s.files.grep(/^spec\//)
|
19
19
|
s.executable = 'listen'
|
20
20
|
s.require_path = 'lib'
|
21
21
|
|
@@ -23,8 +23,7 @@ describe 'Listen' do
|
|
23
23
|
|
24
24
|
it 'warns the backtrace' do
|
25
25
|
expect(Kernel).to receive(:warn).
|
26
|
-
with(
|
27
|
-
expect(Kernel).to receive(:warn).with(/^Backtrace:.*/)
|
26
|
+
with(/exception while processing events: foo .*Backtrace:/)
|
28
27
|
wrapper.listen { touch 'file.rb' }
|
29
28
|
end
|
30
29
|
end
|
data/spec/acceptance/tcp_spec.rb
CHANGED
@@ -6,7 +6,10 @@ describe Listen::Listener do
|
|
6
6
|
let(:broadcast_options) { { forward_to: port } }
|
7
7
|
let(:paths) { Pathname.new(Dir.pwd) }
|
8
8
|
|
9
|
-
around
|
9
|
+
around do |example|
|
10
|
+
fixtures { example.run }
|
11
|
+
Listen.stop
|
12
|
+
end
|
10
13
|
|
11
14
|
modes = if !windows? || Celluloid::VERSION > '0.15.2'
|
12
15
|
[:recipient, :broadcaster]
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
# This is just so stubs work
|
4
|
+
require 'rb-fsevent'
|
5
|
+
|
3
6
|
require 'listen/adapter/darwin'
|
4
7
|
|
5
8
|
include Listen
|
@@ -34,4 +37,109 @@ describe Adapter::Darwin do
|
|
34
37
|
it { should eq 1234 }
|
35
38
|
end
|
36
39
|
end
|
40
|
+
|
41
|
+
describe 'multiple dirs' do
|
42
|
+
subject do
|
43
|
+
dirs = config.keys.map { |p| Pathname(p.to_s) }
|
44
|
+
described_class.new(directories: dirs)
|
45
|
+
end
|
46
|
+
|
47
|
+
let(:foo1) { double('foo1') }
|
48
|
+
let(:foo2) { double('foo2') }
|
49
|
+
let(:foo3) { double('foo3') }
|
50
|
+
|
51
|
+
before do
|
52
|
+
allow(FSEvent).to receive(:new).and_return(*config.values, nil)
|
53
|
+
config.each do |dir, obj|
|
54
|
+
allow(obj).to receive(:watch).with(dir.to_s, latency: 0.1)
|
55
|
+
end
|
56
|
+
subject.configure
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'configuration' do
|
60
|
+
context 'with 1 directory' do
|
61
|
+
let(:config) { { dir1: foo1 } }
|
62
|
+
|
63
|
+
it 'configures directory' do
|
64
|
+
expect(foo1).to have_received(:watch).with('dir1', latency: 0.1)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with 2 directories' do
|
69
|
+
let(:config) { { dir1: foo1, dir2: foo2 } }
|
70
|
+
|
71
|
+
it 'configures directories' do
|
72
|
+
expect(foo1).to have_received(:watch).with('dir1', latency: 0.1)
|
73
|
+
expect(foo2).to have_received(:watch).with('dir2', latency: 0.1)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with 3 directories' do
|
78
|
+
let(:config) { { dir1: foo1, dir2: foo2, dir3: foo3 } }
|
79
|
+
|
80
|
+
it 'configures directories' do
|
81
|
+
expect(foo1).to have_received(:watch).with('dir1', latency: 0.1)
|
82
|
+
expect(foo2).to have_received(:watch).with('dir2', latency: 0.1)
|
83
|
+
expect(foo3).to have_received(:watch).with('dir3', latency: 0.1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'running threads' do
|
89
|
+
let(:running) { [] }
|
90
|
+
|
91
|
+
before do
|
92
|
+
started = Queue.new
|
93
|
+
threads = Queue.new
|
94
|
+
left = Queue.new
|
95
|
+
|
96
|
+
# NOTE: Travis has a hard time creating threads on OSX
|
97
|
+
thread_start_overhead = 3
|
98
|
+
max_test_time = 3 * thread_start_overhead
|
99
|
+
block_time = max_test_time + thread_start_overhead
|
100
|
+
|
101
|
+
config.each do |name, obj|
|
102
|
+
left << name # anything, we're just counting
|
103
|
+
allow(obj).to receive(:run).once do
|
104
|
+
threads << Thread.current
|
105
|
+
started << name
|
106
|
+
left.pop
|
107
|
+
sleep block_time
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
Timeout.timeout(max_test_time) do
|
112
|
+
subject.start
|
113
|
+
running << started.pop until left.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
running << started.pop until started.empty?
|
117
|
+
|
118
|
+
killed = Queue.new
|
119
|
+
killed << threads.pop.kill until threads.empty?
|
120
|
+
killed.pop.join until killed.empty?
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'with 1 directory' do
|
124
|
+
let(:config) { { dir1: foo1 } }
|
125
|
+
it 'runs all the workers without blocking' do
|
126
|
+
expect(running.sort).to eq(config.keys)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with 2 directories' do
|
131
|
+
let(:config) { { dir1: foo1, dir2: foo2 } }
|
132
|
+
it 'runs all the workers without blocking' do
|
133
|
+
expect(running.sort).to eq(config.keys)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'with 3 directories' do
|
138
|
+
let(:config) { { dir1: foo1, dir2: foo2, dir3: foo3 } }
|
139
|
+
it 'runs all the workers without blocking' do
|
140
|
+
expect(running.sort).to eq(config.keys)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
37
145
|
end
|
@@ -6,7 +6,7 @@ describe Listen::File do
|
|
6
6
|
Listen::Record,
|
7
7
|
add_dir: true,
|
8
8
|
update_file: true,
|
9
|
-
unset_path: true
|
9
|
+
unset_path: true
|
10
10
|
)
|
11
11
|
end
|
12
12
|
|
@@ -116,17 +116,17 @@ describe Listen::File do
|
|
116
116
|
let(:record_mtime) { stat_mtime.to_f }
|
117
117
|
|
118
118
|
context 'with accurate stat times' do
|
119
|
-
let(:stat_mtime) { Time.at(
|
120
|
-
let(:stat_atime) { Time.at(
|
121
|
-
let(:stat_ctime) { Time.at(
|
119
|
+
let(:stat_mtime) { Time.at(1_401_235_714.123) }
|
120
|
+
let(:stat_atime) { Time.at(1_401_235_714.123) }
|
121
|
+
let(:stat_ctime) { Time.at(1_401_235_714.123) }
|
122
122
|
let(:record_mtime) { stat_mtime.to_f }
|
123
123
|
it { should be_nil }
|
124
124
|
end
|
125
125
|
|
126
126
|
context 'with inaccurate stat times' do
|
127
|
-
let(:stat_mtime) { Time.at(
|
128
|
-
let(:stat_atime) { Time.at(
|
129
|
-
let(:stat_ctime) { Time.at(
|
127
|
+
let(:stat_mtime) { Time.at(1_401_235_714.0) }
|
128
|
+
let(:stat_atime) { Time.at(1_401_235_714.0) }
|
129
|
+
let(:stat_ctime) { Time.at(1_401_235_714.0) }
|
130
130
|
|
131
131
|
let(:record_mtime) { stat_mtime.to_f }
|
132
132
|
|
@@ -135,7 +135,7 @@ describe Listen::File do
|
|
135
135
|
|
136
136
|
# NOTE: if real mtime is ???14.99, the
|
137
137
|
# saved mtime is ???14.0
|
138
|
-
let(:now) { Time.at(
|
138
|
+
let(:now) { Time.at(1_401_235_716.00) }
|
139
139
|
it { should be_nil }
|
140
140
|
end
|
141
141
|
|
@@ -144,17 +144,17 @@ describe Listen::File do
|
|
144
144
|
# so saved mtime at ???14.0 means it could be
|
145
145
|
# ???14.999, so ???15.999 could still be within 1 second
|
146
146
|
# range
|
147
|
-
let(:now) { Time.at(
|
147
|
+
let(:now) { Time.at(1_401_235_715.999999) }
|
148
148
|
|
149
149
|
before { allow(Time).to receive(:now) { now } }
|
150
150
|
|
151
151
|
context 'without available md5' do
|
152
152
|
let(:md5) { fail Errno::ENOENT }
|
153
153
|
|
154
|
-
# Treat
|
154
|
+
# Treat it as a removed file, because chances are ...
|
155
155
|
# whatever is listening for changes won't be able to deal
|
156
156
|
# with the file either (e.g. because of permissions)
|
157
|
-
it { should be
|
157
|
+
it { should be :removed }
|
158
158
|
|
159
159
|
it 'should not unset record' do
|
160
160
|
expect(async_record).to_not receive(:unset_path)
|
@@ -229,23 +229,23 @@ describe Listen::File do
|
|
229
229
|
subject { Listen::File.inaccurate_mac_time?(stat) }
|
230
230
|
|
231
231
|
context 'with no accurate times' do
|
232
|
-
let(:mtime) { Time.at(
|
233
|
-
let(:atime) { Time.at(
|
234
|
-
let(:ctime) { Time.at(
|
232
|
+
let(:mtime) { Time.at(1_234_567.0) }
|
233
|
+
let(:atime) { Time.at(1_234_567.0) }
|
234
|
+
let(:ctime) { Time.at(1_234_567.0) }
|
235
235
|
it { should be_truthy }
|
236
236
|
end
|
237
237
|
|
238
238
|
context 'with all accurate times' do
|
239
|
-
let(:mtime) { Time.at(
|
240
|
-
let(:atime) { Time.at(
|
241
|
-
let(:ctime) { Time.at(
|
239
|
+
let(:mtime) { Time.at(1_234_567.89) }
|
240
|
+
let(:atime) { Time.at(1_234_567.89) }
|
241
|
+
let(:ctime) { Time.at(1_234_567.89) }
|
242
242
|
it { should be_falsey }
|
243
243
|
end
|
244
244
|
|
245
245
|
context 'with one accurate time' do
|
246
|
-
let(:mtime) { Time.at(
|
247
|
-
let(:atime) { Time.at(
|
248
|
-
let(:ctime) { Time.at(
|
246
|
+
let(:mtime) { Time.at(1_234_567.0) }
|
247
|
+
let(:atime) { Time.at(1_234_567.89) }
|
248
|
+
let(:ctime) { Time.at(1_234_567.0) }
|
249
249
|
it { should be_falsey }
|
250
250
|
end
|
251
251
|
end
|