listen 3.0.2 → 3.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CONTRIBUTING.md +10 -3
- data/README.md +263 -106
- data/bin/listen +3 -4
- data/lib/listen/adapter/base.rb +31 -32
- data/lib/listen/adapter/bsd.rb +9 -8
- data/lib/listen/adapter/config.rb +12 -4
- data/lib/listen/adapter/darwin.rb +53 -29
- data/lib/listen/adapter/linux.rb +21 -15
- data/lib/listen/adapter/polling.rb +9 -6
- data/lib/listen/adapter/windows.rb +19 -22
- data/lib/listen/adapter.rb +25 -25
- data/lib/listen/backend.rb +9 -10
- data/lib/listen/change.rb +18 -27
- data/lib/listen/cli.rb +8 -8
- data/lib/listen/directory.rb +33 -20
- data/lib/listen/error.rb +11 -0
- data/lib/listen/event/config.rb +9 -28
- data/lib/listen/event/loop.rb +47 -68
- data/lib/listen/event/processor.rb +41 -37
- data/lib/listen/event/queue.rb +19 -19
- data/lib/listen/file.rb +23 -8
- data/lib/listen/fsm.rb +74 -72
- data/lib/listen/listener/config.rb +5 -9
- data/lib/listen/listener.rb +27 -23
- data/lib/listen/logger.rb +24 -20
- data/lib/listen/monotonic_time.rb +27 -0
- data/lib/listen/options.rb +11 -8
- data/lib/listen/queue_optimizer.rb +15 -18
- data/lib/listen/record/entry.rb +18 -3
- data/lib/listen/record/symlink_detector.rb +10 -8
- data/lib/listen/record.rb +42 -38
- data/lib/listen/silencer/controller.rb +2 -0
- data/lib/listen/silencer.rb +30 -21
- data/lib/listen/thread.rb +54 -0
- data/lib/listen/version.rb +3 -1
- data/lib/listen.rb +14 -22
- metadata +28 -24
- data/lib/listen/internals/thread_pool.rb +0 -21
data/lib/listen/adapter/base.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'listen/options'
|
2
4
|
require 'listen/record'
|
3
5
|
require 'listen/change'
|
6
|
+
require 'listen/thread'
|
4
7
|
|
5
8
|
module Listen
|
6
9
|
module Adapter
|
7
10
|
class Base
|
8
|
-
attr_reader :options
|
11
|
+
attr_reader :options, :config
|
9
12
|
|
10
13
|
# TODO: only used by tests
|
11
|
-
DEFAULTS = {}
|
12
|
-
|
13
|
-
attr_reader :config
|
14
|
+
DEFAULTS = {}.freeze
|
14
15
|
|
15
16
|
def initialize(config)
|
16
17
|
@started = false
|
@@ -28,9 +29,10 @@ module Listen
|
|
28
29
|
end
|
29
30
|
|
30
31
|
# TODO: it's a separate method as a temporary workaround for tests
|
32
|
+
# rubocop:disable Metrics/MethodLength
|
31
33
|
def configure
|
32
34
|
if @configured
|
33
|
-
|
35
|
+
Listen.logger.warn('Adapter already configured!')
|
34
36
|
return
|
35
37
|
end
|
36
38
|
|
@@ -49,11 +51,12 @@ module Listen
|
|
49
51
|
# TODO: separate config per directory (some day maybe)
|
50
52
|
change_config = Change::Config.new(config.queue, config.silencer)
|
51
53
|
config.directories.each do |dir|
|
52
|
-
record = Record.new(dir)
|
54
|
+
record = Record.new(dir, config.silencer)
|
53
55
|
snapshot = Change.new(change_config, record)
|
54
56
|
@snapshots[dir] = snapshot
|
55
57
|
end
|
56
58
|
end
|
59
|
+
# rubocop:enable Metrics/MethodLength
|
57
60
|
|
58
61
|
def started?
|
59
62
|
@started
|
@@ -63,41 +66,39 @@ module Listen
|
|
63
66
|
configure
|
64
67
|
|
65
68
|
if started?
|
66
|
-
|
69
|
+
Listen.logger.warn('Adapter already started!')
|
67
70
|
return
|
68
71
|
end
|
69
72
|
|
70
73
|
@started = true
|
71
74
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@snapshots.values.each do |snapshot|
|
76
|
-
_timed('Record.build()') { snapshot.record.build }
|
77
|
-
end
|
78
|
-
_run
|
79
|
-
rescue
|
80
|
-
msg = 'run() in thread failed: %s:\n'\
|
81
|
-
' %s\n\ncalled from:\n %s'
|
82
|
-
_log_exception(msg, calling_stack)
|
83
|
-
raise # for unit tests mostly
|
75
|
+
@run_thread = Listen::Thread.new("run_thread") do
|
76
|
+
@snapshots.each_value do |snapshot|
|
77
|
+
_timed('Record.build()') { snapshot.record.build }
|
84
78
|
end
|
79
|
+
_run
|
85
80
|
end
|
86
81
|
end
|
87
82
|
|
88
|
-
def
|
89
|
-
|
83
|
+
def stop
|
84
|
+
_stop
|
85
|
+
config.queue.close # this causes queue.pop to return `nil` to the front-end
|
90
86
|
end
|
91
87
|
|
92
88
|
private
|
93
89
|
|
90
|
+
def _stop
|
91
|
+
@run_thread&.kill
|
92
|
+
@run_thread = nil
|
93
|
+
end
|
94
|
+
|
94
95
|
def _timed(title)
|
95
|
-
start =
|
96
|
+
start = MonotonicTime.now
|
96
97
|
yield
|
97
|
-
diff =
|
98
|
-
Listen
|
98
|
+
diff = MonotonicTime.now - start
|
99
|
+
Listen.logger.info format('%s: %.05f seconds', title, diff)
|
99
100
|
rescue
|
100
|
-
Listen
|
101
|
+
Listen.logger.warn "#{title} crashed: #{$ERROR_INFO.inspect}"
|
101
102
|
raise
|
102
103
|
end
|
103
104
|
|
@@ -107,10 +108,6 @@ module Listen
|
|
107
108
|
@snapshots[dir].invalidate(type, rel_path, options)
|
108
109
|
end
|
109
110
|
|
110
|
-
def _log(*args, &block)
|
111
|
-
self.class.send(:_log, *args, &block)
|
112
|
-
end
|
113
|
-
|
114
111
|
def _log_exception(msg, caller_stack)
|
115
112
|
formatted = format(
|
116
113
|
msg,
|
@@ -119,11 +116,13 @@ module Listen
|
|
119
116
|
caller_stack * "\n"
|
120
117
|
)
|
121
118
|
|
122
|
-
|
119
|
+
Listen.logger.error(formatted)
|
123
120
|
end
|
124
121
|
|
125
|
-
|
126
|
-
|
122
|
+
class << self
|
123
|
+
def usable?
|
124
|
+
const_get('OS_REGEXP') =~ RbConfig::CONFIG['target_os']
|
125
|
+
end
|
127
126
|
end
|
128
127
|
end
|
129
128
|
end
|
data/lib/listen/adapter/bsd.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Listener implementation for BSD's `kqueue`.
|
2
4
|
# @see http://www.freebsd.org/cgi/man.cgi?query=kqueue
|
3
5
|
# @see https://github.com/mat813/rb-kqueue/blob/master/lib/rb-kqueue/queue.rb
|
@@ -5,7 +7,7 @@
|
|
5
7
|
module Listen
|
6
8
|
module Adapter
|
7
9
|
class BSD < Base
|
8
|
-
OS_REGEXP = /bsd|dragonfly/i
|
10
|
+
OS_REGEXP = /bsd|dragonfly/i.freeze
|
9
11
|
|
10
12
|
DEFAULTS = {
|
11
13
|
events: [
|
@@ -16,7 +18,7 @@ module Listen
|
|
16
18
|
:rename
|
17
19
|
# :link, :revoke
|
18
20
|
]
|
19
|
-
}
|
21
|
+
}.freeze
|
20
22
|
|
21
23
|
BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
|
22
24
|
Please add the following to your Gemfile to avoid polling for changes:
|
@@ -38,9 +40,9 @@ module Listen
|
|
38
40
|
|
39
41
|
private
|
40
42
|
|
41
|
-
def _configure(directory, &
|
43
|
+
def _configure(directory, &callback)
|
42
44
|
@worker ||= KQueue::Queue.new
|
43
|
-
@callback =
|
45
|
+
@callback = callback
|
44
46
|
# use Record to make a snapshot of dir, so we
|
45
47
|
# can detect new files
|
46
48
|
_find(directory.to_s) { |path| _watch_file(path, @worker) }
|
@@ -71,8 +73,7 @@ module Listen
|
|
71
73
|
def _change(event_flags)
|
72
74
|
{ modified: [:attrib, :extend],
|
73
75
|
added: [:write],
|
74
|
-
removed: [:rename, :delete]
|
75
|
-
}.each do |change, flags|
|
76
|
+
removed: [:rename, :delete] }.each do |change, flags|
|
76
77
|
return change unless (flags & event_flags).empty?
|
77
78
|
end
|
78
79
|
nil
|
@@ -85,7 +86,7 @@ module Listen
|
|
85
86
|
def _watch_for_new_file(event)
|
86
87
|
queue = event.watcher.queue
|
87
88
|
_find(_event_path(event).to_s) do |file_path|
|
88
|
-
unless queue.watchers.
|
89
|
+
unless queue.watchers.find { |_, v| v.path == file_path.to_s }
|
89
90
|
_watch_file(file_path, queue)
|
90
91
|
end
|
91
92
|
end
|
@@ -94,7 +95,7 @@ module Listen
|
|
94
95
|
def _watch_file(path, queue)
|
95
96
|
queue.watch_file(path, *options.events, &@callback)
|
96
97
|
rescue Errno::ENOENT => e
|
97
|
-
|
98
|
+
Listen.logger.warn "kqueue: watch file failed: #{e.message}"
|
98
99
|
end
|
99
100
|
|
100
101
|
# Quick rubocop workaround
|
@@ -1,19 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pathname'
|
2
4
|
|
3
5
|
module Listen
|
4
6
|
module Adapter
|
5
7
|
class Config
|
6
|
-
attr_reader :directories
|
7
|
-
attr_reader :silencer
|
8
|
-
attr_reader :queue
|
9
|
-
attr_reader :adapter_options
|
8
|
+
attr_reader :directories, :silencer, :queue, :adapter_options
|
10
9
|
|
11
10
|
def initialize(directories, queue, silencer, adapter_options)
|
11
|
+
# Default to current directory if no directories are supplied
|
12
|
+
directories = [Dir.pwd] if directories.to_a.empty?
|
13
|
+
|
12
14
|
# TODO: fix (flatten, array, compact?)
|
13
15
|
@directories = directories.map do |directory|
|
14
16
|
Pathname.new(directory.to_s).realpath
|
15
17
|
end
|
16
18
|
|
19
|
+
@directories.each do |pathname|
|
20
|
+
unless pathname.directory?
|
21
|
+
fail ArgumentError, "must be a directory: #{pathname}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
17
25
|
@silencer = silencer
|
18
26
|
@queue = queue
|
19
27
|
@adapter_options = adapter_options
|
@@ -1,52 +1,76 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'listen/thread'
|
2
4
|
|
3
5
|
module Listen
|
4
6
|
module Adapter
|
5
7
|
# Adapter implementation for Mac OS X `FSEvents`.
|
6
8
|
#
|
7
9
|
class Darwin < Base
|
8
|
-
OS_REGEXP = /darwin(1
|
10
|
+
OS_REGEXP = /darwin(?<major_version>(1|2)\d+)/i.freeze
|
9
11
|
|
10
12
|
# The default delay between checking for changes.
|
11
|
-
DEFAULTS = { latency: 0.1 }
|
13
|
+
DEFAULTS = { latency: 0.1 }.freeze
|
12
14
|
|
13
|
-
|
15
|
+
INCOMPATIBLE_GEM_VERSION = <<-EOS.gsub(/^ {8}/, '')
|
16
|
+
rb-fsevent > 0.9.4 no longer supports OS X 10.6 through 10.8.
|
17
|
+
|
18
|
+
Please add the following to your Gemfile to avoid polling for changes:
|
19
|
+
require 'rbconfig'
|
20
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin(1[0-3])/i
|
21
|
+
gem 'rb-fsevent', '<= 0.9.4'
|
22
|
+
end
|
23
|
+
EOS
|
24
|
+
|
25
|
+
def self.usable?
|
26
|
+
version = RbConfig::CONFIG['target_os'][OS_REGEXP, :major_version]
|
27
|
+
return false unless version
|
28
|
+
return true if version.to_i >= 13 # darwin13 is OS X 10.9
|
14
29
|
|
15
|
-
# NOTE: each directory gets a DIFFERENT callback!
|
16
|
-
def _configure(dir, &callback)
|
17
30
|
require 'rb-fsevent'
|
18
|
-
|
31
|
+
fsevent_version = Gem::Version.new(FSEvent::VERSION)
|
32
|
+
return true if fsevent_version <= Gem::Version.new('0.9.4')
|
33
|
+
Kernel.warn INCOMPATIBLE_GEM_VERSION
|
34
|
+
false
|
35
|
+
end
|
19
36
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
37
|
+
private
|
38
|
+
|
39
|
+
def _configure(dir, &callback)
|
40
|
+
@callbacks[dir] = callback
|
24
41
|
end
|
25
42
|
|
26
|
-
# NOTE: _run is called within a thread, so run every other
|
27
|
-
# worker in it's own thread
|
28
43
|
def _run
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
44
|
+
require 'rb-fsevent'
|
45
|
+
worker = FSEvent.new
|
46
|
+
dirs_to_watch = @callbacks.keys.map(&:to_s)
|
47
|
+
Listen.logger.info { "fsevent: watching: #{dirs_to_watch.inspect}" }
|
48
|
+
worker.watch(dirs_to_watch, { latency: options.latency }, &method(:_process_changes))
|
49
|
+
@worker_thread = Listen::Thread.new("worker_thread") { worker.run }
|
50
|
+
end
|
51
|
+
|
52
|
+
def _process_changes(dirs)
|
53
|
+
dirs.each do |dir|
|
54
|
+
dir = Pathname.new(dir.sub(%r{/$}, ''))
|
55
|
+
|
56
|
+
@callbacks.each do |watched_dir, callback|
|
57
|
+
if watched_dir.eql?(dir) || Listen::Directory.ascendant_of?(watched_dir, dir)
|
58
|
+
callback.call(dir)
|
36
59
|
end
|
37
60
|
end
|
38
61
|
end
|
39
|
-
first.run
|
40
62
|
end
|
41
63
|
|
42
|
-
def _process_event(dir,
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
64
|
+
def _process_event(dir, path)
|
65
|
+
Listen.logger.debug { "fsevent: processing path: #{path.inspect}" }
|
66
|
+
# TODO: does this preserve symlinks?
|
67
|
+
rel_path = path.relative_path_from(dir).to_s
|
68
|
+
_queue_change(:dir, dir, rel_path, recursive: true)
|
69
|
+
end
|
70
|
+
|
71
|
+
def _stop
|
72
|
+
@worker_thread&.kill
|
73
|
+
super
|
50
74
|
end
|
51
75
|
end
|
52
76
|
end
|
data/lib/listen/adapter/linux.rb
CHANGED
@@ -1,43 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Listen
|
2
4
|
module Adapter
|
3
5
|
# @see https://github.com/nex3/rb-inotify
|
4
6
|
class Linux < Base
|
5
|
-
OS_REGEXP = /linux/i
|
7
|
+
OS_REGEXP = /linux/i.freeze
|
6
8
|
|
7
9
|
DEFAULTS = {
|
8
10
|
events: [
|
9
11
|
:recursive,
|
10
12
|
:attrib,
|
11
13
|
:create,
|
14
|
+
:modify,
|
12
15
|
:delete,
|
13
16
|
:move,
|
14
17
|
:close_write
|
15
18
|
],
|
16
19
|
wait_for_delay: 0.1
|
17
|
-
}
|
20
|
+
}.freeze
|
18
21
|
|
19
22
|
private
|
20
23
|
|
21
|
-
|
22
|
-
'/
|
23
|
-
|
24
|
-
INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '')
|
25
|
-
FATAL: Listen error: unable to monitor directories for changes.
|
26
|
-
Visit #{WIKI_URL} for info on how to fix this.
|
27
|
-
EOS
|
24
|
+
README_URL = 'https://github.com/guard/listen'\
|
25
|
+
'/blob/master/README.md#increasing-the-amount-of-inotify-watchers'
|
28
26
|
|
29
27
|
def _configure(directory, &callback)
|
30
|
-
|
28
|
+
require 'rb-inotify'
|
31
29
|
@worker ||= ::INotify::Notifier.new
|
32
30
|
@worker.watch(directory.to_s, *options.events, &callback)
|
33
31
|
rescue Errno::ENOSPC
|
34
|
-
|
32
|
+
raise ::Listen::Error::INotifyMaxWatchesExceeded, <<~EOS
|
33
|
+
Unable to monitor directories for changes because iNotify max watches exceeded. See #{README_URL} .
|
34
|
+
EOS
|
35
35
|
end
|
36
36
|
|
37
37
|
def _run
|
38
38
|
@worker.run
|
39
39
|
end
|
40
40
|
|
41
|
+
# rubocop:disable Metrics/MethodLength
|
41
42
|
def _process_event(dir, event)
|
42
43
|
# NOTE: avoid using event.absolute_name since new API
|
43
44
|
# will need to have a custom recursion implemented
|
@@ -45,15 +46,13 @@ module Listen
|
|
45
46
|
path = Pathname.new(event.watcher.path) + event.name
|
46
47
|
rel_path = path.relative_path_from(dir).to_s
|
47
48
|
|
48
|
-
|
49
|
+
Listen.logger.debug { "inotify: #{rel_path} (#{event.flags.inspect})" }
|
49
50
|
|
50
51
|
if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
|
51
52
|
if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
|
52
53
|
rel_path = path.dirname.relative_path_from(dir).to_s
|
53
|
-
_queue_change(:dir, dir, rel_path, {})
|
54
|
-
else
|
55
|
-
_queue_change(:dir, dir, rel_path, {})
|
56
54
|
end
|
55
|
+
_queue_change(:dir, dir, rel_path, {})
|
57
56
|
return
|
58
57
|
end
|
59
58
|
|
@@ -72,6 +71,7 @@ module Listen
|
|
72
71
|
|
73
72
|
_queue_change(:file, dir, rel_path, params)
|
74
73
|
end
|
74
|
+
# rubocop:enable Metrics/MethodLength
|
75
75
|
|
76
76
|
def _skip_event?(event)
|
77
77
|
# Event on root directory
|
@@ -97,6 +97,12 @@ module Listen
|
|
97
97
|
def _dir_event?(event)
|
98
98
|
event.flags.include?(:isdir)
|
99
99
|
end
|
100
|
+
|
101
|
+
def _stop
|
102
|
+
@worker&.close
|
103
|
+
|
104
|
+
super
|
105
|
+
end
|
100
106
|
end
|
101
107
|
end
|
102
108
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Listen
|
2
4
|
module Adapter
|
3
5
|
# Polling Adapter that works cross-platform and
|
@@ -6,9 +8,9 @@ module Listen
|
|
6
8
|
# file IO than the other implementations.
|
7
9
|
#
|
8
10
|
class Polling < Base
|
9
|
-
OS_REGEXP =
|
11
|
+
OS_REGEXP = //.freeze # match every OS
|
10
12
|
|
11
|
-
DEFAULTS = { latency: 1.0, wait_for_delay: 0.05 }
|
13
|
+
DEFAULTS = { latency: 1.0, wait_for_delay: 0.05 }.freeze
|
12
14
|
|
13
15
|
private
|
14
16
|
|
@@ -19,12 +21,13 @@ module Listen
|
|
19
21
|
|
20
22
|
def _run
|
21
23
|
loop do
|
22
|
-
start =
|
24
|
+
start = MonotonicTime.now
|
23
25
|
@polling_callbacks.each do |callback|
|
24
26
|
callback.call(nil)
|
25
|
-
nap_time = options.latency - (
|
26
|
-
|
27
|
-
|
27
|
+
if (nap_time = options.latency - (MonotonicTime.now - start)) > 0
|
28
|
+
# TODO: warn if nap_time is negative (polling too slow)
|
29
|
+
sleep(nap_time)
|
30
|
+
end
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Listen
|
2
4
|
module Adapter
|
3
5
|
# Adapter implementation for Windows `wdm`.
|
4
6
|
#
|
5
7
|
class Windows < Base
|
6
|
-
OS_REGEXP = /mswin|mingw|cygwin/i
|
8
|
+
OS_REGEXP = /mswin|mingw|cygwin/i.freeze
|
7
9
|
|
8
10
|
BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
|
9
11
|
Please add the following to your Gemfile to avoid polling for changes:
|
@@ -15,8 +17,8 @@ module Listen
|
|
15
17
|
require 'wdm'
|
16
18
|
true
|
17
19
|
rescue LoadError
|
18
|
-
|
19
|
-
|
20
|
+
Listen.logger.debug format('wdm - load failed: %s:%s', $ERROR_INFO,
|
21
|
+
$ERROR_POSITION * "\n")
|
20
22
|
|
21
23
|
Kernel.warn BUNDLER_DECLARE_GEM
|
22
24
|
false
|
@@ -24,21 +26,20 @@ module Listen
|
|
24
26
|
|
25
27
|
private
|
26
28
|
|
27
|
-
def _configure(dir
|
29
|
+
def _configure(dir)
|
28
30
|
require 'wdm'
|
29
|
-
|
31
|
+
Listen.logger.debug 'wdm - starting...'
|
30
32
|
@worker ||= WDM::Monitor.new
|
31
33
|
@worker.watch_recursively(dir.to_s, :files) do |change|
|
32
|
-
|
34
|
+
yield([:file, change])
|
33
35
|
end
|
34
36
|
|
35
37
|
@worker.watch_recursively(dir.to_s, :directories) do |change|
|
36
|
-
|
38
|
+
yield([:dir, change])
|
37
39
|
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
callback.call([:attr, change])
|
41
|
+
@worker.watch_recursively(dir.to_s, :attributes, :last_write) do |change|
|
42
|
+
yield([:attr, change])
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
@@ -46,8 +47,9 @@ module Listen
|
|
46
47
|
@worker.run!
|
47
48
|
end
|
48
49
|
|
50
|
+
# rubocop:disable Metrics/MethodLength
|
49
51
|
def _process_event(dir, event)
|
50
|
-
|
52
|
+
Listen.logger.debug "wdm - callback: #{event.inspect}"
|
51
53
|
|
52
54
|
type, change = event
|
53
55
|
|
@@ -65,12 +67,12 @@ module Listen
|
|
65
67
|
_queue_change(:file, dir, rel_path, options)
|
66
68
|
end
|
67
69
|
when :dir
|
68
|
-
|
70
|
+
case change.type
|
71
|
+
when :removed
|
69
72
|
# TODO: check if watched dir?
|
70
73
|
_queue_change(:dir, dir, Pathname(rel_path).dirname.to_s, {})
|
71
|
-
|
74
|
+
when :added
|
72
75
|
_queue_change(:dir, dir, rel_path, {})
|
73
|
-
else
|
74
76
|
# do nothing - changed directory means either:
|
75
77
|
# - removed subdirs (handled above)
|
76
78
|
# - added subdirs (handled above)
|
@@ -79,20 +81,15 @@ module Listen
|
|
79
81
|
# so what's left?
|
80
82
|
end
|
81
83
|
end
|
82
|
-
rescue
|
83
|
-
details = event.inspect
|
84
|
-
_log :error, format('wdm - callback (%): %s:%s', details, $ERROR_INFO,
|
85
|
-
$ERROR_POSITION * "\n")
|
86
|
-
raise
|
87
84
|
end
|
85
|
+
# rubocop:enable Metrics/MethodLength
|
88
86
|
|
89
87
|
def _change(type)
|
90
88
|
{ modified: [:modified, :attrib], # TODO: is attrib really passed?
|
91
89
|
added: [:added, :renamed_new_file],
|
92
|
-
removed: [:removed, :renamed_old_file] }.
|
93
|
-
|
90
|
+
removed: [:removed, :renamed_old_file] }.find do |change, types|
|
91
|
+
types.include?(type) and break change
|
94
92
|
end
|
95
|
-
nil
|
96
93
|
end
|
97
94
|
end
|
98
95
|
end
|
data/lib/listen/adapter.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'listen/adapter/base'
|
2
4
|
require 'listen/adapter/bsd'
|
3
5
|
require 'listen/adapter/darwin'
|
@@ -7,37 +9,35 @@ require 'listen/adapter/windows'
|
|
7
9
|
|
8
10
|
module Listen
|
9
11
|
module Adapter
|
10
|
-
OPTIMIZED_ADAPTERS = [Darwin, Linux, BSD, Windows]
|
12
|
+
OPTIMIZED_ADAPTERS = [Darwin, Linux, BSD, Windows].freeze
|
11
13
|
POLLING_FALLBACK_MESSAGE = 'Listen will be polling for changes.'\
|
12
14
|
'Learn more at https://github.com/guard/listen#listen-adapters.'
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
16
|
+
class << self
|
17
|
+
def select(options = {})
|
18
|
+
Listen.logger.debug 'Adapter: considering polling ...'
|
19
|
+
return Polling if options[:force_polling]
|
20
|
+
Listen.logger.debug 'Adapter: considering optimized backend...'
|
21
|
+
return _usable_adapter_class if _usable_adapter_class
|
22
|
+
Listen.logger.debug 'Adapter: falling back to polling...'
|
23
|
+
_warn_polling_fallback(options)
|
24
|
+
Polling
|
25
|
+
rescue
|
26
|
+
Listen.logger.warn format('Adapter: failed: %s:%s', $ERROR_POSITION.inspect,
|
27
|
+
$ERROR_POSITION * "\n")
|
28
|
+
raise
|
29
|
+
end
|
27
30
|
|
28
|
-
|
31
|
+
private
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def self._warn_polling_fallback(options)
|
35
|
-
msg = options.fetch(:polling_fallback_message, POLLING_FALLBACK_MESSAGE)
|
36
|
-
Kernel.warn "[Listen warning]:\n #{msg}" if msg
|
37
|
-
end
|
33
|
+
def _usable_adapter_class
|
34
|
+
OPTIMIZED_ADAPTERS.find(&:usable?)
|
35
|
+
end
|
38
36
|
|
39
|
-
|
40
|
-
|
37
|
+
def _warn_polling_fallback(options)
|
38
|
+
msg = options.fetch(:polling_fallback_message, POLLING_FALLBACK_MESSAGE)
|
39
|
+
Kernel.warn "[Listen warning]:\n #{msg}" if msg
|
40
|
+
end
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
data/lib/listen/backend.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'listen/adapter'
|
2
4
|
require 'listen/adapter/base'
|
3
5
|
require 'listen/adapter/config'
|
4
6
|
|
7
|
+
require 'forwardable'
|
8
|
+
|
5
9
|
# This class just aggregates configuration object to avoid Listener specs
|
6
10
|
# from exploding with huge test setup blocks
|
7
11
|
module Listen
|
8
12
|
class Backend
|
13
|
+
extend Forwardable
|
14
|
+
|
9
15
|
def initialize(directories, queue, silencer, config)
|
10
16
|
adapter_select_opts = config.adapter_select_options
|
11
17
|
|
@@ -22,17 +28,10 @@ module Listen
|
|
22
28
|
@adapter = adapter_class.new(aconfig)
|
23
29
|
end
|
24
30
|
|
25
|
-
|
26
|
-
|
27
|
-
end
|
31
|
+
delegate start: :adapter
|
32
|
+
delegate stop: :adapter
|
28
33
|
|
29
|
-
|
30
|
-
# TODO: does nothing
|
31
|
-
end
|
32
|
-
|
33
|
-
def min_delay_between_events
|
34
|
-
@min_delay_between_events
|
35
|
-
end
|
34
|
+
attr_reader :min_delay_between_events
|
36
35
|
|
37
36
|
private
|
38
37
|
|