listen 3.1.5 → 3.7.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 +5 -5
- data/CONTRIBUTING.md +10 -3
- data/README.md +240 -75
- data/bin/listen +3 -4
- data/lib/listen/adapter/base.rb +23 -35
- data/lib/listen/adapter/bsd.rb +6 -5
- data/lib/listen/adapter/config.rb +3 -4
- data/lib/listen/adapter/darwin.rb +29 -44
- data/lib/listen/adapter/linux.rb +15 -13
- data/lib/listen/adapter/polling.rb +8 -5
- data/lib/listen/adapter/windows.rb +15 -17
- data/lib/listen/adapter.rb +9 -11
- data/lib/listen/backend.rb +2 -0
- data/lib/listen/change.rb +14 -21
- data/lib/listen/cli.rb +6 -6
- data/lib/listen/directory.rb +14 -8
- data/lib/listen/error.rb +11 -0
- data/lib/listen/event/config.rb +9 -24
- data/lib/listen/event/loop.rb +44 -67
- data/lib/listen/event/processor.rb +41 -37
- data/lib/listen/event/queue.rb +12 -13
- data/lib/listen/file.rb +22 -7
- data/lib/listen/fsm.rb +72 -71
- data/lib/listen/listener/config.rb +4 -4
- data/lib/listen/listener.rb +26 -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 +13 -16
- data/lib/listen/record/entry.rb +4 -2
- data/lib/listen/record/symlink_detector.rb +10 -8
- data/lib/listen/record.rb +34 -32
- data/lib/listen/silencer/controller.rb +2 -0
- data/lib/listen/silencer.rb +20 -15
- data/lib/listen/thread.rb +54 -0
- data/lib/listen/version.rb +3 -1
- data/lib/listen.rb +15 -25
- metadata +20 -45
- data/lib/listen/internals/thread_pool.rb +0 -29
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: [
|
@@ -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,12 +1,11 @@
|
|
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)
|
12
11
|
# Default to current directory if no directories are supplied
|
@@ -1,12 +1,13 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'listen/thread'
|
3
4
|
|
4
5
|
module Listen
|
5
6
|
module Adapter
|
6
7
|
# Adapter implementation for Mac OS X `FSEvents`.
|
7
8
|
#
|
8
9
|
class Darwin < Base
|
9
|
-
OS_REGEXP = /darwin(?<major_version>1\d+)/i
|
10
|
+
OS_REGEXP = /darwin(?<major_version>(1|2)\d+)/i.freeze
|
10
11
|
|
11
12
|
# The default delay between checking for changes.
|
12
13
|
DEFAULTS = { latency: 0.1 }.freeze
|
@@ -22,11 +23,11 @@ module Listen
|
|
22
23
|
EOS
|
23
24
|
|
24
25
|
def self.usable?
|
25
|
-
require 'rb-fsevent'
|
26
26
|
version = RbConfig::CONFIG['target_os'][OS_REGEXP, :major_version]
|
27
27
|
return false unless version
|
28
28
|
return true if version.to_i >= 13 # darwin13 is OS X 10.9
|
29
29
|
|
30
|
+
require 'rb-fsevent'
|
30
31
|
fsevent_version = Gem::Version.new(FSEvent::VERSION)
|
31
32
|
return true if fsevent_version <= Gem::Version.new('0.9.4')
|
32
33
|
Kernel.warn INCOMPATIBLE_GEM_VERSION
|
@@ -35,57 +36,41 @@ module Listen
|
|
35
36
|
|
36
37
|
private
|
37
38
|
|
38
|
-
# NOTE: each directory gets a DIFFERENT callback!
|
39
39
|
def _configure(dir, &callback)
|
40
|
-
|
41
|
-
|
42
|
-
@workers ||= ::Queue.new
|
43
|
-
@workers << FSEvent.new.tap do |worker|
|
44
|
-
_log :debug, "fsevent: watching: #{dir.to_s.inspect}"
|
45
|
-
worker.watch(dir.to_s, opts, &callback)
|
46
|
-
end
|
40
|
+
@callbacks[dir] = callback
|
47
41
|
end
|
48
42
|
|
49
43
|
def _run
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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 }
|
56
50
|
end
|
57
51
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
new_path = Pathname.new(path.sub(%r{\/$}, ''))
|
62
|
-
_log :debug, "fsevent: #{new_path}"
|
63
|
-
# TODO: does this preserve symlinks?
|
64
|
-
rel_path = new_path.relative_path_from(dir).to_s
|
65
|
-
_queue_change(:dir, dir, rel_path, recursive: true)
|
66
|
-
end
|
67
|
-
end
|
52
|
+
def _process_changes(dirs)
|
53
|
+
dirs.each do |dir|
|
54
|
+
dir = Pathname.new(dir.sub(%r{/$}, ''))
|
68
55
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
56
|
+
@callbacks.each do |watched_dir, callback|
|
57
|
+
if watched_dir.eql?(dir) || Listen::Directory.ascendant_of?(watched_dir, dir)
|
58
|
+
callback.call(dir)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
75
62
|
end
|
76
63
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
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)
|
83
69
|
end
|
84
70
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
workers
|
71
|
+
def _stop
|
72
|
+
@worker_thread&.kill
|
73
|
+
super
|
89
74
|
end
|
90
75
|
end
|
91
76
|
end
|
data/lib/listen/adapter/linux.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
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
|
@@ -18,28 +21,24 @@ module Listen
|
|
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
|
-
Thread.current[:listen_blocking_read_thread] = true
|
39
38
|
@worker.run
|
40
|
-
Thread.current[:listen_blocking_read_thread] = false
|
41
39
|
end
|
42
40
|
|
41
|
+
# rubocop:disable Metrics/MethodLength
|
43
42
|
def _process_event(dir, event)
|
44
43
|
# NOTE: avoid using event.absolute_name since new API
|
45
44
|
# will need to have a custom recursion implemented
|
@@ -47,7 +46,7 @@ module Listen
|
|
47
46
|
path = Pathname.new(event.watcher.path) + event.name
|
48
47
|
rel_path = path.relative_path_from(dir).to_s
|
49
48
|
|
50
|
-
|
49
|
+
Listen.logger.debug { "inotify: #{rel_path} (#{event.flags.inspect})" }
|
51
50
|
|
52
51
|
if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
|
53
52
|
if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
|
@@ -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
|
@@ -99,7 +99,9 @@ module Listen
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def _stop
|
102
|
-
@worker
|
102
|
+
@worker&.close
|
103
|
+
|
104
|
+
super
|
103
105
|
end
|
104
106
|
end
|
105
107
|
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,7 +8,7 @@ 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
13
|
DEFAULTS = { latency: 1.0, wait_for_delay: 0.05 }.freeze
|
12
14
|
|
@@ -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
|
@@ -26,7 +28,7 @@ module Listen
|
|
26
28
|
|
27
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])
|
@@ -36,8 +38,7 @@ module Listen
|
|
36
38
|
yield([:dir, change])
|
37
39
|
end
|
38
40
|
|
39
|
-
|
40
|
-
@worker.watch_recursively(dir.to_s, *events) do |change|
|
41
|
+
@worker.watch_recursively(dir.to_s, :attributes, :last_write) do |change|
|
41
42
|
yield([:attr, change])
|
42
43
|
end
|
43
44
|
end
|
@@ -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,10 +67,11 @@ 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
76
|
# do nothing - changed directory means either:
|
74
77
|
# - removed subdirs (handled above)
|
@@ -78,20 +81,15 @@ module Listen
|
|
78
81
|
# so what's left?
|
79
82
|
end
|
80
83
|
end
|
81
|
-
rescue
|
82
|
-
details = event.inspect
|
83
|
-
_log :error, format('wdm - callback (%s): %s:%s', details, $ERROR_INFO,
|
84
|
-
$ERROR_POSITION * "\n")
|
85
|
-
raise
|
86
84
|
end
|
85
|
+
# rubocop:enable Metrics/MethodLength
|
87
86
|
|
88
87
|
def _change(type)
|
89
88
|
{ modified: [:modified, :attrib], # TODO: is attrib really passed?
|
90
89
|
added: [:added, :renamed_new_file],
|
91
|
-
removed: [:removed, :renamed_old_file] }.
|
92
|
-
|
90
|
+
removed: [:removed, :renamed_old_file] }.find do |change, types|
|
91
|
+
types.include?(type) and break change
|
93
92
|
end
|
94
|
-
nil
|
95
93
|
end
|
96
94
|
end
|
97
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'
|
@@ -9,37 +11,33 @@ module Listen
|
|
9
11
|
module Adapter
|
10
12
|
OPTIMIZED_ADAPTERS = [Darwin, Linux, BSD, Windows].freeze
|
11
13
|
POLLING_FALLBACK_MESSAGE = 'Listen will be polling for changes.'\
|
12
|
-
'Learn more at https://github.com/guard/listen#listen-adapters.'
|
14
|
+
'Learn more at https://github.com/guard/listen#listen-adapters.'
|
13
15
|
|
14
16
|
class << self
|
15
17
|
def select(options = {})
|
16
|
-
|
18
|
+
Listen.logger.debug 'Adapter: considering polling ...'
|
17
19
|
return Polling if options[:force_polling]
|
18
|
-
|
20
|
+
Listen.logger.debug 'Adapter: considering optimized backend...'
|
19
21
|
return _usable_adapter_class if _usable_adapter_class
|
20
|
-
|
22
|
+
Listen.logger.debug 'Adapter: falling back to polling...'
|
21
23
|
_warn_polling_fallback(options)
|
22
24
|
Polling
|
23
25
|
rescue
|
24
|
-
|
25
|
-
|
26
|
+
Listen.logger.warn format('Adapter: failed: %s:%s', $ERROR_POSITION.inspect,
|
27
|
+
$ERROR_POSITION * "\n")
|
26
28
|
raise
|
27
29
|
end
|
28
30
|
|
29
31
|
private
|
30
32
|
|
31
33
|
def _usable_adapter_class
|
32
|
-
OPTIMIZED_ADAPTERS.
|
34
|
+
OPTIMIZED_ADAPTERS.find(&:usable?)
|
33
35
|
end
|
34
36
|
|
35
37
|
def _warn_polling_fallback(options)
|
36
38
|
msg = options.fetch(:polling_fallback_message, POLLING_FALLBACK_MESSAGE)
|
37
39
|
Kernel.warn "[Listen warning]:\n #{msg}" if msg
|
38
40
|
end
|
39
|
-
|
40
|
-
def _log(type, message)
|
41
|
-
Listen::Logger.send(type, message)
|
42
|
-
end
|
43
41
|
end
|
44
42
|
end
|
45
43
|
end
|
data/lib/listen/backend.rb
CHANGED
data/lib/listen/change.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'listen/file'
|
2
4
|
require 'listen/directory'
|
3
5
|
|
@@ -28,49 +30,40 @@ module Listen
|
|
28
30
|
end
|
29
31
|
|
30
32
|
# Invalidate some part of the snapshot/record (dir, file, subtree, etc.)
|
33
|
+
# rubocop:disable Metrics/MethodLength
|
34
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
35
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
31
36
|
def invalidate(type, rel_path, options)
|
32
37
|
watched_dir = Pathname.new(record.root)
|
33
38
|
|
34
39
|
change = options[:change]
|
35
40
|
cookie = options[:cookie]
|
36
41
|
|
37
|
-
if !cookie && config.silenced?(rel_path, type)
|
38
|
-
Listen
|
42
|
+
if !cookie && @config.silenced?(rel_path, type)
|
43
|
+
Listen.logger.debug { "(silenced): #{rel_path.inspect}" }
|
39
44
|
return
|
40
45
|
end
|
41
46
|
|
42
47
|
path = watched_dir + rel_path
|
43
48
|
|
44
|
-
Listen
|
49
|
+
Listen.logger.debug do
|
45
50
|
log_details = options[:silence] && 'recording' || change || 'unknown'
|
46
51
|
"#{log_details}: #{type}:#{path} (#{options.inspect})"
|
47
52
|
end
|
48
53
|
|
49
54
|
if change
|
50
55
|
options = cookie ? { cookie: cookie } : {}
|
51
|
-
config.queue(type, change, watched_dir, rel_path, options)
|
56
|
+
@config.queue(type, change, watched_dir, rel_path, options)
|
52
57
|
elsif type == :dir
|
53
58
|
# NOTE: POSSIBLE RECURSION
|
54
59
|
# TODO: fix - use a queue instead
|
55
60
|
Directory.scan(self, rel_path, options)
|
56
|
-
|
57
|
-
|
58
|
-
return if !change || options[:silence]
|
59
|
-
config.queue(:file, change, watched_dir, rel_path)
|
61
|
+
elsif (change = File.change(record, rel_path)) && !options[:silence]
|
62
|
+
@config.queue(:file, change, watched_dir, rel_path)
|
60
63
|
end
|
61
|
-
rescue RuntimeError => ex
|
62
|
-
msg = format(
|
63
|
-
'%s#%s crashed %s:%s',
|
64
|
-
self.class,
|
65
|
-
__method__,
|
66
|
-
exinspect,
|
67
|
-
ex.backtrace * "\n")
|
68
|
-
Listen::Logger.error(msg)
|
69
|
-
raise
|
70
64
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
attr_reader :config
|
65
|
+
# rubocop:enable Metrics/MethodLength
|
66
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
67
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
75
68
|
end
|
76
69
|
end
|
data/lib/listen/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require 'listen'
|
3
5
|
require 'logger'
|
@@ -33,15 +35,16 @@ module Listen
|
|
33
35
|
|
34
36
|
class Forwarder
|
35
37
|
attr_reader :logger
|
38
|
+
|
36
39
|
def initialize(options)
|
37
40
|
@options = options
|
38
|
-
@logger = ::Logger.new(STDOUT)
|
39
|
-
@logger.level = ::Logger::INFO
|
41
|
+
@logger = ::Logger.new(STDOUT, level: ::Logger::INFO)
|
40
42
|
@logger.formatter = proc { |_, _, _, msg| "#{msg}\n" }
|
41
43
|
end
|
42
44
|
|
43
45
|
def start
|
44
46
|
logger.info 'Starting listen...'
|
47
|
+
|
45
48
|
directory = @options[:directory]
|
46
49
|
relative = @options[:relative]
|
47
50
|
callback = proc do |modified, added, removed|
|
@@ -52,10 +55,7 @@ module Listen
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
55
|
-
listener = Listen.to(
|
56
|
-
directory,
|
57
|
-
relative: relative,
|
58
|
-
&callback)
|
58
|
+
listener = Listen.to(directory, relative: relative, &callback)
|
59
59
|
|
60
60
|
listener.start
|
61
61
|
|
data/lib/listen/directory.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
module Listen
|
4
6
|
# TODO: refactor (turn it into a normal object, cache the stat, etc)
|
5
7
|
class Directory
|
8
|
+
# rubocop:disable Metrics/MethodLength
|
6
9
|
def self.scan(snapshot, rel_path, options)
|
7
10
|
record = snapshot.record
|
8
11
|
dir = Pathname.new(record.root)
|
@@ -14,7 +17,7 @@ module Listen
|
|
14
17
|
path = dir + rel_path
|
15
18
|
current = Set.new(_children(path))
|
16
19
|
|
17
|
-
Listen
|
20
|
+
Listen.logger.debug do
|
18
21
|
format('%s: %s(%s): %s -> %s',
|
19
22
|
(options[:silence] ? 'Recording' : 'Scanning'),
|
20
23
|
rel_path, options.inspect, previous.inspect, current.inspect)
|
@@ -36,22 +39,25 @@ module Listen
|
|
36
39
|
previous = previous.reject { |entry, _| current.include? path + entry }
|
37
40
|
|
38
41
|
_async_changes(snapshot, Pathname.new(rel_path), previous, options)
|
39
|
-
|
40
42
|
rescue Errno::ENOENT, Errno::EHOSTDOWN
|
41
43
|
record.unset_path(rel_path)
|
42
44
|
_async_changes(snapshot, Pathname.new(rel_path), previous, options)
|
43
|
-
|
44
45
|
rescue Errno::ENOTDIR
|
45
46
|
# TODO: path not tested
|
46
47
|
record.unset_path(rel_path)
|
47
48
|
_async_changes(snapshot, path, previous, options)
|
48
49
|
_change(snapshot, :file, rel_path, options)
|
49
50
|
rescue
|
50
|
-
Listen
|
51
|
-
format('scan DIED: %s:%s', $ERROR_INFO, $ERROR_POSITION * "\n")
|
52
|
-
end
|
51
|
+
Listen.logger.warn { format('scan DIED: %s:%s', $ERROR_INFO, $ERROR_POSITION * "\n") }
|
53
52
|
raise
|
54
53
|
end
|
54
|
+
# rubocop:enable Metrics/MethodLength
|
55
|
+
|
56
|
+
def self.ascendant_of?(base, other)
|
57
|
+
other.ascend do |ascendant|
|
58
|
+
break true if base == ascendant
|
59
|
+
end
|
60
|
+
end
|
55
61
|
|
56
62
|
def self._async_changes(snapshot, path, previous, options)
|
57
63
|
fail "Not a Pathname: #{path.inspect}" unless path.respond_to?(:children)
|
@@ -80,8 +86,8 @@ module Listen
|
|
80
86
|
# https://github.com/jruby/jruby/issues/3840
|
81
87
|
exists = path.exist?
|
82
88
|
directory = path.directory?
|
83
|
-
|
84
|
-
|
89
|
+
exists && !directory and raise Errno::ENOTDIR, path.to_s
|
90
|
+
path.children
|
85
91
|
end
|
86
92
|
end
|
87
93
|
end
|
data/lib/listen/error.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Besides programming error exceptions like ArgumentError,
|
4
|
+
# all public interface exceptions should be declared here and inherit from Listen::Error.
|
5
|
+
module Listen
|
6
|
+
class Error < RuntimeError
|
7
|
+
class NotStarted < Error; end
|
8
|
+
class SymlinkLoop < Error; end
|
9
|
+
class INotifyMaxWatchesExceeded < Error; end
|
10
|
+
end
|
11
|
+
end
|
data/lib/listen/event/config.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Listen
|
2
4
|
module Event
|
3
5
|
class Config
|
6
|
+
attr_reader :listener, :event_queue, :min_delay_between_events
|
7
|
+
|
4
8
|
def initialize(
|
5
9
|
listener,
|
6
10
|
event_queue,
|
7
11
|
queue_optimizer,
|
8
12
|
wait_for_delay,
|
9
|
-
&block
|
13
|
+
&block
|
14
|
+
)
|
10
15
|
|
11
16
|
@listener = listener
|
12
17
|
@event_queue = event_queue
|
@@ -15,20 +20,14 @@ module Listen
|
|
15
20
|
@block = block
|
16
21
|
end
|
17
22
|
|
18
|
-
def sleep(
|
19
|
-
Kernel.sleep(
|
23
|
+
def sleep(seconds)
|
24
|
+
Kernel.sleep(seconds)
|
20
25
|
end
|
21
26
|
|
22
27
|
def call(*args)
|
23
|
-
@block
|
24
|
-
end
|
25
|
-
|
26
|
-
def timestamp
|
27
|
-
Time.now.to_f
|
28
|
+
@block&.call(*args)
|
28
29
|
end
|
29
30
|
|
30
|
-
attr_reader :event_queue
|
31
|
-
|
32
31
|
def callable?
|
33
32
|
@block
|
34
33
|
end
|
@@ -36,20 +35,6 @@ module Listen
|
|
36
35
|
def optimize_changes(changes)
|
37
36
|
@queue_optimizer.smoosh_changes(changes)
|
38
37
|
end
|
39
|
-
|
40
|
-
attr_reader :min_delay_between_events
|
41
|
-
|
42
|
-
def stopped?
|
43
|
-
listener.state == :stopped
|
44
|
-
end
|
45
|
-
|
46
|
-
def paused?
|
47
|
-
listener.state == :paused
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
attr_reader :listener
|
53
38
|
end
|
54
39
|
end
|
55
40
|
end
|