listen 2.7.6 → 2.7.7

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -0
  3. data/.rspec +0 -0
  4. data/.rubocop.yml +0 -0
  5. data/.travis.yml +0 -0
  6. data/.yardopts +0 -0
  7. data/CHANGELOG.md +0 -0
  8. data/CONTRIBUTING.md +0 -0
  9. data/Gemfile +2 -0
  10. data/Guardfile +2 -0
  11. data/LICENSE.txt +0 -0
  12. data/README.md +0 -0
  13. data/Rakefile +0 -0
  14. data/lib/listen.rb +0 -0
  15. data/lib/listen/adapter.rb +0 -0
  16. data/lib/listen/adapter/base.rb +47 -21
  17. data/lib/listen/adapter/bsd.rb +31 -25
  18. data/lib/listen/adapter/darwin.rb +13 -12
  19. data/lib/listen/adapter/linux.rb +45 -36
  20. data/lib/listen/adapter/polling.rb +12 -7
  21. data/lib/listen/adapter/tcp.rb +9 -4
  22. data/lib/listen/adapter/windows.rb +46 -58
  23. data/lib/listen/change.rb +12 -8
  24. data/lib/listen/cli.rb +0 -0
  25. data/lib/listen/directory.rb +30 -22
  26. data/lib/listen/file.rb +9 -8
  27. data/lib/listen/listener.rb +35 -12
  28. data/lib/listen/options.rb +23 -0
  29. data/lib/listen/queue_optimizer.rb +23 -13
  30. data/lib/listen/record.rb +98 -21
  31. data/lib/listen/silencer.rb +21 -40
  32. data/lib/listen/tcp.rb +0 -0
  33. data/lib/listen/tcp/broadcaster.rb +0 -0
  34. data/lib/listen/tcp/message.rb +0 -0
  35. data/lib/listen/version.rb +1 -1
  36. data/listen.gemspec +0 -0
  37. data/spec/acceptance/listen_spec.rb +0 -0
  38. data/spec/acceptance/tcp_spec.rb +0 -0
  39. data/spec/lib/listen/adapter/base_spec.rb +17 -16
  40. data/spec/lib/listen/adapter/bsd_spec.rb +0 -0
  41. data/spec/lib/listen/adapter/darwin_spec.rb +11 -4
  42. data/spec/lib/listen/adapter/linux_spec.rb +20 -29
  43. data/spec/lib/listen/adapter/polling_spec.rb +15 -13
  44. data/spec/lib/listen/adapter/tcp_spec.rb +6 -3
  45. data/spec/lib/listen/adapter/windows_spec.rb +0 -0
  46. data/spec/lib/listen/adapter_spec.rb +0 -0
  47. data/spec/lib/listen/change_spec.rb +21 -27
  48. data/spec/lib/listen/directory_spec.rb +60 -42
  49. data/spec/lib/listen/file_spec.rb +16 -20
  50. data/spec/lib/listen/listener_spec.rb +136 -99
  51. data/spec/lib/listen/record_spec.rb +205 -62
  52. data/spec/lib/listen/silencer_spec.rb +44 -114
  53. data/spec/lib/listen/tcp/broadcaster_spec.rb +0 -0
  54. data/spec/lib/listen/tcp/listener_spec.rb +8 -5
  55. data/spec/lib/listen/tcp/message_spec.rb +0 -0
  56. data/spec/lib/listen_spec.rb +0 -0
  57. data/spec/spec_helper.rb +0 -0
  58. data/spec/support/acceptance_helper.rb +15 -4
  59. data/spec/support/fixtures_helper.rb +0 -0
  60. data/spec/support/platform_helper.rb +0 -0
  61. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 962fc43324f716a5abe1d8acd8ea5df7c87d3d28
4
- data.tar.gz: 84b801baff1f5026d64b15df0c9de5fceac572b7
3
+ metadata.gz: e6d5e368c0d42f2b677a88efcd2b983eb1551278
4
+ data.tar.gz: 656135661cb4e2c2bd82c9acdbf9d0779a1c707a
5
5
  SHA512:
