listen 3.3.0 → 3.9.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/CONTRIBUTING.md +2 -2
- data/README.md +156 -24
- data/bin/listen +3 -4
- data/lib/listen/adapter/base.rb +7 -7
- data/lib/listen/adapter/bsd.rb +4 -5
- data/lib/listen/adapter/config.rb +7 -4
- data/lib/listen/adapter/darwin.rb +3 -3
- data/lib/listen/adapter/linux.rb +8 -9
- data/lib/listen/adapter/polling.rb +6 -5
- data/lib/listen/adapter/windows.rb +11 -15
- data/lib/listen/adapter.rb +4 -4
- data/lib/listen/change.rb +10 -19
- data/lib/listen/cli.rb +5 -6
- data/lib/listen/directory.rb +6 -8
- data/lib/listen/error.rb +11 -0
- data/lib/listen/event/config.rb +4 -9
- data/lib/listen/event/loop.rb +6 -8
- data/lib/listen/event/processor.rb +13 -15
- data/lib/listen/event/queue.rb +9 -9
- data/lib/listen/file.rb +13 -5
- data/lib/listen/fsm.rb +5 -2
- data/lib/listen/listener/config.rb +2 -4
- data/lib/listen/listener.rb +2 -0
- data/lib/listen/logger.rb +42 -12
- data/lib/listen/monotonic_time.rb +27 -0
- data/lib/listen/options.rb +9 -8
- data/lib/listen/queue_optimizer.rb +7 -12
- data/lib/listen/record/entry.rb +1 -1
- data/lib/listen/record/symlink_detector.rb +14 -8
- data/lib/listen/record.rb +37 -46
- data/lib/listen/silencer.rb +24 -20
- data/lib/listen/thread.rb +14 -12
- data/lib/listen/version.rb +1 -1
- metadata +15 -8
data/lib/listen/change.rb
CHANGED
@@ -30,13 +30,16 @@ module Listen
|
|
30
30
|
end
|
31
31
|
|
32
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
|
33
36
|
def invalidate(type, rel_path, options)
|
34
37
|
watched_dir = Pathname.new(record.root)
|
35
38
|
|
36
39
|
change = options[:change]
|
37
40
|
cookie = options[:cookie]
|
38
41
|
|
39
|
-
if !cookie && config.silenced?(rel_path, type)
|
42
|
+
if !cookie && @config.silenced?(rel_path, type)
|
40
43
|
Listen.logger.debug { "(silenced): #{rel_path.inspect}" }
|
41
44
|
return
|
42
45
|
end
|
@@ -50,29 +53,17 @@ module Listen
|
|
50
53
|
|
51
54
|
if change
|
52
55
|
options = cookie ? { cookie: cookie } : {}
|
53
|
-
config.queue(type, change, watched_dir, rel_path, options)
|
56
|
+
@config.queue(type, change, watched_dir, rel_path, options)
|
54
57
|
elsif type == :dir
|
55
58
|
# NOTE: POSSIBLE RECURSION
|
56
59
|
# TODO: fix - use a queue instead
|
57
60
|
Directory.scan(self, rel_path, options)
|
58
|
-
|
59
|
-
|
60
|
-
return if !change || options[:silence]
|
61
|
-
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)
|
62
63
|
end
|
63
|
-
rescue RuntimeError => ex
|
64
|
-
msg = format(
|
65
|
-
'%s#%s crashed %s:%s',
|
66
|
-
self.class,
|
67
|
-
__method__,
|
68
|
-
exinspect,
|
69
|
-
ex.backtrace * "\n")
|
70
|
-
Listen.logger.error(msg)
|
71
|
-
raise
|
72
64
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
attr_reader :config
|
65
|
+
# rubocop:enable Metrics/MethodLength
|
66
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
67
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
77
68
|
end
|
78
69
|
end
|
data/lib/listen/cli.rb
CHANGED
@@ -18,9 +18,9 @@ module Listen
|
|
18
18
|
|
19
19
|
class_option :directory,
|
20
20
|
type: :array,
|
21
|
-
default: '.',
|
21
|
+
default: ['.'],
|
22
22
|
aliases: '-d',
|
23
|
-
banner: '
|
23
|
+
banner: 'One or more directories to listen to'
|
24
24
|
|
25
25
|
class_option :relative,
|
26
26
|
type: :boolean,
|
@@ -35,6 +35,7 @@ module Listen
|
|
35
35
|
|
36
36
|
class Forwarder
|
37
37
|
attr_reader :logger
|
38
|
+
|
38
39
|
def initialize(options)
|
39
40
|
@options = options
|
40
41
|
@logger = ::Logger.new(STDOUT, level: ::Logger::INFO)
|
@@ -43,6 +44,7 @@ module Listen
|
|
43
44
|
|
44
45
|
def start
|
45
46
|
logger.info 'Starting listen...'
|
47
|
+
|
46
48
|
directory = @options[:directory]
|
47
49
|
relative = @options[:relative]
|
48
50
|
callback = proc do |modified, added, removed|
|
@@ -53,10 +55,7 @@ module Listen
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
56
|
-
listener = Listen.to(
|
57
|
-
directory,
|
58
|
-
relative: relative,
|
59
|
-
&callback)
|
58
|
+
listener = Listen.to(*directory, relative: relative, &callback)
|
60
59
|
|
61
60
|
listener.start
|
62
61
|
|
data/lib/listen/directory.rb
CHANGED
@@ -5,6 +5,7 @@ require 'set'
|
|
5
5
|
module Listen
|
6
6
|
# TODO: refactor (turn it into a normal object, cache the stat, etc)
|
7
7
|
class Directory
|
8
|
+
# rubocop:disable Metrics/MethodLength
|
8
9
|
def self.scan(snapshot, rel_path, options)
|
9
10
|
record = snapshot.record
|
10
11
|
dir = Pathname.new(record.root)
|
@@ -35,25 +36,22 @@ module Listen
|
|
35
36
|
end
|
36
37
|
|
37
38
|
# TODO: this is not tested properly
|
38
|
-
previous = previous.reject { |entry, _| current.include?
|
39
|
+
previous = previous.reject { |entry, _| current.include?(path + entry) }
|
39
40
|
|
40
41
|
_async_changes(snapshot, Pathname.new(rel_path), previous, options)
|
41
|
-
|
42
42
|
rescue Errno::ENOENT, Errno::EHOSTDOWN
|
43
43
|
record.unset_path(rel_path)
|
44
44
|
_async_changes(snapshot, Pathname.new(rel_path), previous, options)
|
45
|
-
|
46
45
|
rescue Errno::ENOTDIR
|
47
46
|
# TODO: path not tested
|
48
47
|
record.unset_path(rel_path)
|
49
48
|
_async_changes(snapshot, path, previous, options)
|
50
49
|
_change(snapshot, :file, rel_path, options)
|
51
50
|
rescue
|
52
|
-
Listen.logger.warn
|
53
|
-
format('scan DIED: %s:%s', $ERROR_INFO, $ERROR_POSITION * "\n")
|
54
|
-
end
|
51
|
+
Listen.logger.warn { format('scan DIED: %s:%s', $ERROR_INFO, $ERROR_POSITION * "\n") }
|
55
52
|
raise
|
56
53
|
end
|
54
|
+
# rubocop:enable Metrics/MethodLength
|
57
55
|
|
58
56
|
def self.ascendant_of?(base, other)
|
59
57
|
other.ascend do |ascendant|
|
@@ -88,8 +86,8 @@ module Listen
|
|
88
86
|
# https://github.com/jruby/jruby/issues/3840
|
89
87
|
exists = path.exist?
|
90
88
|
directory = path.directory?
|
91
|
-
|
92
|
-
|
89
|
+
exists && !directory and raise Errno::ENOTDIR, path.to_s
|
90
|
+
path.children
|
93
91
|
end
|
94
92
|
end
|
95
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
@@ -3,16 +3,15 @@
|
|
3
3
|
module Listen
|
4
4
|
module Event
|
5
5
|
class Config
|
6
|
-
attr_reader :listener
|
7
|
-
attr_reader :event_queue
|
8
|
-
attr_reader :min_delay_between_events
|
6
|
+
attr_reader :listener, :event_queue, :min_delay_between_events
|
9
7
|
|
10
8
|
def initialize(
|
11
9
|
listener,
|
12
10
|
event_queue,
|
13
11
|
queue_optimizer,
|
14
12
|
wait_for_delay,
|
15
|
-
&block
|
13
|
+
&block
|
14
|
+
)
|
16
15
|
|
17
16
|
@listener = listener
|
18
17
|
@event_queue = event_queue
|
@@ -26,11 +25,7 @@ module Listen
|
|
26
25
|
end
|
27
26
|
|
28
27
|
def call(*args)
|
29
|
-
@block
|
30
|
-
end
|
31
|
-
|
32
|
-
def timestamp
|
33
|
-
Time.now.to_f
|
28
|
+
@block&.call(*args)
|
34
29
|
end
|
35
30
|
|
36
31
|
def callable?
|
data/lib/listen/event/loop.rb
CHANGED
@@ -5,15 +5,15 @@ require 'thread'
|
|
5
5
|
require 'timeout'
|
6
6
|
require 'listen/event/processor'
|
7
7
|
require 'listen/thread'
|
8
|
+
require 'listen/error'
|
8
9
|
|
9
10
|
module Listen
|
10
11
|
module Event
|
11
12
|
class Loop
|
12
13
|
include Listen::FSM
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
end
|
15
|
+
Error = ::Listen::Error
|
16
|
+
NotStarted = ::Listen::Error::NotStarted # for backward compatibility
|
17
17
|
|
18
18
|
start_state :pre_start
|
19
19
|
state :pre_start
|
@@ -40,6 +40,7 @@ module Listen
|
|
40
40
|
|
41
41
|
MAX_STARTUP_SECONDS = 5.0
|
42
42
|
|
43
|
+
# @raises Error::NotStarted if background thread hasn't started in MAX_STARTUP_SECONDS
|
43
44
|
def start
|
44
45
|
# TODO: use a Fiber instead?
|
45
46
|
return unless state == :pre_start
|
@@ -52,7 +53,7 @@ module Listen
|
|
52
53
|
|
53
54
|
Listen.logger.debug("Waiting for processing to start...")
|
54
55
|
|
55
|
-
wait_for_state(:started, MAX_STARTUP_SECONDS) or
|
56
|
+
wait_for_state(:started, timeout: MAX_STARTUP_SECONDS) or
|
56
57
|
raise Error::NotStarted, "thread didn't start in #{MAX_STARTUP_SECONDS} seconds (in state: #{state.inspect})"
|
57
58
|
|
58
59
|
Listen.logger.debug('Processing started.')
|
@@ -64,12 +65,9 @@ module Listen
|
|
64
65
|
end
|
65
66
|
|
66
67
|
def stop
|
67
|
-
return if stopped?
|
68
68
|
transition! :stopped
|
69
69
|
|
70
|
-
|
71
|
-
@wait_thread.join
|
72
|
-
end
|
70
|
+
@wait_thread&.join
|
73
71
|
@wait_thread = nil
|
74
72
|
end
|
75
73
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'listen/monotonic_time'
|
4
|
+
|
3
5
|
module Listen
|
4
6
|
module Event
|
5
7
|
class Processor
|
@@ -33,7 +35,7 @@ module Listen
|
|
33
35
|
|
34
36
|
def _wait_until_events_calm_down
|
35
37
|
loop do
|
36
|
-
now =
|
38
|
+
now = MonotonicTime.now
|
37
39
|
|
38
40
|
# Assure there's at least latency between callbacks to allow
|
39
41
|
# for accumulating changes
|
@@ -51,10 +53,10 @@ module Listen
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def _check_stopped
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
if @listener.stopped?
|
57
|
+
_flush_wakeup_reasons
|
58
|
+
raise Stopped
|
59
|
+
end
|
58
60
|
end
|
59
61
|
|
60
62
|
def _sleep(seconds)
|
@@ -70,22 +72,22 @@ module Listen
|
|
70
72
|
end
|
71
73
|
|
72
74
|
def _remember_time_of_first_unprocessed_event
|
73
|
-
@
|
75
|
+
@_remember_time_of_first_unprocessed_event ||= MonotonicTime.now
|
74
76
|
end
|
75
77
|
|
76
78
|
def _reset_no_unprocessed_events
|
77
|
-
@
|
79
|
+
@_remember_time_of_first_unprocessed_event = nil
|
78
80
|
end
|
79
81
|
|
80
82
|
def _deadline
|
81
|
-
@
|
83
|
+
@_remember_time_of_first_unprocessed_event + @latency
|
82
84
|
end
|
83
85
|
|
84
86
|
# blocks until event is popped
|
85
87
|
# returns the event or `nil` when the event_queue is closed
|
86
88
|
def _wait_until_events
|
87
89
|
config.event_queue.pop.tap do |_event|
|
88
|
-
@
|
90
|
+
@_remember_time_of_first_unprocessed_event ||= MonotonicTime.now
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
@@ -96,10 +98,6 @@ module Listen
|
|
96
98
|
end
|
97
99
|
end
|
98
100
|
|
99
|
-
def _timestamp
|
100
|
-
config.timestamp
|
101
|
-
end
|
102
|
-
|
103
101
|
# for easier testing without sleep loop
|
104
102
|
def _process_changes(event)
|
105
103
|
_reset_no_unprocessed_events
|
@@ -113,13 +111,13 @@ module Listen
|
|
113
111
|
result = [hash[:modified], hash[:added], hash[:removed]]
|
114
112
|
return if result.all?(&:empty?)
|
115
113
|
|
116
|
-
block_start =
|
114
|
+
block_start = MonotonicTime.now
|
117
115
|
exception_note = " (exception)"
|
118
116
|
::Listen::Thread.rescue_and_log('_process_changes') do
|
119
117
|
config.call(*result)
|
120
118
|
exception_note = nil
|
121
119
|
end
|
122
|
-
Listen.logger.debug "Callback#{exception_note} took #{
|
120
|
+
Listen.logger.debug "Callback#{exception_note} took #{MonotonicTime.now - block_start} sec"
|
123
121
|
end
|
124
122
|
|
125
123
|
attr_reader :config
|
data/lib/listen/event/queue.rb
CHANGED
@@ -30,21 +30,21 @@ module Listen
|
|
30
30
|
fail "Invalid change: #{change.inspect}" unless change.is_a?(Symbol)
|
31
31
|
fail "Invalid path: #{path.inspect}" unless path.is_a?(String)
|
32
32
|
|
33
|
-
dir =
|
34
|
-
|
33
|
+
dir = if @config.relative?
|
34
|
+
_safe_relative_from_cwd(dir)
|
35
|
+
else
|
36
|
+
dir
|
37
|
+
end
|
38
|
+
@event_queue << [type, change, dir, path, options]
|
35
39
|
end
|
36
40
|
|
37
|
-
delegate empty?:
|
38
|
-
delegate pop:
|
39
|
-
delegate close:
|
41
|
+
delegate empty?: :@event_queue
|
42
|
+
delegate pop: :@event_queue
|
43
|
+
delegate close: :@event_queue
|
40
44
|
|
41
45
|
private
|
42
46
|
|
43
|
-
attr_reader :event_queue
|
44
|
-
attr_reader :config
|
45
|
-
|
46
47
|
def _safe_relative_from_cwd(dir)
|
47
|
-
return dir unless config.relative?
|
48
48
|
dir.relative_path_from(Pathname.pwd)
|
49
49
|
rescue ArgumentError
|
50
50
|
dir
|
data/lib/listen/file.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'digest
|
3
|
+
require 'digest'
|
4
4
|
|
5
5
|
module Listen
|
6
6
|
class File
|
7
|
+
# rubocop:disable Metrics/MethodLength
|
8
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
9
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
7
10
|
def self.change(record, rel_path)
|
8
11
|
path = Pathname.new(record.root) + rel_path
|
9
12
|
lstat = path.lstat
|
@@ -50,7 +53,7 @@ module Listen
|
|
50
53
|
# then at ???14.998, but the fstat time would be ???14.0 in
|
51
54
|
# both cases).
|
52
55
|
#
|
53
|
-
# If change
|
56
|
+
# If change happened at ???14.999997, the mtime is 14.0, so for
|
54
57
|
# an mtime=???14.0 we assume it could even be almost ???15.0
|
55
58
|
#
|
56
59
|
# So if Time.now.to_f is ???15.999998 and stat reports mtime
|
@@ -64,9 +67,11 @@ module Listen
|
|
64
67
|
#
|
65
68
|
return if data[:mtime].to_i + 2 <= Time.now.to_f
|
66
69
|
|
67
|
-
|
68
|
-
record.update_file(rel_path, data.merge(
|
69
|
-
|
70
|
+
sha = Digest::SHA256.file(path).digest
|
71
|
+
record.update_file(rel_path, data.merge(sha: sha))
|
72
|
+
if record_data[:sha] && sha != record_data[:sha]
|
73
|
+
:modified
|
74
|
+
end
|
70
75
|
rescue SystemCallError
|
71
76
|
record.unset_path(rel_path)
|
72
77
|
:removed
|
@@ -74,6 +79,9 @@ module Listen
|
|
74
79
|
Listen.logger.debug "lstat failed for: #{rel_path} (#{$ERROR_INFO})"
|
75
80
|
raise
|
76
81
|
end
|
82
|
+
# rubocop:enable Metrics/MethodLength
|
83
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
84
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
77
85
|
|
78
86
|
def self.inaccurate_mac_time?(stat)
|
79
87
|
# 'mac' means Modified/Accessed/Created
|
data/lib/listen/fsm.rb
CHANGED
@@ -53,6 +53,9 @@ module Listen
|
|
53
53
|
# if not already, waits for a state change (up to timeout seconds--`nil` means infinite)
|
54
54
|
# returns truthy iff the transition to one of the desired state has occurred
|
55
55
|
def wait_for_state(*wait_for_states, timeout: nil)
|
56
|
+
wait_for_states.each do |state|
|
57
|
+
state.is_a?(Symbol) or raise ArgumentError, "states must be symbols (got #{state.inspect})"
|
58
|
+
end
|
56
59
|
@mutex.synchronize do
|
57
60
|
if !wait_for_states.include?(@state)
|
58
61
|
@state_changed.wait(@mutex, timeout)
|
@@ -113,8 +116,8 @@ module Listen
|
|
113
116
|
@name = name
|
114
117
|
@block = block
|
115
118
|
@transitions = if transitions
|
116
|
-
|
117
|
-
|
119
|
+
Array(transitions).map(&:to_sym)
|
120
|
+
end
|
118
121
|
end
|
119
122
|
|
120
123
|
def call(obj)
|
@@ -25,9 +25,7 @@ module Listen
|
|
25
25
|
@relative
|
26
26
|
end
|
27
27
|
|
28
|
-
attr_reader :min_delay_between_events
|
29
|
-
|
30
|
-
attr_reader :silencer_rules
|
28
|
+
attr_reader :min_delay_between_events, :silencer_rules
|
31
29
|
|
32
30
|
def adapter_instance_options(klass)
|
33
31
|
valid_keys = klass.const_get('DEFAULTS').keys
|
@@ -35,7 +33,7 @@ module Listen
|
|
35
33
|
end
|
36
34
|
|
37
35
|
def adapter_select_options
|
38
|
-
valid_keys = %w
|
36
|
+
valid_keys = %w[force_polling polling_fallback_message].map(&:to_sym)
|
39
37
|
Hash[@options.select { |key, _| valid_keys.include?(key) }]
|
40
38
|
end
|
41
39
|
end
|
data/lib/listen/listener.rb
CHANGED
@@ -33,6 +33,7 @@ module Listen
|
|
33
33
|
# @yieldparam [Array<String>] added the list of added files
|
34
34
|
# @yieldparam [Array<String>] removed the list of removed files
|
35
35
|
#
|
36
|
+
# rubocop:disable Metrics/MethodLength
|
36
37
|
def initialize(*dirs, &block)
|
37
38
|
options = dirs.last.is_a?(Hash) ? dirs.pop : {}
|
38
39
|
|
@@ -60,6 +61,7 @@ module Listen
|
|
60
61
|
|
61
62
|
initialize_fsm
|
62
63
|
end
|
64
|
+
# rubocop:enable Metrics/MethodLength
|
63
65
|
|
64
66
|
start_state :initializing
|
65
67
|
|
data/lib/listen/logger.rb
CHANGED
@@ -6,30 +6,60 @@ module Listen
|
|
6
6
|
# Listen.logger will always be present.
|
7
7
|
# If you don't want logging, set Listen.logger = ::Logger.new('/dev/null', level: ::Logger::UNKNOWN)
|
8
8
|
|
9
|
+
@adapter_warn_behavior = :warn
|
10
|
+
|
9
11
|
class << self
|
10
12
|
attr_writer :logger
|
13
|
+
attr_accessor :adapter_warn_behavior
|
11
14
|
|
12
15
|
def logger
|
13
16
|
@logger ||= default_logger
|
14
17
|
end
|
15
18
|
|
19
|
+
def adapter_warn(message)
|
20
|
+
case ENV['LISTEN_GEM_ADAPTER_WARN_BEHAVIOR']&.to_sym || adapter_warn_behavior_callback(message)
|
21
|
+
when :log
|
22
|
+
logger.warn(message)
|
23
|
+
when :silent, nil, false
|
24
|
+
# do nothing
|
25
|
+
else # :warn
|
26
|
+
warn(message)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
16
30
|
private
|
17
31
|
|
18
32
|
def default_logger
|
19
|
-
level =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
33
|
+
level =
|
34
|
+
case ENV['LISTEN_GEM_DEBUGGING'].to_s
|
35
|
+
when /debug|2/i
|
36
|
+
::Logger::DEBUG
|
37
|
+
when /info|true|yes|1/i
|
38
|
+
::Logger::INFO
|
39
|
+
when /warn/i
|
40
|
+
::Logger::WARN
|
41
|
+
when /fatal/i
|
42
|
+
::Logger::FATAL
|
43
|
+
else
|
44
|
+
::Logger::ERROR
|
45
|
+
end
|
31
46
|
|
32
47
|
::Logger.new(STDERR, level: level)
|
33
48
|
end
|
49
|
+
|
50
|
+
def adapter_warn_behavior_callback(message)
|
51
|
+
if adapter_warn_behavior.respond_to?(:call)
|
52
|
+
case behavior = adapter_warn_behavior.call(message)
|
53
|
+
when Symbol
|
54
|
+
behavior
|
55
|
+
when false, nil
|
56
|
+
:silent
|
57
|
+
else
|
58
|
+
:warn
|
59
|
+
end
|
60
|
+
else
|
61
|
+
adapter_warn_behavior
|
62
|
+
end
|
63
|
+
end
|
34
64
|
end
|
35
65
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Listen
|
4
|
+
module MonotonicTime
|
5
|
+
class << self
|
6
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
7
|
+
|
8
|
+
def now
|
9
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
10
|
+
end
|
11
|
+
|
12
|
+
elsif defined?(Process::CLOCK_MONOTONIC_RAW)
|
13
|
+
|
14
|
+
def now
|
15
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
|
16
|
+
end
|
17
|
+
|
18
|
+
else
|
19
|
+
|
20
|
+
def now
|
21
|
+
Time.now.to_f
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/listen/options.rb
CHANGED
@@ -5,21 +5,22 @@ module Listen
|
|
5
5
|
def initialize(opts, defaults)
|
6
6
|
@options = {}
|
7
7
|
given_options = opts.dup
|
8
|
-
defaults.
|
8
|
+
defaults.each_key do |key|
|
9
9
|
@options[key] = given_options.delete(key) || defaults[key]
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
given_options.empty? or raise ArgumentError, "Unknown options: #{given_options.inspect}"
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
# rubocop:disable Lint/MissingSuper
|
16
|
+
def respond_to_missing?(name, *_)
|
17
|
+
@options.has_key?(name)
|
17
18
|
end
|
18
19
|
|
19
20
|
def method_missing(name, *_)
|
20
|
-
|
21
|
-
|
22
|
-
fail NameError, msg
|
21
|
+
respond_to_missing?(name) or raise NameError, "Bad option: #{name.inspect} (valid:#{@options.keys.inspect})"
|
22
|
+
@options[name]
|
23
23
|
end
|
24
|
+
# rubocop:enable Lint/MissingSuper
|
24
25
|
end
|
25
26
|
end
|
@@ -62,7 +62,7 @@ module Listen
|
|
62
62
|
actions << :added if actions.delete(:moved_to)
|
63
63
|
actions << :removed if actions.delete(:moved_from)
|
64
64
|
|
65
|
-
modified = actions.
|
65
|
+
modified = actions.find { |x| x == :modified }
|
66
66
|
_calculate_add_remove_difference(actions, path, modified)
|
67
67
|
end
|
68
68
|
|
@@ -91,10 +91,8 @@ module Listen
|
|
91
91
|
def _reinterpret_related_changes(cookies)
|
92
92
|
table = { moved_to: :added, moved_from: :removed }
|
93
93
|
cookies.flat_map do |_, changes|
|
94
|
-
|
95
|
-
|
96
|
-
to_dir, to_file = data
|
97
|
-
[[:modified, to_dir, to_file]]
|
94
|
+
if (editor_modified = editor_modified?(changes))
|
95
|
+
[[:modified, *editor_modified]]
|
98
96
|
else
|
99
97
|
not_silenced = changes.reject do |type, _, _, path, _|
|
100
98
|
config.silenced?(Pathname(path), type)
|
@@ -106,7 +104,7 @@ module Listen
|
|
106
104
|
end
|
107
105
|
end
|
108
106
|
|
109
|
-
def
|
107
|
+
def editor_modified?(changes)
|
110
108
|
return unless changes.size == 2
|
111
109
|
|
112
110
|
from_type = from = nil
|
@@ -118,17 +116,14 @@ module Listen
|
|
118
116
|
from_type, _from_change, _, from, = data
|
119
117
|
when :moved_to
|
120
118
|
to_type, _to_change, to_dir, to, = data
|
121
|
-
else
|
122
|
-
return nil
|
123
119
|
end
|
124
120
|
end
|
125
121
|
|
126
|
-
return unless from && to
|
127
|
-
|
128
122
|
# Expect an ignored moved_from and non-ignored moved_to
|
129
123
|
# to qualify as an "editor modify"
|
130
|
-
|
131
|
-
|
124
|
+
if from && to && config.silenced?(Pathname(from), from_type) && !config.silenced?(Pathname(to), to_type)
|
125
|
+
[to_dir, to]
|
126
|
+
end
|
132
127
|
end
|
133
128
|
end
|
134
129
|
end
|