listen 3.1.4 → 3.3.0.pre.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CONTRIBUTING.md +8 -1
- data/README.md +130 -68
- data/lib/listen.rb +15 -25
- 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 +29 -44
- 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 +10 -2
- data/lib/listen/event/config.rb +8 -18
- data/lib/listen/event/loop.rb +43 -64
- data/lib/listen/event/processor.rb +26 -24
- 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 +25 -24
- 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 +6 -4
- 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 +5 -0
- data/lib/listen/silencer/controller.rb +2 -0
- data/lib/listen/thread.rb +47 -0
- data/lib/listen/version.rb +3 -1
- metadata +16 -48
- data/lib/listen/internals/thread_pool.rb +0 -29
data/lib/listen/adapter/base.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
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
|
@@ -30,7 +33,7 @@ module Listen
|
|
30
33
|
# TODO: it's a separate method as a temporary workaround for tests
|
31
34
|
def configure
|
32
35
|
if @configured
|
33
|
-
|
36
|
+
Listen.logger.warn('Adapter already configured!')
|
34
37
|
return
|
35
38
|
end
|
36
39
|
|
@@ -63,48 +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.values.each do |snapshot|
|
77
|
+
_timed('Record.build()') { snapshot.record.build }
|
84
78
|
end
|
79
|
+
_run
|
85
80
|
end
|
86
81
|
end
|
87
82
|
|
88
83
|
def stop
|
89
84
|
_stop
|
90
|
-
|
91
|
-
|
92
|
-
def self.usable?
|
93
|
-
const_get('OS_REGEXP') =~ RbConfig::CONFIG['target_os']
|
85
|
+
config.queue.close # this causes queue.pop to return `nil` to the front-end
|
94
86
|
end
|
95
87
|
|
96
88
|
private
|
97
89
|
|
98
90
|
def _stop
|
91
|
+
@run_thread&.kill
|
92
|
+
@run_thread = nil
|
99
93
|
end
|
100
94
|
|
101
95
|
def _timed(title)
|
102
96
|
start = Time.now.to_f
|
103
97
|
yield
|
104
98
|
diff = Time.now.to_f - start
|
105
|
-
Listen
|
99
|
+
Listen.logger.info format('%s: %.05f seconds', title, diff)
|
106
100
|
rescue
|
107
|
-
Listen
|
101
|
+
Listen.logger.warn "#{title} crashed: #{$ERROR_INFO.inspect}"
|
108
102
|
raise
|
109
103
|
end
|
110
104
|
|
@@ -114,10 +108,6 @@ module Listen
|
|
114
108
|
@snapshots[dir].invalidate(type, rel_path, options)
|
115
109
|
end
|
116
110
|
|
117
|
-
def _log(*args, &block)
|
118
|
-
self.class.send(:_log, *args, &block)
|
119
|
-
end
|
120
|
-
|
121
111
|
def _log_exception(msg, caller_stack)
|
122
112
|
formatted = format(
|
123
113
|
msg,
|
@@ -126,14 +116,12 @@ module Listen
|
|
126
116
|
caller_stack * "\n"
|
127
117
|
)
|
128
118
|
|
129
|
-
|
119
|
+
Listen.logger.error(formatted)
|
130
120
|
end
|
131
121
|
|
132
122
|
class << self
|
133
|
-
|
134
|
-
|
135
|
-
def _log(*args, &block)
|
136
|
-
Listen::Logger.send(*args, &block)
|
123
|
+
def usable?
|
124
|
+
const_get('OS_REGEXP') =~ RbConfig::CONFIG['target_os']
|
137
125
|
end
|
138
126
|
end
|
139
127
|
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
|
@@ -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
|
@@ -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,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,12 +49,18 @@ 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
|
54
56
|
end
|
55
57
|
|
58
|
+
def self.ascendant_of?(base, other)
|
59
|
+
other.ascend do |ascendant|
|
60
|
+
break true if base == ascendant
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
56
64
|
def self._async_changes(snapshot, path, previous, options)
|
57
65
|
fail "Not a Pathname: #{path.inspect}" unless path.respond_to?(:children)
|
58
66
|
previous.each do |entry, data|
|
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
|