6
- metadata.gz: d95876a7d2bd3a39a8130685e2dfa40745ee6ca947646aa303a42b0ab1c4d8bb92347adf2b7e817209840c0a973a813ee5b3d90aafe0db6541361f2fa0d6b5a3
7
- data.tar.gz: 3cf94f221c2c4debd84f14a4545ba6caecb45bf6c1d3097ab5482d4ce9e907858aa80d736cc66d54d74e9e100423aca970515ccc0debf89dfb5990f16d169752
6
+ metadata.gz: d3b07e9530ffa906cf9cc1413dba58b990dc5e720e7c42f4364ac84790e47c43d10bfa5aa5afcbe43e360e1f6c459a5f7ffb15f7dc20ef8467127c6d1c8c93de
7
+ data.tar.gz: c6e078178e2879ec66de6c54a52542aa973d9de372edda0c8e74356d7fd1377330f569ef8ae73bf61b4f09962fe341d53dec6201bfdd4261275163fcdfb1a035
data/.gitignore CHANGED
File without changes
data/.rspec CHANGED
File without changes
data/.rubocop.yml CHANGED
File without changes
data/.travis.yml CHANGED
File without changes
data/.yardopts CHANGED
File without changes
data/CHANGELOG.md CHANGED
File without changes
data/CONTRIBUTING.md CHANGED
File without changes
data/Gemfile CHANGED
@@ -38,6 +38,8 @@ group :tool do
38
38
  gem 'yard', require: false
39
39
  gem 'guard-rspec', require: false
40
40
  gem 'guard-rubocop'
41
+ gem 'pry-rescue'
42
+ gem 'pry-stack_explorer'
41
43
  end
42
44
 
43
45
  group :test do
data/Guardfile CHANGED
@@ -1,3 +1,5 @@
1
+ ignore(%r{spec/\.fixtures/})
2
+
1
3
  guard :rspec, cmd: 'bundle exec rspec', failed_mode: :keep do
2
4
  watch(%r{^spec/.+_spec\.rb$})
3
5
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
data/LICENSE.txt CHANGED
File without changes
data/README.md CHANGED
File without changes
data/Rakefile CHANGED
File without changes
data/lib/listen.rb CHANGED
File without changes
File without changes
@@ -1,19 +1,58 @@
1
+ require 'listen/options'
2
+
1
3
  module Listen
2
4
  module Adapter
3
5
  class Base
4
6
  include Celluloid
5
7
 
6
- attr_accessor :listener
8
+ attr_reader :options
9
+
10
+ # TODO: only used by tests
11
+ DEFAULTS = {}
12
+
13
+ def initialize(opts)
14
+ @configured = nil
15
+ options = opts.dup
16
+ @mq = options.delete(:mq)
17
+ @directories = options.delete(:directories)
18
+
19
+ Array(@directories).each do |dir|
20
+ next if dir.is_a?(Pathname)
21
+ fail ArgumentError, "not a Pathname: #{dir.inspect}"
22
+ end
23
+
24
+ # TODO: actually use this in every adapter
25
+ @recursion = options.delete(:recursion)
26
+ @recursion = true if @recursion.nil?
7
27
 
8
- def initialize(listener)
9
- @listener = listener
28
+ defaults = self.class.const_get('DEFAULTS')
29
+ @options = Listen::Options.new(options, defaults)
10
30
  rescue
11
31
  _log :error, "adapter config failed: #{$!}:#{$@.join("\n")}"
12
32
  raise
13
33
  end
14
34
 
35
+ # TODO: it's a separate method as a temporary workaround for tests
36
+ def configure
37
+ return if @configured
38
+ @configured = true
39
+
40
+ @callbacks ||= {}
41
+ @directories.each do |dir|
42
+ unless dir.is_a?(Pathname)
43
+ fail ArgumentError, "not a Pathname: #{dir.inspect}"
44
+ end
45
+
46
+ callback = @callbacks[dir] || lambda do |event|
47
+ _process_event(dir, event)
48
+ end
49
+ @callbacks[dir] = callback
50
+ _configure(dir, &callback)
51
+ end
52
+ end
53
+
15
54
  def start
16
- _configure
55
+ configure
17
56
  Thread.new do
