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
@@ -1,25 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'set'
|
4
|
+
require 'listen/error'
|
4
5
|
|
5
6
|
module Listen
|
6
7
|
# @private api
|
7
8
|
class Record
|
8
9
|
class SymlinkDetector
|
9
|
-
|
10
|
+
README_URL = 'https://github.com/guard/listen/blob/master/README.md'
|
10
11
|
|
11
|
-
SYMLINK_LOOP_ERROR = <<-EOS
|
12
|
+
SYMLINK_LOOP_ERROR = <<-EOS
|
12
13
|
** ERROR: directory is already being watched! **
|
13
14
|
|
14
15
|
Directory: %s
|
15
16
|
|
16
17
|
is already being watched through: %s
|
17
18
|
|
18
|
-
MORE INFO: #{
|
19
|
+
MORE INFO: #{README_URL}
|
19
20
|
EOS
|
20
21
|
|
21
|
-
|
22
|
-
end
|
22
|
+
Error = ::Listen::Error # for backward compatibility
|
23
23
|
|
24
24
|
def initialize
|
25
25
|
@real_dirs = Set.new
|
@@ -27,14 +27,20 @@ module Listen
|
|
27
27
|
|
28
28
|
def verify_unwatched!(entry)
|
29
29
|
real_path = entry.real_path
|
30
|
-
@real_dirs.add?(real_path)
|
30
|
+
@real_dirs.add?(real_path) or _fail(entry.sys_path, real_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Leaving this stub here since some warning work-arounds were referring to it.
|
34
|
+
# Deprecated. Will be removed in Listen v4.0.
|
35
|
+
def warn(message)
|
36
|
+
Listen.adapter_warn(message)
|
31
37
|
end
|
32
38
|
|
33
39
|
private
|
34
40
|
|
35
41
|
def _fail(symlinked, real_path)
|
36
|
-
|
37
|
-
|
42
|
+
warn(format(SYMLINK_LOOP_ERROR, symlinked, real_path))
|
43
|
+
raise ::Listen::Error::SymlinkLoop, 'Failed due to looped symlinks'
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
data/lib/listen/record.rb
CHANGED
@@ -10,14 +10,17 @@ module Listen
|
|
10
10
|
# TODO: deprecate
|
11
11
|
|
12
12
|
attr_reader :root
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
def initialize(directory, silencer)
|
15
|
+
reset_tree
|
15
16
|
@root = directory.to_s
|
17
|
+
@silencer = silencer
|
16
18
|
end
|
17
19
|
|
18
20
|
def add_dir(rel_path)
|
19
|
-
|
20
|
-
|
21
|
+
if !empty_dirname?(rel_path.to_s)
|
22
|
+
@tree[rel_path.to_s]
|
23
|
+
end
|
21
24
|
end
|
22
25
|
|
23
26
|
def update_file(rel_path, data)
|
@@ -32,47 +35,32 @@ module Listen
|
|
32
35
|
|
33
36
|
def file_data(rel_path)
|
34
37
|
dirname, basename = Pathname(rel_path).split.map(&:to_s)
|
35
|
-
if
|
36
|
-
tree[basename]
|
37
|
-
tree[basename].dup
|
38
|
+
if empty_dirname?(dirname)
|
39
|
+
@tree[basename].dup
|
38
40
|
else
|
39
|
-
tree[dirname] ||= {}
|
40
|
-
tree[dirname][basename]
|
41
|
-
tree[dirname][basename].dup
|
41
|
+
@tree[dirname][basename] ||= {}
|
42
|
+
@tree[dirname][basename].dup
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
46
|
def dir_entries(rel_path)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
result = {}
|
54
|
-
subtree.each do |key, values|
|
55
|
-
# only get data for file entries
|
56
|
-
result[key] = values.key?(:mtime) ? values : {}
|
47
|
+
rel_path_s = rel_path.to_s
|
48
|
+
subtree = if empty_dirname?(rel_path_s)
|
49
|
+
@tree
|
50
|
+
else
|
51
|
+
@tree[rel_path_s]
|
57
52
|
end
|
58
|
-
result
|
59
|
-
end
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
if path == rel_path
|
66
|
-
result.merge!(meta)
|
67
|
-
else
|
68
|
-
sub_path = path.sub(%r{\A#{rel_path}/?}, '')
|
69
|
-
result[sub_path] = meta
|
54
|
+
subtree.each_with_object({}) do |(key, values), result|
|
55
|
+
# only return data for file entries inside the dir (which will each be sub-hashes)
|
56
|
+
if values.is_a?(Hash)
|
57
|
+
result[key] = values.has_key?(:mtime) ? values : {}
|
70
58
|
end
|
71
59
|
end
|
72
60
|
end
|
73
61
|
|
74
62
|
def build
|
75
|
-
|
63
|
+
reset_tree
|
76
64
|
# TODO: test with a file name given
|
77
65
|
# TODO: test other permissions
|
78
66
|
# TODO: test with mixed encoding
|
@@ -84,35 +72,38 @@ module Listen
|
|
84
72
|
|
85
73
|
private
|
86
74
|
|
87
|
-
def
|
88
|
-
|
75
|
+
def empty_dirname?(dirname)
|
76
|
+
dirname == '.' || dirname == ''
|
89
77
|
end
|
90
78
|
|
91
|
-
|
79
|
+
def reset_tree
|
80
|
+
@tree = Hash.new { |h, k| h[k] = {} }
|
81
|
+
end
|
92
82
|
|
93
83
|
def _fast_update_file(dirname, basename, data)
|
94
|
-
if
|
95
|
-
tree[basename] =
|
84
|
+
if empty_dirname?(dirname.to_s)
|
85
|
+
@tree[basename] = @tree[basename].merge(data)
|
96
86
|
else
|
97
|
-
tree[dirname]
|
98
|
-
tree[dirname][basename] = (tree[dirname][basename] || {}).merge(data)
|
87
|
+
@tree[dirname][basename] = (@tree[dirname][basename] || {}).merge(data)
|
99
88
|
end
|
100
89
|
end
|
101
90
|
|
102
91
|
def _fast_unset_path(dirname, basename)
|
103
92
|
# this may need to be reworked to properly remove
|
104
93
|
# entries from a tree, without adding non-existing dirs to the record
|
105
|
-
if
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
tree[dirname].delete(basename)
|
94
|
+
if empty_dirname?(dirname.to_s)
|
95
|
+
if @tree.key?(basename)
|
96
|
+
@tree.delete(basename)
|
97
|
+
end
|
98
|
+
elsif @tree.key?(dirname)
|
99
|
+
@tree[dirname].delete(basename)
|
111
100
|
end
|
112
101
|
end
|
113
102
|
|
114
103
|
def _fast_build_dir(remaining, symlink_detector)
|
115
104
|
entry = remaining.pop
|
105
|
+
return if @silencer.silenced?(entry.record_dir_key, :dir)
|
106
|
+
|
116
107
|
children = entry.children # NOTE: children() implicitly tests if dir
|
117
108
|
symlink_detector.verify_unwatched!(entry)
|
118
109
|
children.each { |child| remaining << child }
|
data/lib/listen/silencer.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Listen
|
4
4
|
class Silencer
|
5
5
|
# The default list of directories that get ignored.
|
6
|
-
|
7
|
-
|
6
|
+
DEFAULT_IGNORED_FILES = %r{\A(?:
|
7
|
+
\.git
|
8
8
|
| \.svn
|
9
9
|
| \.hg
|
10
10
|
| \.rbx
|
@@ -13,8 +13,12 @@ module Listen
|
|
13
13
|
| vendor/bundle
|
14
14
|
| log
|
15
15
|
| tmp
|
16
|
-
|vendor/ruby
|
17
|
-
|
16
|
+
| vendor/ruby
|
17
|
+
|
18
|
+
# emacs temp files
|
19
|
+
| \#.+\#
|
20
|
+
| \.\#.+
|
21
|
+
)(/|\z)}x.freeze
|
18
22
|
|
19
23
|
# The default list of files that get ignored.
|
20
24
|
DEFAULT_IGNORED_EXTENSIONS = %r{(?:
|
@@ -35,7 +39,7 @@ module Listen
|
|
35
39
|
| ^4913
|
36
40
|
|
37
41
|
# Sed temporary files - but without actual words, like 'sedatives'
|
38
|
-
| (
|
42
|
+
| (?:\A
|
39
43
|
sed
|
40
44
|
|
41
45
|
(?:
|
@@ -55,41 +59,41 @@ module Listen
|
|
55
59
|
| \.DS_Store
|
56
60
|
| \.tmp
|
57
61
|
| ~
|
58
|
-
)
|
62
|
+
)\z}x.freeze
|
59
63
|
|
64
|
+
# TODO: deprecate these mutators; use attr_reader instead
|
60
65
|
attr_accessor :only_patterns, :ignore_patterns
|
61
66
|
|
62
|
-
def initialize
|
63
|
-
configure(
|
67
|
+
def initialize(**options)
|
68
|
+
configure(options)
|
64
69
|
end
|
65
70
|
|
71
|
+
# TODO: deprecate this mutator
|
66
72
|
def configure(options)
|
67
73
|
@only_patterns = options[:only] ? Array(options[:only]) : nil
|
68
74
|
@ignore_patterns = _init_ignores(options[:ignore], options[:ignore!])
|
69
75
|
end
|
70
76
|
|
71
|
-
# Note: relative_path is temporarily expected to be a relative Pathname to
|
72
|
-
# make refactoring easier (ideally, it would take a string)
|
73
|
-
|
74
|
-
# TODO: switch type and path places - and verify
|
75
77
|
def silenced?(relative_path, type)
|
76
|
-
path = relative_path.to_s
|
78
|
+
path = relative_path.to_s # in case it is a Pathname
|
77
79
|
|
78
|
-
|
79
|
-
return true unless only_patterns.any? { |pattern| path =~ pattern }
|
80
|
-
end
|
81
|
-
|
82
|
-
ignore_patterns.any? { |pattern| path =~ pattern }
|
80
|
+
_ignore?(path) || (only_patterns && type == :file && !_only?(path))
|
83
81
|
end
|
84
82
|
|
85
83
|
private
|
86
84
|
|
87
|
-
|
85
|
+
def _ignore?(path)
|
86
|
+
ignore_patterns.any? { |pattern| path =~ pattern }
|
87
|
+
end
|
88
|
+
|
89
|
+
def _only?(path)
|
90
|
+
only_patterns.any? { |pattern| path =~ pattern }
|
91
|
+
end
|
88
92
|
|
89
93
|
def _init_ignores(ignores, overrides)
|
90
94
|
patterns = []
|
91
95
|
unless overrides
|
92
|
-
patterns <<
|
96
|
+
patterns << DEFAULT_IGNORED_FILES
|
93
97
|
patterns << DEFAULT_IGNORED_EXTENSIONS
|
94
98
|
end
|
95
99
|
|
data/lib/listen/thread.rb
CHANGED
@@ -9,6 +9,7 @@ module Listen
|
|
9
9
|
class << self
|
10
10
|
# Creates a new thread with the given name.
|
11
11
|
# Any exceptions raised by the thread will be logged with the thread name and complete backtrace.
|
12
|
+
# rubocop:disable Style/MultilineBlockChain
|
12
13
|
def new(name, &block)
|
13
14
|
thread_name = "listen-#{name}"
|
14
15
|
caller_stack = caller
|
@@ -19,31 +20,32 @@ module Listen
|
|
19
20
|
thread.name = thread_name
|
20
21
|
end
|
21
22
|
end
|
23
|
+
# rubocop:enable Style/MultilineBlockChain
|
22
24
|
|
23
25
|
def rescue_and_log(method_name, *args, caller_stack: nil)
|
24
26
|
yield(*args)
|
25
|
-
rescue
|
26
|
-
_log_exception(
|
27
|
+
rescue => exception
|
28
|
+
_log_exception(exception, method_name, caller_stack: caller_stack)
|
27
29
|
end
|
28
30
|
|
29
31
|
private
|
30
32
|
|
31
|
-
def _log_exception(
|
33
|
+
def _log_exception(exception, thread_name, caller_stack: nil)
|
32
34
|
complete_backtrace = if caller_stack
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
message = "Exception rescued in #{thread_name}:\n#{_exception_with_causes(
|
35
|
+
[*exception.backtrace, "--- Thread.new ---", *caller_stack]
|
36
|
+
else
|
37
|
+
exception.backtrace
|
38
|
+
end
|
39
|
+
message = "Exception rescued in #{thread_name}:\n#{_exception_with_causes(exception)}\n#{complete_backtrace * "\n"}"
|
38
40
|
Listen.logger.error(message)
|
39
41
|
end
|
40
42
|
|
41
|
-
def _exception_with_causes(
|
42
|
-
result = +"#{
|
43
|
-
if
|
43
|
+
def _exception_with_causes(exception)
|
44
|
+
result = +"#{exception.class}: #{exception}"
|
45
|
+
if exception.cause
|
44
46
|
result << "\n"
|
45
47
|
result << "--- Caused by: ---\n"
|
46
|
-
result << _exception_with_causes(
|
48
|
+
result << _exception_with_causes(exception.cause)
|
47
49
|
end
|
48
50
|
result
|
49
51
|
end
|
data/lib/listen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: listen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thibaud Guillaume-Gentil
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rb-fsevent
|
@@ -51,7 +51,7 @@ dependencies:
|
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: 0.9.10
|
53
53
|
description: The Listen gem listens to file modifications and notifies you about the
|
54
|
-
|
54
|
+
changes. Works everywhere!
|
55
55
|
email: thibaud@thibaud.gg
|
56
56
|
executables:
|
57
57
|
- listen
|
@@ -76,6 +76,7 @@ files:
|
|
76
76
|
- lib/listen/change.rb
|
77
77
|
- lib/listen/cli.rb
|
78
78
|
- lib/listen/directory.rb
|
79
|
+
- lib/listen/error.rb
|
79
80
|
- lib/listen/event/config.rb
|
80
81
|
- lib/listen/event/loop.rb
|
81
82
|
- lib/listen/event/processor.rb
|
@@ -85,6 +86,7 @@ files:
|
|
85
86
|
- lib/listen/listener.rb
|
86
87
|
- lib/listen/listener/config.rb
|
87
88
|
- lib/listen/logger.rb
|
89
|
+
- lib/listen/monotonic_time.rb
|
88
90
|
- lib/listen/options.rb
|
89
91
|
- lib/listen/queue_optimizer.rb
|
90
92
|
- lib/listen/record.rb
|
@@ -99,7 +101,12 @@ licenses:
|
|
99
101
|
- MIT
|
100
102
|
metadata:
|
101
103
|
allowed_push_host: https://rubygems.org
|
102
|
-
|
104
|
+
bug_tracker_uri: https://github.com/guard/listen/issues
|
105
|
+
changelog_uri: https://github.com/guard/listen/releases
|
106
|
+
documentation_uri: https://www.rubydoc.info/gems/listen/3.9.0
|
107
|
+
homepage_uri: https://github.com/guard/listen
|
108
|
+
source_code_uri: https://github.com/guard/listen/tree/v3.9.0
|
109
|
+
post_install_message:
|
103
110
|
rdoc_options: []
|
104
111
|
require_paths:
|
105
112
|
- lib
|
@@ -107,15 +114,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
114
|
requirements:
|
108
115
|
- - ">="
|
109
116
|
- !ruby/object:Gem::Version
|
110
|
-
version: 2.
|
117
|
+
version: 2.4.0
|
111
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
119
|
requirements:
|
113
120
|
- - ">="
|
114
121
|
- !ruby/object:Gem::Version
|
115
122
|
version: '0'
|
116
123
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
118
|
-
signing_key:
|
124
|
+
rubygems_version: 3.4.6
|
125
|
+
signing_key:
|
119
126
|
specification_version: 4
|
120
127
|
summary: Listen to file modifications
|
121
128
|
test_files: []
|