listen 3.1.5 → 3.7.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 +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
|