18
57
  begin
19
58
  _run
@@ -34,23 +73,10 @@ module Listen
34
73
 
35
74
  private
36
75
 
37
- def _configure
38
- end
39
-
40
- def _directories
41
- listener.directories
42
- end
43
-
44
- def _notify_change(type, path, options = {})
45
- unless (worker = listener.async(:change_pool))
46
- _log :warn, 'Failed to allocate worker from change pool'
47
- return
48
- end
49
-
50
- worker.change(type, path, options)
51
- rescue RuntimeError
52
- _log :error, "_notify_change crashed: #{$!}:#{$@.join("\n")}"
53
- raise
76
+ def _queue_change(type, dir, rel_path, options)
77
+ # TODO: temporary workaround to remove dependency on Change through
78
+ # Celluloid in tests
79
+ @mq.send(:_queue_raw_change, type, dir, rel_path, options)
54
80
  end
55
81
 
56
82
  def _log(type, message)
@@ -7,7 +7,16 @@ module Listen
7
7
  class BSD < Base
8
8
  OS_REGEXP = /bsd|dragonfly/i
9
9
 
10
- EVENTS = [:delete, :write, :extend, :attrib, :rename] # :link, :revoke
10
+ DEFAULTS = {
11
+ events: [
12
+ :delete,
13
+ :write,
14
+ :extend,
15
+ :attrib,
16
+ :rename
17
+ # :link, :revoke
18
+ ]
19
+ }
11
20
 
12
21
  BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
13
22
  Please add the following to your Gemfile to avoid polling for changes:
@@ -47,36 +56,33 @@ module Listen
47
56
 
48
57
  private
49
58
 
50
- def _configure
51
- @worker = KQueue::Queue.new
52
- _directories.each do |path|
53
- # use Record to make a snapshot of dir, so we
54
- # can detect new files
55
- _find(path.to_s) { |file_path| _watch_file(file_path, @worker) }
56
- end
59
+ def _configure(directory, &_callback)
60
+ @worker ||= KQueue::Queue.new
61
+ # use Record to make a snapshot of dir, so we
62
+ # can detect new files
63
+ _find(directory.to_s) { |path| _watch_file(path, @worker) }
57
64
  end
58
65
 
59
66
  def _run
60
67
  @worker.run
61
68
  end
62
69
 
63
- def _worker_callback
64
- lambda do |event|
65
- path = _event_path(event)
66
- if path.directory?
67
- # Force dir content tracking to kick in, or we won't have
68
- # names of added files
69
- _notify_change(:dir, path, recursive: true)
70
- else
71
- _notify_change(:file, path, change: _change(event.flags))
72
- end
73
-
74
- # If it is a directory, and it has a write flag, it means a
75
- # file has been added so find out which and deal with it.
76
- # No need to check for removed files, kqueue will forget them
77
- # when the vfs does.
78
- _watch_for_new_file(event) if path.directory?
70
+ def _process_event(dir, event)
71
+ full_path = _event_path(event)
72
+ if full_path.directory?
73
+ # Force dir content tracking to kick in, or we won't have
74
+ # names of added files
75
+ _queue_change(:dir, dir, '.', recursive: true)
76
+ else
77
+ path = full_path.relative_path_from(dir)
78
+ _queue_change(:file, dir, path, change: _change(event.flags))
79
79
  end
80
+
81
+ # If it is a directory, and it has a write flag, it means a
82
+ # file has been added so find out which and deal with it.
83
+ # No need to check for removed files, kqueue will forget them
84
+ # when the vfs does.
85
+ _watch_for_new_file(event) if path.directory?
80
86
  end
81
87
 
82
88
  def _change(event_flags)
@@ -103,7 +109,7 @@ module Listen
103
109
  end
104
110
 
105
111
  def _watch_file(path, queue)
106
- queue.watch_file(path, *EVENTS, &_worker_callback)
112
+ queue.watch_file(path, *options.events, &_worker_callback)
107
113
  end
108
114
 
109
115
  # Quick rubocop workaround
@@ -6,28 +6,29 @@ module Listen
6
6
  OS_REGEXP = /darwin(1.+)?$/i
7
7
 
8
8
  # The default delay between checking for changes.
9
- DEFAULT_LATENCY = 0.1
9
+ DEFAULTS = { latency: 0.1 }
10
10
 
11
11
  private
12
12
 
13
- def _configure
13
+ def _configure(dir, &callback)
14
14
  require 'rb-fsevent'
15
- @worker = FSEvent.new
16
- @worker.watch(_directories.map(&:to_s), latency: _latency) do |changes|
17
- changes.each do |path|
18
- new_path = Pathname.new(path.sub(/\/$/, ''))
19
- _log :debug, "fsevent: #{new_path}"
20
- _notify_change(:dir, new_path)
21
- end
22
- end
15
+ @worker ||= FSEvent.new
16
+ opts = { latency: options.latency }
17
+ @worker.watch(dir.to_s, opts, &callback)
23
18
  end
24
19
 
25
20
  def _run
26
21
  @worker.run
27
22
  end
28
23
 
29
- def _latency
30
- listener.options[:latency] || DEFAULT_LATENCY
24
+ def _process_event(dir, event)
25
+ event.each do |path|
26
+ new_path = Pathname.new(path.sub(/\/$/, ''))
27
+ _log :debug, "fsevent: #{new_path}"
28
+ # TODO: does this preserve symlinks?
29
+ rel_path = new_path.relative_path_from(dir).to_s
30
+ _queue_change(:dir, dir, rel_path, recursive: true)
31
+ end
31
32
  end
32
33
  end
33
34
  end
@@ -1,31 +1,34 @@
1
1
  module Listen
2
2
  module Adapter
3
- # Listener implementation for Linux `inotify`.
4
3
  # @see https://github.com/nex3/rb-inotify
5
- #
6
4
  class Linux < Base
7
5
  OS_REGEXP = /linux/i
8
6
 
9
- EVENTS = [:recursive, :attrib, :create, :delete, :move, :close_write]
7
+ DEFAULTS = {
8
+ events: [
9
+ :recursive,
10
+ :attrib,
11
+ :create,
12
+ :delete,
13
+ :move,
14
+ :close_write
15
+ ]
16
+ }
17
+
18
+ private
10
19
 
11
20
  WIKI_URL = 'https://github.com/guard/listen'\
12
21
  '/wiki/Increasing-the-amount-of-inotify-watchers'
13
22
 
14
23
  INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '')
15
24
  FATAL: Listen error: unable to monitor directories for changes.
16
-
17
- Please head to #{WIKI_URL}
18
- for information on how to solve this issue.
25
+ Visit #{WIKI_URL} for info on how to fix this.
19
26
  EOS
20
27
 
21
- private
22
-
23
- def _configure
28
+ def _configure(directory, &callback)
24
29
  require 'rb-inotify'
25
- @worker = INotify::Notifier.new
26
- _directories.each do |path|
27
- @worker.watch(path.to_s, *EVENTS, &_callback)
28
- end
30
+ @worker ||= INotify::Notifier.new
31
+ @worker.watch(directory.to_s, *options.events, &callback)
29
32
  rescue Errno::ENOSPC
30
33
  # workaround - Celluloid catches abort and prints nothing
31
34
  STDERR.puts INOTIFY_LIMIT_MESSAGE
@@ -37,32 +40,38 @@ module Listen
37
40
  @worker.run
38
41
  end
39
42
 
40
- def _callback
41
- lambda do |event|
42
- # NOTE: avoid using event.absolute_name since new API
43
- # will need to have a custom recursion implemented
44
- # to properly match events to configured directories
45
- path = Pathname.new(event.watcher.path) + event.name
46
-
47
- _log :debug, "inotify: #{event.name} #{path} (#{event.flags.inspect})"
48
-
49
- if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
50
- if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
51
- _notify_change(:dir, path.dirname)
52
- else
53
- _notify_change(:dir, path)
54
- end
43
+ def _process_event(dir, event)
44
+ # NOTE: avoid using event.absolute_name since new API
45
+ # will need to have a custom recursion implemented
46
+ # to properly match events to configured directories
47
+ path = Pathname.new(event.watcher.path) + event.name
48
+ rel_path = path.relative_path_from(dir).to_s
49
+
50
+ _log :debug, "inotify: #{rel_path} (#{event.flags.inspect})"
51
+
52
+ if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
53
+ if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
54
+ _queue_change(:dir, dir, Pathname(rel_path).dirname, {})
55
55
  else
