listen 3.3.0.pre.3 → 3.3.4

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: 1d6639e347ad2eafe447bbb0c00b50ceab5e3f7a6b3bdce169482c910765198d
4
- data.tar.gz: 617efbcc3815ea658bc886e02d9ee24a0dd142cc0273442d4323d55766c5d8ed
3
+ metadata.gz: 3502b7f13a7eb3801aa2ccfb12825f13ce550b6339316389a17ed6319c3c450d
4
+ data.tar.gz: 433b372d311ea535f67999d23ef947dd3e4358849a60467dcf41582387d04b0f
5
5
  SHA512:
6
- metadata.gz: 5e55c50925feb8a8909978344e3061cec23ae2ee9cdba2583928da45bb907eb17284e7fb94aad431c9b1ba141f6401f98f131c6b774fe49619ea7bf353c6f352
7
- data.tar.gz: 221ca770caf975a3bd686704e93888a852cba10e725f582c22076a42d0540b085f929faaf474b9d81690beb0c88fb647107e3bf296e738bd913d1c0f5097a442
6
+ metadata.gz: 9a522a4498c0369db11679d60a7702e348f41442c33aa25e292807b91aa42fd7a672ed2ce77f1993c30da16b1dcae2733ecf2ddd70bbbd9a1bd89c8192483199
7
+ data.tar.gz: e8f695e75d67aa62ad4f3bd5e7d237717b7f58f1e6d444099697c901b21d49ed144250eca0ef3987dd33c625de4e181b86e57f35fdc839ccdb0b6a3b3578bf75
data/README.md CHANGED
@@ -16,13 +16,13 @@ The `listen` gem listens to file modifications and notifies you about the change
16
16
  * You can watch multiple directories.
17
17
  * Regexp-patterns for ignoring paths for more accuracy and speed
18
18
  * Increased change detection accuracy on OS X HFS and VFAT volumes.
