listen 3.6.0 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 656e49f1b6eca9a31fd9c09b916aa73ed6d3df255e4859c4b77ebdb7a0f4a6bf
4
- data.tar.gz: 98be9697f64242c869fa47211477f84f8b636402fa4e865989e1b54e020a8032
3
+ metadata.gz: b365e353c2a0dc2a64459faa2c30a9b472130637b5fb4f50977c07adb2c6fd0c
4
+ data.tar.gz: 82d72d4f26f25a7fd1e285fbebe66fffcee246999696313a4f19997ce2017ef5
5
5
  SHA512:
6
- metadata.gz: af8bfbe37ce9b791f78ebdee29c32781742f3e58e98eae92381c34169bdf389db38f82228b77a56ba13e73646dbb7d2dae9a275be0c5b75e300010adb4382129
7
- data.tar.gz: f97c76664f9aa893bd89bcf93484701a6bda473ab9b13e2101cacb4f450f5ca0d116362027368fe561a724fb0c55213bbc6944bd58b0ee4cb6812f588d00feeb
6
+ metadata.gz: f52228164dce447a048dea3e04268909004e17f0b40788495f58f9afc9a9a9f3a0207fd0acb541d0cffe0be78836a10f652acf6ff418cea9ea119708eaec0b83
7
+ data.tar.gz: ee91854777075dc80f4cbcff3e507f4ca162aabe37fa11709b145184ada246efd0401ba89a50de8008c2ca7345d0dd08d20bbad7c6d9272004ad3581e74abca5
data/README.md CHANGED
@@ -14,7 +14,7 @@ The `listen` gem listens to file modifications and notifies you about the change
14
14
  * You can watch multiple directories.
15
15
  * Regexp-patterns for ignoring paths for more accuracy and speed
16
16
  * Increased change detection accuracy on OS X HFS and VFAT volumes.
