listen 2.7.11 → 2.7.12
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/.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
|