19
- * Tested on selected Ruby environments via [Travis CI](https://travis-ci.org/guard/listen). (See [.travis.yml](https:///github.com/guard/listen/master/.travis.yml) for supported/tested Ruby Versions),
19
+ * Continuous Integration: tested on selected Ruby environments via [Github Workflows](https:///github.com/guard/listen/master/.github/workflows).
20
20
 
21
21
  ## Issues / limitations
22
22
 
23
23
  * Limited support for symlinked directories ([#279](https://github.com/guard/listen/issues/279)):
24
24
  * Symlinks are always followed ([#25](https://github.com/guard/listen/issues/25)).
25
- * Symlinked directories pointing within a watched directory are not supported ([#273](https://github.com/guard/listen/pull/273)- see [Duplicate directory errors](https://github.com/guard/listen/wiki/Duplicate-directory-errors)).
25
+ * Symlinked directories pointing within a watched directory are not supported ([#273](https://github.com/guard/listen/pull/273).
26
26
  * No directory/adapter-specific configuration options.
27
27
  * Support for plugins planned for future.
28
28
  * TCP functionality was removed in `listen` [3.0.0](https://github.com/guard/listen/releases/tag/v3.0.0) ([#319](https://github.com/guard/listen/issues/319), [#218](https://github.com/guard/listen/issues/218)). There are plans to extract this feature to separate gems ([#258](https://github.com/guard/listen/issues/258)), until this is finished, you can use by locking the `listen` gem to version `'~> 2.10'`.
@@ -40,7 +40,7 @@ Pull requests or help is very welcome for these.
40
40
  The simplest way to install `listen` is to use [Bundler](http://bundler.io).
41
41
 
42
42
  ```ruby
43
- gem 'listen', '~> 3.3' # NOTE: for TCP functionality, use '~> 2.10' for now
43
+ gem 'listen'
44
44
  ```
45
45
 
46
46
  ## Complete Example
@@ -240,7 +240,7 @@ If you are on Windows, it's recommended to use the [`wdm`](https://github.com/Ma
240
240
  Please add the following to your Gemfile:
241
241
 
242
242
  ```ruby
243
- gem 'wdm', '>= 0.1.0' if Gem.win_platform?
243
+ gem 'wdm', '>= 0.1.0', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
244
244
  ```
245
245
 
246
246
  ### On \*BSD
@@ -259,7 +259,82 @@ end
259
259
 
260
260
  ### Getting the [polling fallback message](#options)?
261
261
 
262
- Please visit the [installation section of the Listen WIKI](https://github.com/guard/listen/wiki#installation) for more information and options for potential fixes.
262
+ If you see:
263
+ ```
264
+ Listen will be polling for changes.
265
+ ```
266
+
267
+ This means the Listen gem can’t find an optimized adapter. Typically this is caused by:
268
+
269
+ - You’re on Windows and WDM gem isn’t installed.
270
+ - You’re running the app without Bundler or RubyGems.
271
+ - Using Sass which includes an ancient (the “dinosaur” type of ancient) version of the Listen gem.
272
+
273
+ Possible solutions:
274
+
275
+ 1. Suppress the message by using the :force_polling option. Or, you could just ignore the message since it’s harmless.
276
+ 2. Windows users: Install the WDM gem.
277
+ 3. Upgrade Ruby (use RubyInstaller for Windows or RVM/rbenv for Mac) and RubyGems.
278
+ 3. Run your apps using Bundler.
279
+ 4. Sass users: Install the latest version of Listen and try again.
280
+
281
+ #### Simplified Bundler and Sass example
282
+ Create a Gemfile with these lines:
283
+ ```
284
+ source 'https://rubygems.org'
285
+ gem 'listen'
286
+ gem 'sass'
287
+ ```
288
+ Next, use Bundler to update gems:
289
+ ```
290
+ $ bundle update
291
+ $ bundle exec sass --watch # ... or whatever app is using Listen.
292
+ ```
293
+
294
+ ### Increasing the amount of inotify watchers
295
+
296
+ If you are running Debian, RedHat, or another similar Linux distribution, run the following in a terminal:
297
+ ```
298
+ $ sudo sh -c "echo fs.inotify.max_user_watches=524288 >> /etc/sysctl.conf"
299
+ $ sudo sysctl -p
300
+ ```
301
+ If you are running ArchLinux, search the `/etc/sysctl.d/` directory for config files with the setting:
302
+ ```
303
+ $ grep -H -s "fs.inotify.max_user_watches" /etc/sysctl.d/*
304
+ /etc/sysctl.d/40-max_user_watches.conf:fs.inotify.max_user_watches=100000
305
+ ```
306
+ Then change the setting in the file you found above to a higher value (see [here](https://www.archlinux.org/news/deprecation-of-etcsysctlconf/) for why):
307
+ ```
308
+ $ sudo sh -c "echo fs.inotify.max_user_watches=524288 > /etc/sysctl.d/40-max-user-watches.conf"
309
+ $ sudo sysctl --system
310
+ ```
311
+
312
+ #### The technical details
313
+ Listen uses `inotify` by default on Linux to monitor directories for changes.
314
+ It's not uncommon to encounter a system limit on the number of files you can monitor.
315
+ For example, Ubuntu Lucid's (64bit) `inotify` limit is set to 8192.
316
+
317
+ You can get your current inotify file watch limit by executing:
318
+ ```
319
+ $ cat /proc/sys/fs/inotify/max_user_watches
320
+ ```
321
+ When this limit is not enough to monitor all files inside a directory, the limit must be increased for Listen to work properly.
322
+
323
+ You can set a new limit temporarily with:
324
+ ```
325
+ $ sudo sysctl fs.inotify.max_user_watches=524288
326
+ $ sudo sysctl -p
327
+ ```
328
+ If you like to make your limit permanent, use:
329
+ ```
330
+ $ sudo sh -c "echo fs.inotify.max_user_watches=524288 >> /etc/sysctl.conf"
331
+ $ sudo sysctl -p
332
+ ```
333
+ You may also need to pay attention to the values of `max_queued_events` and `max_user_instances` if Listen keeps on complaining.
334
+
335
+ #### More info
336
+ Man page for [inotify(7)](https://linux.die.net/man/7/inotify).
337
+ Blog post: [limit of inotify](https://blog.sorah.jp/2012/01/24/inotify-limitation).
263
338
 
264
339
  ### Issues and Troubleshooting
265
340
 
@@ -267,7 +342,35 @@ If the gem doesn't work as expected, start by setting `LISTEN_GEM_DEBUGGING=debu
267
342
 
268
343
  *NOTE: without providing the output after setting the `LISTEN_GEM_DEBUGGING=debug` environment variable, it is usually impossible to guess why `listen` is not working as expected.*
269
344
 
270
- See [TROUBLESHOOTING](https://github.com/guard/listen/wiki/Troubleshooting)
345
+ #### 3 steps before you start diagnosing problems
346
+ These 3 steps will:
347
+
348
+ - help quickly troubleshoot obscure problems (trust me, most of them are obscure)
349
+ - help quickly identify the area of the problem (a full list is below)
350
+ - help you get familiar with listen's diagnostic mode (it really comes in handy, trust me)
351
+ - help you create relevant output before you submit an issue (so we can respond with answers instead of tons of questions)
352
+
353
+ Step 1 - The most important option in Listen
354
+ For effective troubleshooting set the `LISTEN_GEM_DEBUGGING=info` variable before starting `listen`.
355
+
356
+ Step 2 - Verify polling works
357
+ Polling has to work ... or something is really wrong (and we need to know that before anything else).
358
+
359
+ (see force_polling option).
360
+
361
+ After starting `listen`, you should see something like:
362
+ ```
363
+ INFO -- : Record.build(): 0.06773114204406738 seconds
364
+ ```
365
+ Step 3 - Trigger some changes directly without using editors or apps
366
+ Make changes e.g. touch foo or echo "a" >> foo (for troubleshooting, avoid using an editor which could generate too many misleading events).
367
+
368
+ You should see something like:
369
+ ```
370
+ INFO -- : listen: raw changes: [[:added, "/home/me/foo"]]
371
+ INFO -- : listen: final changes: {:modified=>[], :added=>["/home/me/foo"], :removed=>[]}
372
+ ```
373
+ "raw changes" contains changes collected during the :wait_for_delay and :latency intervals, while "final changes" is what listen decided are relevant changes (for better editor support).
271
374
 
272
375
  ## Performance
273
376
 
@@ -289,7 +392,11 @@ Also, if the directories you're watching contain many files, make sure you're:
289
392
 
290
393
  When in doubt, `LISTEN_GEM_DEBUGGING=debug` can help discover the actual events and time they happened.
291
394
 
292
- See also [Tips and Techniques](https://github.com/guard/listen/wiki/Tips-and-Techniques).
395
+ ## Tips and Techniques
396
+ - Watch only directories you're interested in.
397
+ - Set your editor to save quickly (e.g. without backup files, without atomic-save)
398
+ - Tweak the `:latency` and `:wait_for_delay` options until you get good results (see [options](#options)).
399
+ - Add `:ignore` rules to silence all events you don't care about (reduces a lot of noise, especially if you use it on directories)
293
400
 
294
401
  ## Development
295
402
 
data/bin/listen CHANGED
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'listen'
4
5
  require 'listen/cli'
5
6
 
6
- unless defined?(JRUBY_VERSION)
7
- if Signal.list.keys.include?('INT')
8
- Signal.trap('INT') { Thread.new { Listen.stop } }
9
- end
7
+ if !defined?(JRUBY_VERSION) && Signal.list.keys.include?('INT')
8
+ Signal.trap('INT') { Thread.new { Listen.stop } }
10
9
  end
11
10
 
12
11
  Listen::CLI.start
@@ -11,7 +11,7 @@ module Listen
11
11
  module Adapter
12
12
  OPTIMIZED_ADAPTERS = [Darwin, Linux, BSD, Windows].freeze
13
13
  POLLING_FALLBACK_MESSAGE = 'Listen will be polling for changes.'\
14
- 'Learn more at https://github.com/guard/listen#listen-adapters.'.freeze
14
+ 'Learn more at https://github.com/guard/listen#listen-adapters.'
15
15
 
16
16
  class << self
17
17
  def select(options = {})
@@ -24,14 +24,14 @@ module Listen
24
24
  Polling
25
25
  rescue
26
26
  Listen.logger.warn format('Adapter: failed: %s:%s', $ERROR_POSITION.inspect,
27
- $ERROR_POSITION * "\n")
27
+ $ERROR_POSITION * "\n")
28
28
  raise
29
29
  end
30
30
 
31
31
  private
32
32
 
33
33
  def _usable_adapter_class
34
- OPTIMIZED_ADAPTERS.detect(&:usable?)
34
+ OPTIMIZED_ADAPTERS.find(&:usable?)
35
35
  end
36
36
 
37
37
  def _warn_polling_fallback(options)
@@ -8,13 +8,11 @@ require 'listen/thread'
8
8
  module Listen
9
9
  module Adapter
10
10
  class Base
11
- attr_reader :options
11
+ attr_reader :options, :config
12
12
 
13
13
  # TODO: only used by tests
14
14
  DEFAULTS = {}.freeze
15
15
 
16
- attr_reader :config
17
-
18
16
  def initialize(config)
19
17
  @started = false
20
18
  @config = config
@@ -31,6 +29,7 @@ module Listen
31
29
  end
32
30
 
33
31
  # TODO: it's a separate method as a temporary workaround for tests
32
+ # rubocop:disable Metrics/MethodLength
34
33
  def configure
35
34
  if @configured
36
35
  Listen.logger.warn('Adapter already configured!')
@@ -57,6 +56,7 @@ module Listen
57
56
  @snapshots[dir] = snapshot
58
57
  end
59
58
  end
59
+ # rubocop:enable Metrics/MethodLength
60
60
 
61
61
  def started?
62
62
  @started
@@ -73,7 +73,7 @@ module Listen
73
73
  @started = true
74
74
 
75
75
  @run_thread = Listen::Thread.new("run_thread") do
76
- @snapshots.values.each do |snapshot|
76
+ @snapshots.each_value do |snapshot|
77
77
  _timed('Record.build()') { snapshot.record.build }
78
78
  end
79
79
  _run
@@ -7,7 +7,7 @@
7
7
  module Listen
8
8
  module Adapter
9
9
  class BSD < Base
10
- OS_REGEXP = /bsd|dragonfly/i
10
+ OS_REGEXP = /bsd|dragonfly/i.freeze
11
11
 
12
12
  DEFAULTS = {
13
13
  events: [
@@ -73,8 +73,7 @@ module Listen
73
73
  def _change(event_flags)
74
74
  { modified: [:attrib, :extend],
75
75
  added: [:write],
76
- removed: [:rename, :delete]
77
- }.each do |change, flags|
76
+ removed: [:rename, :delete] }.each do |change, flags|
78
77
  return change unless (flags & event_flags).empty?
79
78
  end
80
79
  nil
@@ -87,7 +86,7 @@ module Listen
87
86
  def _watch_for_new_file(event)
88
87
  queue = event.watcher.queue
89
88
  _find(_event_path(event).to_s) do |file_path|
90
- unless queue.watchers.detect { |_, v| v.path == file_path.to_s }
89
+ unless queue.watchers.find { |_, v| v.path == file_path.to_s }
91
90
  _watch_file(file_path, queue)
92
91
  end
93
92
  end
@@ -5,10 +5,7 @@ require 'pathname'
5
5
  module Listen
6
6
  module Adapter
7
7
  class Config
8
- attr_reader :directories
9
- attr_reader :silencer
10
- attr_reader :queue
11
- attr_reader :adapter_options
8
+ attr_reader :directories, :silencer, :queue, :adapter_options
12
9
 
13
10
  def initialize(directories, queue, silencer, adapter_options)
14
11
  # Default to current directory if no directories are supplied
@@ -7,7 +7,7 @@ module Listen
7
7
  # Adapter implementation for Mac OS X `FSEvents`.
8
8
  #
9
9
  class Darwin < Base
10
- OS_REGEXP = /darwin(?<major_version>(1|2)\d+)/i
10
+ OS_REGEXP = /darwin(?<major_version>(1|2)\d+)/i.freeze
11
11
 
12
12
  # The default delay between checking for changes.
13
13
  DEFAULTS = { latency: 0.1 }.freeze
@@ -51,7 +51,7 @@ module Listen
51
51
 
52
52
  def _process_changes(dirs)
53
53
  dirs.each do |dir|
54
- dir = Pathname.new(dir.sub(%r{\/$}, ''))
54
+ dir = Pathname.new(dir.sub(%r{/$}, ''))
55
55
 
56
56
  @callbacks.each do |watched_dir, callback|
57
57
  if watched_dir.eql?(dir) || Listen::Directory.ascendant_of?(watched_dir, dir)
@@ -4,7 +4,7 @@ module Listen
4
4
  module Adapter
5
5
  # @see https://github.com/nex3/rb-inotify
6
6
  class Linux < Base
7
- OS_REGEXP = /linux/i
7
+ OS_REGEXP = /linux/i.freeze
8
8
 
9
9
  DEFAULTS = {
10
10
  events: [
@@ -22,7 +22,7 @@ module Listen
22
22
  private
23
23
 
24
24
  WIKI_URL = 'https://github.com/guard/listen'\
25
- '/wiki/Increasing-the-amount-of-inotify-watchers'.freeze
25
+ '/blob/master/README.md#increasing-the-amount-of-inotify-watchers'
26
26
 
27
27
  INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '')
28
28
  FATAL: Listen error: unable to monitor directories for changes.
@@ -41,6 +41,7 @@ module Listen
41
41
  @worker.run
42
42
  end
43
43
 
44
+ # rubocop:disable Metrics/MethodLength
44
45
  def _process_event(dir, event)
45
46
  # NOTE: avoid using event.absolute_name since new API
46
47
  # will need to have a custom recursion implemented
@@ -73,6 +74,7 @@ module Listen
73
74
 
74
75
  _queue_change(:file, dir, rel_path, params)
75
76
  end
77
+ # rubocop:enable Metrics/MethodLength
76
78
 
77
79
  def _skip_event?(event)
78
80
  # Event on root directory
@@ -8,7 +8,7 @@ module Listen
8
8
  # file IO than the other implementations.
9
9
  #
10
10
  class Polling < Base
11
- OS_REGEXP = // # match every OS
11
+ OS_REGEXP = //.freeze # match every OS
12
12
 
13
13
  DEFAULTS = { latency: 1.0, wait_for_delay: 0.05 }.freeze
14
14
 
@@ -5,7 +5,7 @@ module Listen
5
5
  # Adapter implementation for Windows `wdm`.
6
6
  #
7
7
  class Windows < Base
8
- OS_REGEXP = /mswin|mingw|cygwin/i
8
+ OS_REGEXP = /mswin|mingw|cygwin/i.freeze
9
9
 
10
10
  BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
11
11
  Please add the following to your Gemfile to avoid polling for changes:
@@ -18,7 +18,7 @@ module Listen
18
18
  true
19
19
  rescue LoadError
20
20
  Listen.logger.debug format('wdm - load failed: %s:%s', $ERROR_INFO,
21
- $ERROR_POSITION * "\n")
21
+ $ERROR_POSITION * "\n")
22
22
 
23
23
  Kernel.warn BUNDLER_DECLARE_GEM
24
24
  false
@@ -38,8 +38,7 @@ module Listen
38
38
  yield([:dir, change])
39
39
  end
40
40
 
41
- events = [:attributes, :last_write]
42
- @worker.watch_recursively(dir.to_s, *events) do |change|
41
+ @worker.watch_recursively(dir.to_s, :attributes, :last_write) do |change|
43
42
  yield([:attr, change])
44
43
  end
45
44
  end
@@ -48,6 +47,7 @@ module Listen
48
47
  @worker.run!
49
48
  end
50
49
 
50
+ # rubocop:disable Metrics/MethodLength
51
51
  def _process_event(dir, event)
52
52
  Listen.logger.debug "wdm - callback: #{event.inspect}"
53
53
 
@@ -67,10 +67,11 @@ module Listen
67
67
  _queue_change(:file, dir, rel_path, options)
68
68
  end
69
69
  when :dir
70
- if change.type == :removed
70
+ case change.type
71
+ when :removed
71
72
  # TODO: check if watched dir?
72
73
  _queue_change(:dir, dir, Pathname(rel_path).dirname.to_s, {})
73
- elsif change.type == :added
74
+ when :added
74
75
  _queue_change(:dir, dir, rel_path, {})
75
76
  # do nothing - changed directory means either:
76
77
  # - removed subdirs (handled above)
@@ -80,20 +81,15 @@ module Listen
80
81
  # so what's left?
81
82
  end
82
83
  end
83
- rescue
84
- details = event.inspect
85
- Listen.logger.error format('wdm - callback (%s): %s:%s', details, $ERROR_INFO,
86
- $ERROR_POSITION * "\n")
87
- raise
88
84
  end
85
+ # rubocop:enable Metrics/MethodLength
89
86
 
90
87
  def _change(type)
91
88
  { modified: [:modified, :attrib], # TODO: is attrib really passed?
92
89
  added: [:added, :renamed_new_file],
93
- removed: [:removed, :renamed_old_file] }.each do |change, types|
94
- return change if types.include?(type)
90
+ removed: [:removed, :renamed_old_file] }.find do |change, types|
91
+ types.include?(type) and break change
95
92
  end
96
- nil
97
93
  end
98
94
  end
99
95
  end
@@ -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
- else
59
- change = File.change(record, rel_path)
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
- private
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
@@ -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
 
@@ -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)
@@ -38,22 +39,19 @@ module Listen
38
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 do
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
- return path.children unless exists && !directory
92
- raise Errno::ENOTDIR, path.to_s
89
+ exists && !directory and raise Errno::ENOTDIR, path.to_s
90
+ path.children
93
91
  end
94
92
  end
95
93
  end
@@ -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,7 +25,7 @@ module Listen
26
25
  end
27
26
 
28
27
  def call(*args)
29
- @block.call(*args) if @block
28
+ @block&.call(*args)
30
29
  end
31
30
 
32
31
  def timestamp
@@ -64,12 +64,9 @@ module Listen
64
64
  end
65
65
 
66
66
  def stop
67
- return if stopped?
68
67
  transition! :stopped
69
68
 
70
- if @wait_thread.alive?
71
- @wait_thread.join
72
- end
69
+ @wait_thread&.join
73
70
  @wait_thread = nil
74
71
  end
75
72
 
@@ -51,10 +51,10 @@ module Listen
51
51
  end
52
52
 
53
53
  def _check_stopped
54
- return unless @listener.stopped?
55
-
56
- _flush_wakeup_reasons
57
- raise Stopped
54
+ if @listener.stopped?
55
+ _flush_wakeup_reasons
56
+ raise Stopped
57
+ end
58
58
  end
59
59
 
60
60
  def _sleep(seconds)
@@ -70,22 +70,22 @@ module Listen
70
70
  end
71
71
 
72
72
  def _remember_time_of_first_unprocessed_event
73
- @first_unprocessed_event_time ||= _timestamp
73
+ @_remember_time_of_first_unprocessed_event ||= _timestamp
74
74
  end
75
75
 
76
76
  def _reset_no_unprocessed_events
77
- @first_unprocessed_event_time = nil
77
+ @_remember_time_of_first_unprocessed_event = nil
78
78
  end
79
79
 
80
80
  def _deadline
81
- @first_unprocessed_event_time + @latency
81
+ @_remember_time_of_first_unprocessed_event + @latency
82
82
  end
83
83
 
84
84
  # blocks until event is popped
85
85
  # returns the event or `nil` when the event_queue is closed
86
86
  def _wait_until_events
87
87
  config.event_queue.pop.tap do |_event|
88
- @first_unprocessed_event_time ||= _timestamp
88
+ @_remember_time_of_first_unprocessed_event ||= _timestamp
89
89
  end
90
90
  end
91
91
 
@@ -114,8 +114,12 @@ module Listen
114
114
  return if result.all?(&:empty?)
115
115
 
116
116
  block_start = _timestamp
117
- config.call(*result)
118
- Listen.logger.debug "Callback took #{_timestamp - block_start} sec"
117
+ exception_note = " (exception)"
118
+ ::Listen::Thread.rescue_and_log('_process_changes') do
119
+ config.call(*result)
120
+ exception_note = nil
121
+ end
122
+ Listen.logger.debug "Callback#{exception_note} took #{_timestamp - block_start} sec"
119
123
  end
120
124
 
121
125
  attr_reader :config
@@ -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 = _safe_relative_from_cwd(dir)
34
- event_queue.public_send(:<<, [type, change, dir, path, options])
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?: :event_queue
38
- delegate pop: :event_queue
39
- delegate close: :event_queue
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
@@ -4,6 +4,9 @@ require 'digest/md5'
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
@@ -74,6 +77,9 @@ module Listen
74
77
  Listen.logger.debug "lstat failed for: #{rel_path} (#{$ERROR_INFO})"
75
78
  raise
76
79
  end
80
+ # rubocop:enable Metrics/MethodLength
81
+ # rubocop:enable Metrics/CyclomaticComplexity
82
+ # rubocop:enable Metrics/PerceivedComplexity
77
83
 
78
84
  def self.inaccurate_mac_time?(stat)
79
85
  # 'mac' means Modified/Accessed/Created
@@ -113,8 +113,8 @@ module Listen
113
113
  @name = name
114
114
  @block = block
115
115
  @transitions = if transitions
116
- Array(transitions).map(&:to_sym)
117
- end
116
+ Array(transitions).map(&:to_sym)
117
+ end
118
118
  end
119
119
 
120
120
  def call(obj)
@@ -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
 
@@ -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(force_polling polling_fallback_message).map(&:to_sym)
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
@@ -16,18 +16,19 @@ module Listen
16
16
  private
17
17
 
18
18
  def default_logger
19
- level = case ENV['LISTEN_GEM_DEBUGGING'].to_s
20
- when /debug|2/i
21
- ::Logger::DEBUG
22
- when /info|true|yes|1/i
23
- ::Logger::INFO
24
- when /warn/i
25
- ::Logger::WARN
26
- when /fatal/i
27
- ::Logger::FATAL
28
- else
29
- ::Logger::ERROR
30
- end
19
+ level =
20
+ case ENV['LISTEN_GEM_DEBUGGING'].to_s
21
+ when /debug|2/i
22
+ ::Logger::DEBUG
23
+ when /info|true|yes|1/i
24
+ ::Logger::INFO
25
+ when /warn/i
26
+ ::Logger::WARN
27
+ when /fatal/i
28
+ ::Logger::FATAL
29
+ else
30
+ ::Logger::ERROR
31
+ end
31
32
 
32
33
  ::Logger.new(STDERR, level: level)
33
34
  end
@@ -5,21 +5,22 @@ module Listen
5
5
  def initialize(opts, defaults)
6
6
  @options = {}
7
7
  given_options = opts.dup
8
- defaults.keys.each do |key|
8
+ defaults.each_key do |key|
9
9
  @options[key] = given_options.delete(key) || defaults[key]
10
10
  end
11
11
 
12
- return if given_options.empty?
12
+ given_options.empty? or raise ArgumentError, "Unknown options: #{given_options.inspect}"
13
+ end
13
14
 
14
- msg = "Unknown options: #{given_options.inspect}"
15
- Listen.logger.warn msg
16
- fail msg
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
- return @options[name] if @options.key?(name)
21
- msg = "Bad option: #{name.inspect} (valid:#{@options.keys.inspect})"
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.detect { |x| x == :modified }
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
- data = _detect_possible_editor_save(changes)
95
- if data
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 _detect_possible_editor_save(changes)
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
- return unless config.silenced?(Pathname(from), from_type)
131
- config.silenced?(Pathname(to), to_type) ? nil : [to_dir, to]
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
@@ -10,14 +10,16 @@ module Listen
10
10
  # TODO: deprecate
11
11
 
12
12
  attr_reader :root
13
+
13
14
  def initialize(directory)
14
15
  @tree = _auto_hash
15
16
  @root = directory.to_s
16
17
  end
17
18
 
18
19
  def add_dir(rel_path)
19
- return if [nil, '', '.'].include? rel_path
20
- @tree[rel_path] ||= {}
20
+ if ![nil, '', '.'].include?(rel_path)
21
+ @tree[rel_path] ||= {}
22
+ end
21
23
  end
22
24
 
23
25
  def update_file(rel_path, data)
@@ -33,41 +35,26 @@ module Listen
33
35
  def file_data(rel_path)
34
36
  dirname, basename = Pathname(rel_path).split.map(&:to_s)
35
37
  if [nil, '', '.'].include? dirname
36
- tree[basename] ||= {}
37
- tree[basename].dup
38
+ @tree[basename] ||= {}
39
+ @tree[basename].dup
38
40
  else
39
- tree[dirname] ||= {}
40
- tree[dirname][basename] ||= {}
41
- tree[dirname][basename].dup
41
+ @tree[dirname] ||= {}
42
+ @tree[dirname][basename] ||= {}
43
+ @tree[dirname][basename].dup
42
44
  end
43
45
  end
44
46
 
45
47
  def dir_entries(rel_path)
46
- subtree =
47
- if [nil, '', '.'].include? rel_path.to_s
48
- tree
49
- else
50
- _sub_tree(rel_path)
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 : {}
48
+ subtree = if ['', '.'].include? rel_path.to_s
49
+ @tree
50
+ else
51
+ @tree[rel_path.to_s] ||= _auto_hash
52
+ @tree[rel_path.to_s]
57
53
  end
58
- result
59
- end
60
-
61
- def _sub_tree(rel_path)
62
- tree.each_with_object({}) do |(path, meta), result|
63
- next unless path.start_with?(rel_path)
64
54
 
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
70
- end
55
+ subtree.transform_values do |values|
56
+ # only get data for file entries
57
+ values.key?(:mtime) ? values : {}
71
58
  end
72
59
  end
73
60
 
@@ -85,29 +72,27 @@ module Listen
85
72
  private
86
73
 
87
74
  def _auto_hash
88
- Hash.new { |h, k| h[k] = Hash.new }
75
+ Hash.new { |h, k| h[k] = {} }
89
76
  end
90
77
 
91
- attr_reader :tree
92
-
93
78
  def _fast_update_file(dirname, basename, data)
94
- if [nil, '', '.'].include? dirname
95
- tree[basename] = (tree[basename] || {}).merge(data)
79
+ if [nil, '', '.'].include?(dirname)
80
+ @tree[basename] = (@tree[basename] || {}).merge(data)
96
81
  else
97
- tree[dirname] ||= {}
98
- tree[dirname][basename] = (tree[dirname][basename] || {}).merge(data)
82
+ @tree[dirname] ||= {}
83
+ @tree[dirname][basename] = (@tree[dirname][basename] || {}).merge(data)
99
84
  end
100
85
  end
101
86
 
102
87
  def _fast_unset_path(dirname, basename)
103
88
  # this may need to be reworked to properly remove
104
89
  # entries from a tree, without adding non-existing dirs to the record
105
- if [nil, '', '.'].include? dirname
106
- return unless tree.key?(basename)
107
- tree.delete(basename)
108
- else
109
- return unless tree.key?(dirname)
110
- tree[dirname].delete(basename)
90
+ if [nil, '', '.'].include?(dirname)
91
+ if @tree.key?(basename)
92
+ @tree.delete(basename)
93
+ end
94
+ elsif @tree.key?(dirname)
95
+ @tree[dirname].delete(basename)
111
96
  end
112
97
  end
113
98
 
@@ -17,7 +17,7 @@ module Listen
17
17
 
18
18
  def children
19
19
  child_relative = _join
20
- (_entries(sys_path) - %w(. ..)).map do |name|
20
+ (_entries(sys_path) - %w[. ..]).map do |name|
21
21
  Entry.new(@root, child_relative, name)
22
22
  end
23
23
  end
@@ -6,9 +6,9 @@ module Listen
6
6
  # @private api
7
7
  class Record
8
8
  class SymlinkDetector
9
- WIKI = 'https://github.com/guard/listen/wiki/Duplicate-directory-errors'.freeze
9
+ WIKI = 'https://github.com/guard/listen/wiki/Duplicate-directory-errors'
10
10
 
11
- SYMLINK_LOOP_ERROR = <<-EOS.freeze
11
+ SYMLINK_LOOP_ERROR = <<-EOS
12
12
  ** ERROR: directory is already being watched! **
13
13
 
14
14
  Directory: %s
@@ -33,8 +33,8 @@ module Listen
33
33
  private
34
34
 
35
35
  def _fail(symlinked, real_path)
36
- STDERR.puts format(SYMLINK_LOOP_ERROR, symlinked, real_path)
37
- fail Error, 'Failed due to looped symlinks'
36
+ warn(format(SYMLINK_LOOP_ERROR, symlinked, real_path))
37
+ raise Error, 'Failed due to looped symlinks'
38
38
  end
39
39
  end
40
40
  end
@@ -14,7 +14,7 @@ module Listen
14
14
  | log
15
15
  | tmp
16
16
  |vendor/ruby
17
- )(/|$)}x
17
+ )(/|$)}x.freeze
18
18
 
19
19
  # The default list of files that get ignored.
20
20
  DEFAULT_IGNORED_EXTENSIONS = %r{(?:
@@ -55,7 +55,7 @@ module Listen
55
55
  | \.DS_Store
56
56
  | \.tmp
57
57
  | ~
58
- )$}x
58
+ )$}x.freeze
59
59
 
60
60
  attr_accessor :only_patterns, :ignore_patterns
61
61
 
@@ -75,16 +75,18 @@ module Listen
75
75
  def silenced?(relative_path, type)
76
76
  path = relative_path.to_s
77
77
 
78
- if only_patterns && type == :file
79
- return true unless only_patterns.any? { |pattern| path =~ pattern }
80
- end
81
-
82
- ignore_patterns.any? { |pattern| path =~ pattern }
78
+ _ignore?(path) || (only_patterns && type == :file && !_only?(path))
83
79
  end
84
80
 
85
81
  private
86
82
 
87
- attr_reader :options
83
+ def _ignore?(path)
84
+ ignore_patterns.any? { |pattern| path =~ pattern }
85
+ end
86
+
87
+ def _only?(path)
88
+ only_patterns.any? { |pattern| path =~ pattern }
89
+ end
88
90
 
89
91
  def _init_ignores(ignores, overrides)
90
92
  patterns = []
@@ -9,36 +9,43 @@ 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
- def new(name)
12
+ # rubocop:disable Style/MultilineBlockChain
13
+ def new(name, &block)
13
14
  thread_name = "listen-#{name}"
14
-
15
15
  caller_stack = caller
16
+
16
17
  ::Thread.new do
17
- begin
18
- yield
19
- rescue Exception => ex
20
- _log_exception(ex, thread_name, caller_stack)
21
- nil
22
- end
18
+ rescue_and_log(thread_name, caller_stack: caller_stack, &block)
23
19
  end.tap do |thread|
24
20
  thread.name = thread_name
25
21
  end
26
22
  end
23
+ # rubocop:enable Style/MultilineBlockChain
24
+
25
+ def rescue_and_log(method_name, *args, caller_stack: nil)
26
+ yield(*args)
27
+ rescue Exception => exception # rubocop:disable Lint/RescueException
28
+ _log_exception(exception, method_name, caller_stack: caller_stack)
29
+ end
27
30
 
28
31
  private
29
32
 
30
- def _log_exception(ex, thread_name, caller_stack)
31
- complete_backtrace = [*ex.backtrace, "--- Thread.new ---", *caller_stack]
32
- message = "Exception rescued in #{thread_name}:\n#{_exception_with_causes(ex)}\n#{complete_backtrace * "\n"}"
33
+ def _log_exception(exception, thread_name, caller_stack: nil)
34
+ complete_backtrace = if caller_stack
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"}"
33
40
  Listen.logger.error(message)
34
41
  end
35
42
 
36
- def _exception_with_causes(ex)
37
- result = +"#{ex.class}: #{ex}"
38
- if ex.cause
43
+ def _exception_with_causes(exception)
44
+ result = +"#{exception.class}: #{exception}"
45
+ if exception.cause
39
46
  result << "\n"
40
47
  result << "--- Caused by: ---\n"
41
- result << _exception_with_causes(ex.cause)
48
+ result << _exception_with_causes(exception.cause)
42
49
  end
43
50
  result
44
51
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Listen
4
- VERSION = '3.3.0.pre.3'
4
+ VERSION = '3.3.4'
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.3.0.pre.3
4
+ version: 3.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thibaud Guillaume-Gentil
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-04 00:00:00.000000000 Z
11
+ date: 2020-12-31 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
- changegem. Works everywhere!
54
+ changes. Works everywhere!
55
55
  email: thibaud@thibaud.gg
56
56
  executables:
57
57
  - listen
@@ -99,6 +99,12 @@ licenses:
99
99
  - MIT
100
100
  metadata:
101
101
  allowed_push_host: https://rubygems.org
102
+ bug_tracker_uri: https://github.com/guard/listen/issues
103
+ changelog_uri: https://github.com/guard/listen/releases
104
+ documentation_uri: https://www.rubydoc.info/gems/listen/3.3.4
105
+ homepage_uri: https://github.com/guard/listen
106
+ source_code_uri: https://github.com/guard/listen/tree/v3.3.4
107
+ wiki_uri: https://github.com/guard/listen/wiki
102
108
  post_install_message:
103
109
  rdoc_options: []
104
110
  require_paths:
@@ -110,9 +116,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
116
  version: 2.2.7
111
117
  required_rubygems_version: !ruby/object:Gem::Requirement
112
118
  requirements:
113
- - - ">"
119
+ - - ">="
114
120
  - !ruby/object:Gem::Version
115
- version: 1.3.1
121
+ version: '0'
116
122
  requirements: []
117
123
  rubygems_version: 3.0.1
118
124
  signing_key: