listen 3.2.1 → 3.3.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 +4 -4
- data/README.md +110 -65
- data/lib/listen.rb +15 -20
- data/lib/listen/adapter.rb +6 -8
- data/lib/listen/adapter/base.rb +17 -29
- data/lib/listen/adapter/bsd.rb +3 -1
- data/lib/listen/adapter/config.rb +2 -0
- data/lib/listen/adapter/darwin.rb +10 -12
- data/lib/listen/adapter/linux.rb +7 -4
- data/lib/listen/adapter/polling.rb +2 -0
- data/lib/listen/adapter/windows.rb +6 -4
- data/lib/listen/backend.rb +2 -0
- data/lib/listen/change.rb +5 -3
- data/lib/listen/cli.rb +3 -2
- data/lib/listen/directory.rb +4 -2
- data/lib/listen/event/config.rb +8 -18
- data/lib/listen/event/loop.rb +43 -64
- data/lib/listen/event/processor.rb +31 -25
- data/lib/listen/event/queue.rb +4 -5
- data/lib/listen/file.rb +9 -2
- data/lib/listen/fsm.rb +69 -71
- data/lib/listen/listener.rb +24 -23
- data/lib/listen/listener/config.rb +2 -0
- data/lib/listen/logger.rb +27 -24
- data/lib/listen/options.rb +3 -1
- data/lib/listen/queue_optimizer.rb +2 -0
- data/lib/listen/record.rb +16 -2
- data/lib/listen/record/entry.rb +3 -1
- data/lib/listen/record/symlink_detector.rb +2 -0
- data/lib/listen/silencer.rb +2 -0
- data/lib/listen/silencer/controller.rb +2 -0
- data/lib/listen/thread.rb +52 -0
- data/lib/listen/version.rb +3 -1
- metadata +7 -23
- 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
|
@@ -94,7 +96,7 @@ module Listen
|
|
94
96
|
def _watch_file(path, queue)
|
95
97
|
queue.watch_file(path, *options.events, &@callback)
|
96
98
|
rescue Errno::ENOENT => e
|
97
|
-
|
99
|
+
Listen.logger.warn "kqueue: watch file failed: #{e.message}"
|
98
100
|
end
|
99
101
|
|
100
102
|
# Quick rubocop workaround
|
@@ -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
|
10
11
|
|
11
12
|
# The default delay between checking for changes.
|
12
13
|
DEFAULTS = { latency: 0.1 }.freeze
|
@@ -43,9 +44,9 @@ module Listen
|
|
43
44
|
require 'rb-fsevent'
|
44
45
|
worker = FSEvent.new
|
45
46
|
dirs_to_watch = @callbacks.keys.map(&:to_s)
|
46
|
-
|
47
|
+
Listen.logger.info { "fsevent: watching: #{dirs_to_watch.inspect}" }
|
47
48
|
worker.watch(dirs_to_watch, { latency: options.latency }, &method(:_process_changes))
|
48
|
-
Listen::
|
49
|
+
@worker_thread = Listen::Thread.new("worker_thread") { worker.run }
|
49
50
|
end
|
50
51
|
|
51
52
|
def _process_changes(dirs)
|
@@ -61,18 +62,15 @@ module Listen
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def _process_event(dir, path)
|
64
|
-
|
65
|
+
Listen.logger.debug { "fsevent: processing path: #{path.inspect}" }
|
65
66
|
# TODO: does this preserve symlinks?
|
66
67
|
rel_path = path.relative_path_from(dir).to_s
|
67
68
|
_queue_change(:dir, dir, rel_path, recursive: true)
|
68
69
|
end
|
69
70
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
rescue
|
74
|
-
format_string = 'fsevent: running worker failed: %s:%s called from: %s'
|
75
|
-
_log_exception format_string, caller
|
71
|
+
def _stop
|
72
|
+
@worker_thread&.kill
|
73
|
+
super
|
76
74
|
end
|
77
75
|
end
|
78
76
|
end
|
data/lib/listen/adapter/linux.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Listen
|
2
4
|
module Adapter
|
3
5
|
# @see https://github.com/nex3/rb-inotify
|
@@ -9,6 +11,7 @@ module Listen
|
|
9
11
|
:recursive,
|
10
12
|
:attrib,
|
11
13
|
:create,
|
14
|
+
:modify,
|
12
15
|
:delete,
|
13
16
|
:move,
|
14
17
|
:close_write
|
@@ -35,9 +38,7 @@ module Listen
|
|
35
38
|
end
|
36
39
|
|
37
40
|
def _run
|
38
|
-
Thread.current[:listen_blocking_read_thread] = true
|
39
41
|
@worker.run
|
40
|
-
Thread.current[:listen_blocking_read_thread] = false
|
41
42
|
end
|
42
43
|
|
43
44
|
def _process_event(dir, event)
|
@@ -47,7 +48,7 @@ module Listen
|
|
47
48
|
path = Pathname.new(event.watcher.path) + event.name
|
48
49
|
rel_path = path.relative_path_from(dir).to_s
|
49
50
|
|
50
|
-
|
51
|
+
Listen.logger.debug { "inotify: #{rel_path} (#{event.flags.inspect})" }
|
51
52
|
|
52
53
|
if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
|
53
54
|
if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
|
@@ -99,7 +100,9 @@ module Listen
|
|
99
100
|
end
|
100
101
|
|
101
102
|
def _stop
|
102
|
-
@worker
|
103
|
+
@worker&.close
|
104
|
+
|
105
|
+
super
|
103
106
|
end
|
104
107
|
end
|
105
108
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Listen
|
2
4
|
module Adapter
|
3
5
|
# Adapter implementation for Windows `wdm`.
|
@@ -15,7 +17,7 @@ module Listen
|
|
15
17
|
require 'wdm'
|
16
18
|
true
|
17
19
|
rescue LoadError
|
18
|
-
|
20
|
+
Listen.logger.debug format('wdm - load failed: %s:%s', $ERROR_INFO,
|
19
21
|
$ERROR_POSITION * "\n")
|
20
22
|
|
21
23
|
Kernel.warn BUNDLER_DECLARE_GEM
|
@@ -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])
|
@@ -47,7 +49,7 @@ module Listen
|
|
47
49
|
end
|
48
50
|
|
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
|
|
@@ -80,7 +82,7 @@ module Listen
|
|
80
82
|
end
|
81
83
|
rescue
|
82
84
|
details = event.inspect
|
83
|
-
|
85
|
+
Listen.logger.error format('wdm - callback (%s): %s:%s', details, $ERROR_INFO,
|
84
86
|
$ERROR_POSITION * "\n")
|
85
87
|
raise
|
86
88
|
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
|
|
@@ -35,13 +37,13 @@ module Listen
|
|
35
37
|
cookie = options[:cookie]
|
36
38
|
|
37
39
|
if !cookie && config.silenced?(rel_path, type)
|
38
|
-
Listen
|
40
|
+
Listen.logger.debug { "(silenced): #{rel_path.inspect}" }
|
39
41
|
return
|
40
42
|
end
|
41
43
|
|
42
44
|
path = watched_dir + rel_path
|
43
45
|
|
44
|
-
Listen
|
46
|
+
Listen.logger.debug do
|
45
47
|
log_details = options[:silence] && 'recording' || change || 'unknown'
|
46
48
|
"#{log_details}: #{type}:#{path} (#{options.inspect})"
|
47
49
|
end
|
@@ -65,7 +67,7 @@ module Listen
|
|
65
67
|
__method__,
|
66
68
|
exinspect,
|
67
69
|
ex.backtrace * "\n")
|
68
|
-
Listen
|
70
|
+
Listen.logger.error(msg)
|
69
71
|
raise
|
70
72
|
end
|
71
73
|
|
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'
|
@@ -35,8 +37,7 @@ module Listen
|
|
35
37
|
attr_reader :logger
|
36
38
|
def initialize(options)
|
37
39
|
@options = options
|
38
|
-
@logger = ::Logger.new(STDOUT)
|
39
|
-
@logger.level = ::Logger::INFO
|
40
|
+
@logger = ::Logger.new(STDOUT, level: ::Logger::INFO)
|
40
41
|
@logger.formatter = proc { |_, _, _, msg| "#{msg}\n" }
|
41
42
|
end
|
42
43
|
|
data/lib/listen/directory.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
module Listen
|
@@ -14,7 +16,7 @@ module Listen
|
|
14
16
|
path = dir + rel_path
|
15
17
|
current = Set.new(_children(path))
|
16
18
|
|
17
|
-
Listen
|
19
|
+
Listen.logger.debug do
|
18
20
|
format('%s: %s(%s): %s -> %s',
|
19
21
|
(options[:silence] ? 'Recording' : 'Scanning'),
|
20
22
|
rel_path, options.inspect, previous.inspect, current.inspect)
|
@@ -47,7 +49,7 @@ module Listen
|
|
47
49
|
_async_changes(snapshot, path, previous, options)
|
48
50
|
_change(snapshot, :file, rel_path, options)
|
49
51
|
rescue
|
50
|
-
Listen
|
52
|
+
Listen.logger.warn do
|
51
53
|
format('scan DIED: %s:%s', $ERROR_INFO, $ERROR_POSITION * "\n")
|
52
54
|
end
|
53
55
|
raise
|
data/lib/listen/event/config.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Listen
|
2
4
|
module Event
|
3
5
|
class Config
|
6
|
+
attr_reader :listener
|
7
|
+
attr_reader :event_queue
|
8
|
+
attr_reader :min_delay_between_events
|
9
|
+
|
4
10
|
def initialize(
|
5
11
|
listener,
|
6
12
|
event_queue,
|
@@ -15,8 +21,8 @@ module Listen
|
|
15
21
|
@block = block
|
16
22
|
end
|
17
23
|
|
18
|
-
def sleep(
|
19
|
-
Kernel.sleep(
|
24
|
+
def sleep(seconds)
|
25
|
+
Kernel.sleep(seconds)
|
20
26
|
end
|
21
27
|
|
22
28
|
def call(*args)
|
@@ -27,8 +33,6 @@ module Listen
|
|
27
33
|
Time.now.to_f
|
28
34
|
end
|
29
35
|
|
30
|
-
attr_reader :event_queue
|
31
|
-
|
32
36
|
def callable?
|
33
37
|
@block
|
34
38
|
end
|
@@ -36,20 +40,6 @@ module Listen
|
|
36
40
|
def optimize_changes(changes)
|
37
41
|
@queue_optimizer.smoosh_changes(changes)
|
38
42
|
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
43
|
end
|
54
44
|
end
|
55
45
|
end
|
data/lib/listen/event/loop.rb
CHANGED
@@ -1,55 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thread'
|
2
4
|
|
3
5
|
require 'timeout'
|
4
6
|
require 'listen/event/processor'
|
7
|
+
require 'listen/thread'
|
5
8
|
|
6
9
|
module Listen
|
7
10
|
module Event
|
8
11
|
class Loop
|
12
|
+
include Listen::FSM
|
13
|
+
|
9
14
|
class Error < RuntimeError
|
10
|
-
class NotStarted < Error
|
11
|
-
end
|
15
|
+
class NotStarted < Error; end
|
12
16
|
end
|
13
17
|
|
18
|
+
start_state :pre_start
|
19
|
+
state :pre_start
|
20
|
+
state :starting
|
21
|
+
state :started
|
22
|
+
state :stopped
|
23
|
+
|
14
24
|
def initialize(config)
|
15
25
|
@config = config
|
16
26
|
@wait_thread = nil
|
17
|
-
@state = :paused
|
18
27
|
@reasons = ::Queue.new
|
28
|
+
initialize_fsm
|
19
29
|
end
|
20
30
|
|
21
31
|
def wakeup_on_event
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
_wakeup(:event)
|
32
|
+
if started? && @wait_thread&.alive?
|
33
|
+
_wakeup(:event)
|
34
|
+
end
|
26
35
|
end
|
27
36
|
|
28
|
-
def
|
29
|
-
|
37
|
+
def started?
|
38
|
+
state == :started
|
30
39
|
end
|
31
40
|
|
32
|
-
|
33
|
-
return false if stopped?
|
34
|
-
return false if paused?
|
35
|
-
state == :processing
|
36
|
-
end
|
41
|
+
MAX_STARTUP_SECONDS = 5.0
|
37
42
|
|
38
|
-
def
|
43
|
+
def start
|
39
44
|
# TODO: use a Fiber instead?
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
return unless state == :pre_start
|
46
|
+
|
47
|
+
transition! :starting
|
48
|
+
|
49
|
+
@wait_thread = Listen::Thread.new("wait_thread") do
|
50
|
+
_process_changes
|
43
51
|
end
|
44
52
|
|
45
|
-
Listen
|
46
|
-
Timeout.timeout(5) { q.pop }
|
47
|
-
end
|
53
|
+
Listen.logger.debug("Waiting for processing to start...")
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
55
|
+
wait_for_state(:started, MAX_STARTUP_SECONDS) or
|
56
|
+
raise Error::NotStarted, "thread didn't start in #{MAX_STARTUP_SECONDS} seconds (in state: #{state.inspect})"
|
57
|
+
|
58
|
+
Listen.logger.debug('Processing started.')
|
53
59
|
end
|
54
60
|
|
55
61
|
def pause
|
@@ -57,60 +63,33 @@ module Listen
|
|
57
63
|
# fail NotImplementedError
|
58
64
|
end
|
59
65
|
|
60
|
-
def
|
61
|
-
return
|
62
|
-
|
63
|
-
|
64
|
-
|
66
|
+
def stop
|
67
|
+
return if stopped?
|
68
|
+
transition! :stopped
|
69
|
+
|
70
|
+
if @wait_thread.alive?
|
71
|
+
@wait_thread.join
|
65
72
|
end
|
66
73
|
@wait_thread = nil
|
67
74
|
end
|
68
75
|
|
69
76
|
def stopped?
|
70
|
-
|
77
|
+
state == :stopped
|
71
78
|
end
|
72
79
|
|
73
80
|
private
|
74
81
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
attr_accessor :state
|
79
|
-
|
80
|
-
def _wait_for_changes(ready_queue, config)
|
81
|
-
processor = Event::Processor.new(config, @reasons)
|
82
|
+
def _process_changes
|
83
|
+
processor = Event::Processor.new(@config, @reasons)
|
82
84
|
|
83
|
-
|
84
|
-
processor.loop_for(config.min_delay_between_events)
|
85
|
-
rescue StandardError => ex
|
86
|
-
_nice_error(ex)
|
87
|
-
end
|
88
|
-
|
89
|
-
def _sleep(*args)
|
90
|
-
Kernel.sleep(*args)
|
91
|
-
end
|
92
|
-
|
93
|
-
def _wait_until_resumed(ready_queue)
|
94
|
-
self.state = :paused
|
95
|
-
ready_queue << :ready
|
96
|
-
sleep
|
97
|
-
self.state = :processing
|
98
|
-
end
|
85
|
+
transition! :started
|
99
86
|
|
100
|
-
|
101
|
-
indent = "\n -- "
|
102
|
-
msg = format(
|
103
|
-
'exception while processing events: %s Backtrace:%s%s',
|
104
|
-
ex,
|
105
|
-
indent,
|
106
|
-
ex.backtrace * indent
|
107
|
-
)
|
108
|
-
Listen::Logger.error(msg)
|
87
|
+
processor.loop_for(@config.min_delay_between_events)
|
109
88
|
end
|
110
89
|
|
111
90
|
def _wakeup(reason)
|
112
91
|
@reasons << reason
|
113
|
-
wait_thread.wakeup
|
92
|
+
@wait_thread.wakeup
|
114
93
|
end
|
115
94
|
end
|
116
95
|
end
|