17
- * Continuous Integration: tested on selected Ruby environments via [Github Workflows](https:///github.com/guard/listen/master/.github/workflows).
17
+ * Continuous Integration: tested on selected Ruby environments via [Github Workflows](https://github.com/guard/listen/tree/master/.github/workflows).
18
18
 
19
19
  ## Issues / limitations
20
20
 
@@ -97,23 +97,23 @@ The callback receives **three** array parameters: `modified`, `added` and `remov
97
97
  Each of these three is always an array with 0 or more entries.
98
98
  Each array entry is an absolute path.
99
99
 
100
- ### Pause / unpause / stop
100
+ ### Pause / start / stop
101
101
 
102
- Listeners can also be easily paused/unpaused:
102
+ Listeners can also be easily paused and later un-paused with start:
103
103
 
104
104
  ``` ruby
105
105
  listener = Listen.to('dir/path/to/listen') { |modified, added, removed| puts 'handle changes here...' }
106
106
 
107
107
  listener.start
108
- listener.paused? # => false
108
+ listener.paused? # => false
109
109
  listener.processing? # => true
110
110
 
111
- listener.pause # stops processing changes (but keeps on collecting them)
112
- listener.paused? # => true
111
+ listener.pause # stops processing changes (but keeps on collecting them)
112
+ listener.paused? # => true
113
113
  listener.processing? # => false
114
114
 
115
- listener.unpause # resumes processing changes ("start" would do the same)
116
- listener.stop # stop both listening to changes and processing them
115
+ listener.start # resumes processing changes
116
+ listener.stop # stop both listening to changes and processing them
117
117
  ```
118
118
 
119
119
  Note: While paused, `listen` keeps on collecting changes in the background - to clear them, call `stop`.
@@ -122,7 +122,7 @@ listener.stop # stop both listening to changes and processing them
122
122
 
123
123
  ### Ignore / ignore!
124
124
 
125
- `Listen` ignores some directories and extensions by default (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer).
125
+ `Listen` ignores some directories and extensions by default (See DEFAULT_IGNORED_FILES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer).
126
126
  You can add ignoring patterns with the `ignore` option/method or overwrite default with `ignore!` option/method.
127
127
 
128
128
  ``` ruby
@@ -155,9 +155,9 @@ Note: `:only` regexp patterns are evaluated only against relative **file** paths
155
155
 
156
156
  All the following options can be set through the `Listen.to` after the directory path(s) params.
157
157
 
158
- ```ruby
158
+ ``` ruby
159
159
  ignore: [%r{/foo/bar}, /\.pid$/, /\.coffee$/] # Ignore a list of paths
160
- # default: See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer
160
+ # default: See DEFAULT_IGNORED_FILES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer
161
161
 
162
162
  ignore!: %r{/foo/bar} # Same as ignore options, but overwrite default ignored paths.
163
163
 
@@ -187,7 +187,7 @@ This is the primary method of debugging.
187
187
 
188
188
  ### Custom Logger
189
189
  You can call `Listen.logger =` to set a custom `listen` logger for the process. For example:
190
- ```
190
+ ``` ruby
191
191
  Listen.logger = Rails.logger
192
192
  ```
193
193
 
@@ -197,7 +197,7 @@ If no custom logger is set, a default `listen` logger which logs to to `STDERR`
197
197
  The default logger defaults to the `error` logging level (severity).
198
198
  You can override the logging level by setting the environment variable `LISTEN_GEM_DEBUGGING=<level>`.
199
199
  For `<level>`, all standard `::Logger` levels are supported, with any mix of upper-/lower-case:
200
- ```
200
+ ``` ruby
201
201
  export LISTEN_GEM_DEBUGGING=debug # or 2 [deprecated]
202
202
  export LISTEN_GEM_DEBUGGING=info # or 1 or true or yes [deprecated]
203
203
  export LISTEN_GEM_DEBUGGING=warn
@@ -210,9 +210,38 @@ Note: The alternate values `1`, `2`, `true` and `yes` shown above are deprecated
210
210
 
211
211
  ### Disabling Logging
212
212
  If you want to disable `listen` logging, set
213
- ```
213
+ ``` ruby
214
214
  Listen.logger = ::Logger.new('/dev/null')
215
215
  ```
216
+
217
+ ### Adapter Warnings
218
+ If listen is having trouble with the underlying adapter, it will display warnings with `Kernel#warn` by default,
219
+ which in turn writes to STDERR.
220
+ Sometimes this is not desirable, for example in an environment where STDERR is ignored.
221
+ For these reasons, the behavior can be configured using `Listen.adapter_warn_behavior =`:
222
+ ``` ruby
223
+ Listen.adapter_warn_behavior = :warn # default (true means the same)
224
+ Listen.adapter_warn_behavior = :log # send to logger.warn
225
+ Listen.adapter_warn_behavior = :silent # suppress all adapter warnings (nil or false mean the same)
226
+ ```
227
+ Also there are some cases where specific warnings are not helpful.
228
+ For example, if you are using the polling adapter--and expect to--you can suppress the warning about it
229
+ by providing a callable object like a lambda or proc that determines the behavior based on the `message`:
230
+ ``` ruby
231
+ Listen.adapter_warn_behavior = ->(message) do
232
+ case message
233
+ when /Listen will be polling for changes/
234
+ :silent
235
+ when /directory is already being watched/
236
+ :log
237
+ else
238
+ :warn
239
+ end
240
+ end
241
+ ```
242
+ In cases where the `Listen` gem is embedded inside another service--such as `guard`--the above configuration
243
+ can be set in the environment variable `LISTEN_GEM_ADAPTER_WARN_BEHAVIOR=warn|log|silent`.
244
+
216
245
  ## Listen Adapters
217
246
 
218
247
  The `Listen` gem has a set of adapters to notify it when there are changes.
@@ -51,7 +51,7 @@ module Listen
51
51
  # TODO: separate config per directory (some day maybe)
52
52
  change_config = Change::Config.new(config.queue, config.silencer)
53
53
  config.directories.each do |dir|
54
- record = Record.new(dir)
54
+ record = Record.new(dir, config.silencer)
55
55
  snapshot = Change.new(change_config, record)
56
56
  @snapshots[dir] = snapshot
57
57
  end
@@ -34,7 +34,7 @@ module Listen
34
34
  require 'find'
35
35
  true
36
36
  rescue LoadError
37
- Kernel.warn BUNDLER_DECLARE_GEM
37
+ Listen.adapter_warn(BUNDLER_DECLARE_GEM)
38
38
  false
39
39
  end
40
40
 
@@ -16,6 +16,12 @@ module Listen
16
16
  Pathname.new(directory.to_s).realpath
17
17
  end
18
18
 
19
+ @directories.each do |pathname|
20
+ unless pathname.directory?
21
+ fail ArgumentError, "must be a directory: #{pathname}"
22
+ end
23
+ end
24
+
19
25
  @silencer = silencer
20
26
  @queue = queue
21
27
  @adapter_options = adapter_options
@@ -30,7 +30,7 @@ module Listen
30
30
  require 'rb-fsevent'
31
31
  fsevent_version = Gem::Version.new(FSEvent::VERSION)
32
32
  return true if fsevent_version <= Gem::Version.new('0.9.4')
33
- Kernel.warn INCOMPATIBLE_GEM_VERSION
33
+ Listen.adapter_warn(INCOMPATIBLE_GEM_VERSION)
34
34
  false
35
35
  end
36
36
 
@@ -24,17 +24,14 @@ module Listen
24
24
  README_URL = 'https://github.com/guard/listen'\
25
25
  '/blob/master/README.md#increasing-the-amount-of-inotify-watchers'
26
26
 
27
- INOTIFY_LIMIT_MESSAGE = <<-EOS
28
- FATAL: Listen error: unable to monitor directories for changes.
29
- Visit #{README_URL} for info on how to fix this.
30
- EOS
31
-
32
27
  def _configure(directory, &callback)
33
28
  require 'rb-inotify'
34
29
  @worker ||= ::INotify::Notifier.new
35
30
  @worker.watch(directory.to_s, *options.events, &callback)
36
31
  rescue Errno::ENOSPC
37
- abort(INOTIFY_LIMIT_MESSAGE)
32
+ raise ::Listen::Error::INotifyMaxWatchesExceeded, <<~EOS
33
+ Unable to monitor directories for changes because iNotify max watches exceeded. See #{README_URL} .
34
+ EOS
38
35
  end
39
36
 
40
37
  def _run
@@ -20,7 +20,7 @@ module Listen
20
20
  Listen.logger.debug format('wdm - load failed: %s:%s', $ERROR_INFO,
21
21
  $ERROR_POSITION * "\n")
22
22
 
23
- Kernel.warn BUNDLER_DECLARE_GEM
23
+ Listen.adapter_warn(BUNDLER_DECLARE_GEM)
24
24
  false
25
25
  end
26
26
 
@@ -36,7 +36,7 @@ module Listen
36
36
 
37
37
  def _warn_polling_fallback(options)
38
38
  msg = options.fetch(:polling_fallback_message, POLLING_FALLBACK_MESSAGE)
39
- Kernel.warn "[Listen warning]:\n #{msg}" if msg
39
+ Listen.adapter_warn("[Listen warning]:\n #{msg}") if msg
40
40
  end
41
41
  end
42
42
  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: 'The directory to listen to'
23
+ banner: 'One or more directories to listen to'
24
24
 
25
25
  class_option :relative,
26
26
  type: :boolean,
@@ -55,7 +55,7 @@ module Listen
55
55
  end
56
56
  end
57
57
 
58
- listener = Listen.to(directory, relative: relative, &callback)
58
+ listener = Listen.to(*directory, relative: relative, &callback)
59
59
 
60
60
  listener.start
61
61
 
@@ -36,7 +36,7 @@ module Listen
36
36
  end
37
37
 
38
38
  # TODO: this is not tested properly
39
- previous = previous.reject { |entry, _| current.include? path + entry }
39
+ previous = previous.reject { |entry, _| current.include?(path + entry) }
40
40
 
41
41
  _async_changes(snapshot, Pathname.new(rel_path), previous, options)
42
42
  rescue Errno::ENOENT, Errno::EHOSTDOWN
data/lib/listen/error.rb CHANGED
@@ -6,5 +6,6 @@ module Listen
6
6
  class Error < RuntimeError
7
7
  class NotStarted < Error; end
8
8
  class SymlinkLoop < Error; end
9
+ class INotifyMaxWatchesExceeded < Error; end
9
10
  end
10
11
  end
data/lib/listen/logger.rb CHANGED
@@ -6,13 +6,27 @@ 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
@@ -32,5 +46,20 @@ module Listen
32
46
 
33
47
  ::Logger.new(STDERR, level: level)
34
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
35
64
  end
36
65
  end
@@ -30,6 +30,12 @@ module Listen
30
30
  @real_dirs.add?(real_path) or _fail(entry.sys_path, real_path)
31
31
  end
32
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)
37
+ end
38
+
33
39
  private
34
40
 
35
41
  def _fail(symlinked, real_path)
data/lib/listen/record.rb CHANGED
@@ -11,14 +11,15 @@ module Listen
11
11
 
12
12
  attr_reader :root
13
13
 
14
- def initialize(directory)
15
- @tree = _auto_hash
14
+ def initialize(directory, silencer)
15
+ reset_tree
16
16
  @root = directory.to_s
17
+ @silencer = silencer
17
18
  end
18
19
 
19
20
  def add_dir(rel_path)
20
- if ![nil, '', '.'].include?(rel_path)
21
- @tree[rel_path] ||= {}
21
+ if !empty_dirname?(rel_path.to_s)
22
+ @tree[rel_path.to_s]
22
23
  end
23
24
  end
24
25
 
@@ -34,32 +35,32 @@ module Listen
34
35
 
35
36
  def file_data(rel_path)
36
37
  dirname, basename = Pathname(rel_path).split.map(&:to_s)
37
- if [nil, '', '.'].include? dirname
38
- @tree[basename] ||= {}
38
+ if empty_dirname?(dirname)
39
39
  @tree[basename].dup
40
40
  else
41
- @tree[dirname] ||= {}
42
41
  @tree[dirname][basename] ||= {}
43
42
  @tree[dirname][basename].dup
44
43
  end
45
44
  end
46
45
 
47
46
  def dir_entries(rel_path)
48
- subtree = if ['', '.'].include? rel_path.to_s
47
+ rel_path_s = rel_path.to_s
48
+ subtree = if empty_dirname?(rel_path_s)
49
49
  @tree
50
50
  else
51
- @tree[rel_path.to_s] ||= _auto_hash
52
- @tree[rel_path.to_s]
51
+ @tree[rel_path_s]
53
52
  end
54
53
 
55
- subtree.transform_values do |values|
56
- # only get data for file entries
57
- values.key?(:mtime) ? values : {}
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 : {}
58
+ end
58
59
  end
59
60
  end
60
61
 
61
62
  def build
62
- @tree = _auto_hash
63
+ reset_tree
63
64
  # TODO: test with a file name given
64
65
  # TODO: test other permissions
65
66
  # TODO: test with mixed encoding
@@ -71,15 +72,18 @@ module Listen
71
72
 
72
73
  private
73
74
 
74
- def _auto_hash
75
- Hash.new { |h, k| h[k] = {} }
75
+ def empty_dirname?(dirname)
76
+ dirname == '.' || dirname == ''
77
+ end
78
+
79
+ def reset_tree
80
+ @tree = Hash.new { |h, k| h[k] = {} }
76
81
  end
77
82
 
78
83
  def _fast_update_file(dirname, basename, data)
79
- if [nil, '', '.'].include?(dirname)
80
- @tree[basename] = (@tree[basename] || {}).merge(data)
84
+ if empty_dirname?(dirname.to_s)
85
+ @tree[basename] = @tree[basename].merge(data)
81
86
  else
82
- @tree[dirname] ||= {}
83
87
  @tree[dirname][basename] = (@tree[dirname][basename] || {}).merge(data)
84
88
  end
85
89
  end
@@ -87,7 +91,7 @@ module Listen
87
91
  def _fast_unset_path(dirname, basename)
88
92
  # this may need to be reworked to properly remove
89
93
  # entries from a tree, without adding non-existing dirs to the record
90
- if [nil, '', '.'].include?(dirname)
94
+ if empty_dirname?(dirname.to_s)
91
95
  if @tree.key?(basename)
92
96
  @tree.delete(basename)
93
97
  end
@@ -98,6 +102,8 @@ module Listen
98
102
 
99
103
  def _fast_build_dir(remaining, symlink_detector)
100
104
  entry = remaining.pop
105
+ return if @silencer.silenced?(entry.record_dir_key, :dir)
106
+
101
107
  children = entry.children # NOTE: children() implicitly tests if dir
102
108
  symlink_detector.verify_unwatched!(entry)
103
109
  children.each { |child| remaining << child }
@@ -3,8 +3,8 @@
3
3
  module Listen
4
4
  class Silencer
5
5
  # The default list of directories that get ignored.
6
- DEFAULT_IGNORED_DIRECTORIES = %r{^(?:
7
- \.git
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
- )(/|$)}x.freeze
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,25 +59,23 @@ module Listen
55
59
  | \.DS_Store
56
60
  | \.tmp
57
61
  | ~
58
- )$}x.freeze
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
80
  _ignore?(path) || (only_patterns && type == :file && !_only?(path))