56
- next if _skip_event?(event)
57
- cookie_opts = event.cookie.zero? ? {} : { cookie: event.cookie }
58
- if _dir_event?(event)
59
- _notify_change(:dir, path, cookie_opts)
60
- else
61
- options = { change: _change(event.flags) }
62
- _notify_change(:file, path, options.merge(cookie_opts))
63
- end
56
+ _queue_change(:dir, dir, rel_path, {})
64
57
  end
58
+ return
65
59
  end
60
+
61
+ return if _skip_event?(event)
62
+
63
+ cookie_params = event.cookie.zero? ? {} : { cookie: event.cookie }
64
+
65
+ # Note: don't pass options to force rescanning the directory, so we can
66
+ # detect moving/deleting a whole tree
67
+ if _dir_event?(event)
68
+ _queue_change(:dir, dir, rel_path, cookie_params)
69
+ return
70
+ end
71
+
72
+ params = cookie_params.merge(change: _change(event.flags))
73
+
74
+ _queue_change(:file, dir, rel_path, params)
66
75
  end
67
76
 
68
77
  def _skip_event?(event)
@@ -6,26 +6,31 @@ module Listen
6
6
  # file IO than the other implementations.
7
7
  #
8
8
  class Polling < Base
9
- OS_REGEXP = // # match any
9
+ OS_REGEXP = // # match every OS
10
10
 
11
- DEFAULT_POLLING_LATENCY = 1.0
11
+ DEFAULTS = { latency: 1.0 }
12
12
 
13
13
  private
14
14
 
15
- def _latency
16
- listener.options[:latency] || DEFAULT_POLLING_LATENCY
15
+ def _configure(_, &callback)
16
+ @polling_callbacks ||= []
17
+ @polling_callbacks << callback
17
18
  end
18
19
 
19
20
  def _run
20
21
  loop do
21
22
  start = Time.now.to_f
22
- _directories.each do |path|
23
- _notify_change(:dir, path, recursive: true)
24
- nap_time = _latency - (Time.now.to_f - start)
23
+ @polling_callbacks.each do |callback|
24
+ callback.call(nil)
25
+ nap_time = options.latency - (Time.now.to_f - start)
25
26
  sleep(nap_time) if nap_time > 0
26
27
  end
27
28
  end
28
29
  end
30
+
31
+ def _process_event(dir, _)
32
+ _queue_change(:dir, dir, '.', recursive: true)
33
+ end
29
34
  end
30
35
  end
31
36
  end
@@ -9,15 +9,19 @@ module Listen
9
9
  OS_REGEXP = // # match any
10
10
 
11
11
  include Celluloid::IO
12
-
13
12
  finalizer :finalize
14
13
 
14
+ DEFAULTS = {
15
+ host: 'localhost',
16
+ port: '4000'
17
+ }
18
+
15
19
  attr_reader :buffer, :socket
16
20
 
17
21
  # Initializes and starts a Celluloid::IO-powered TCP-recipient
18
22
  def start
19
23
  attempts = 3
20
- @socket = TCPSocket.new(listener.host, listener.port)
24
+ @socket = TCPSocket.new(options.host, options.port)
21
25
  @buffer = ''
22
26
  async.run
23
27
  rescue Celluloid::Task::TerminatedError
@@ -66,8 +70,9 @@ module Listen
66
70
 
67
71
  # Handles incoming message by notifying of path changes
68
72
  def handle_message(message)
69
- type, modification, path, _ = message.object
70
- _notify_change(type.to_sym, path, change: modification.to_sym)
73
+ type, change, dir, path, _ = message.object
74
+ _log :debug, "TCP message: #{[type, change, dir, path].inspect}"
75
+ _queue_change(type.to_sym, Pathname(dir), path, change: change.to_sym)
71
76
  end
72
77
 
73
78
  def self.local_fs?