79
81
  end
@@ -91,7 +93,7 @@ module Listen
91
93
  def _init_ignores(ignores, overrides)
92
94
  patterns = []
93
95
  unless overrides
94
- patterns << DEFAULT_IGNORED_DIRECTORIES
96
+ patterns << DEFAULT_IGNORED_FILES
95
97
  patterns << DEFAULT_IGNORED_EXTENSIONS
96
98
  end
97
99
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Listen
4
- VERSION = '3.6.0'
4
+ VERSION = '3.9.0'
5
5
  end
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.6.0
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: 2021-07-20 00:00:00.000000000 Z
11
+ date: 2024-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rb-fsevent
@@ -103,10 +103,10 @@ metadata:
103
103
  allowed_push_host: https://rubygems.org
104
104
  bug_tracker_uri: https://github.com/guard/listen/issues
105
105
  changelog_uri: https://github.com/guard/listen/releases
106
- documentation_uri: https://www.rubydoc.info/gems/listen/3.6.0
106
+ documentation_uri: https://www.rubydoc.info/gems/listen/3.9.0
107
107
  homepage_uri: https://github.com/guard/listen
108
- source_code_uri: https://github.com/guard/listen/tree/v3.6.0
109
- post_install_message:
108
+ source_code_uri: https://github.com/guard/listen/tree/v3.9.0
109
+ post_install_message:
110
110
  rdoc_options: []
111
111
  require_paths:
112
112
  - lib
@@ -121,8 +121,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
123
  requirements: []
124
- rubygems_version: 3.0.1
125
- signing_key:
124
+ rubygems_version: 3.4.6
125
+ signing_key:
126
126
  specification_version: 4
127
127
  summary: Listen to file modifications
128
128
  test_files: []