listen 3.1.4 → 3.3.0.pre.3

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.
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'listen/options'
2
4
  require 'listen/record'
3
5
  require 'listen/change'
6
+ require 'listen/thread'
4
7
 
5
8
  module Listen
6
9
  module Adapter
@@ -30,7 +33,7 @@ module Listen
30
33
  # TODO: it's a separate method as a temporary workaround for tests
31
34
  def configure
32
35
  if @configured
33
- _log(:warn, 'Adapter already configured!')
36
+ Listen.logger.warn('Adapter already configured!')
34
37
  return
35
38
  end
36
39
 
@@ -63,48 +66,39 @@ module Listen
63
66
  configure
64
67
 
65
68
  if started?
66
- _log(:warn, 'Adapter already started!')
69
+ Listen.logger.warn('Adapter already started!')
67
70
  return
68
71
  end
69
72
 
70
73
  @started = true
71
74
 
72
- calling_stack = caller.dup
73
- Listen::Internals::ThreadPool.add do
74
- begin
75
- @snapshots.values.each do |snapshot|
76
- _timed('Record.build()') { snapshot.record.build }
77
- end
78
- _run
79
- rescue
80
- msg = 'run() in thread failed: %s:\n'\
81
- ' %s\n\ncalled from:\n %s'
82
- _log_exception(msg, calling_stack)
83
- raise # for unit tests mostly
75
+ @run_thread = Listen::Thread.new("run_thread") do
76
+ @snapshots.values.each do |snapshot|
77
+ _timed('Record.build()') { snapshot.record.build }
84
78
  end
79
+ _run
85
80
  end
86
81
  end
87
82
 
88
83
  def stop
89
84
  _stop
90
- end
91
-
92
- def self.usable?
93
- const_get('OS_REGEXP') =~ RbConfig::CONFIG['target_os']
85
+ config.queue.close # this causes queue.pop to return `nil` to the front-end
94
86
  end
95
87
 
96
88
  private
97
89
 
98
90
  def _stop
91
+ @run_thread&.kill
92
+ @run_thread = nil
99
93
  end
100
94
 
101
95
  def _timed(title)
102
96
  start = Time.now.to_f
103
97
  yield
104
98
  diff = Time.now.to_f - start
105
- Listen::Logger.info format('%s: %.05f seconds', title, diff)
99
+ Listen.logger.info format('%s: %.05f seconds', title, diff)
106
100
  rescue
107
- Listen::Logger.warn "#{title} crashed: #{$ERROR_INFO.inspect}"
101
+ Listen.logger.warn "#{title} crashed: #{$ERROR_INFO.inspect}"
108
102
  raise
109
103
  end
110
104
 
@@ -114,10 +108,6 @@ module Listen
114
108
  @snapshots[dir].invalidate(type, rel_path, options)
115
109
  end
116
110
 
117
- def _log(*args, &block)
118
- self.class.send(:_log, *args, &block)
119
- end
120
-
121
111
  def _log_exception(msg, caller_stack)
122
112
  formatted = format(
123
113
  msg,
@@ -126,14 +116,12 @@ module Listen
126
116
  caller_stack * "\n"
127
117
  )
128
118
 
129
- _log(:error, formatted)
119
+ Listen.logger.error(formatted)
130
120
  end
131
121
 
132
122
  class << self
133
- private
134
-
135
- def _log(*args, &block)
136
- Listen::Logger.send(*args, &block)
123
+ def usable?
124
+ const_get('OS_REGEXP') =~ RbConfig::CONFIG['target_os']
137
125
  end
138
126
  end
139
127
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Listener implementation for BSD's `kqueue`.
2
4
  # @see http://www.freebsd.org/cgi/man.cgi?query=kqueue
3
5
  # @see https://github.com/mat813/rb-kqueue/blob/master/lib/rb-kqueue/queue.rb
@@ -94,7 +96,7 @@ module Listen
94
96
  def _watch_file(path, queue)
95
97
  queue.watch_file(path, *options.events, &@callback)
96
98
  rescue Errno::ENOENT => e
97
- _log :warn, "kqueue: watch file failed: #{e.message}"
99
+ Listen.logger.warn "kqueue: watch file failed: #{e.message}"
98
100
  end
99
101
 
100
102
  # Quick rubocop workaround
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
 
3
5
  module Listen
@@ -1,12 +1,13 @@
1
- require 'thread'
2
- require 'listen/internals/thread_pool'
1
+ # frozen_string_literal: true
2
+
3
+ require 'listen/thread'
3
4
 
4
5
  module Listen
5
6
  module Adapter
6
7
  # Adapter implementation for Mac OS X `FSEvents`.
7
8
  #
8
9
  class Darwin < Base
9
- OS_REGEXP = /darwin(?<major_version>1\d+)/i
10
+ OS_REGEXP = /darwin(?<major_version>(1|2)\d+)/i
10
11
 
11
12
  # The default delay between checking for changes.
12
13
  DEFAULTS = { latency: 0.1 }.freeze
@@ -22,11 +23,11 @@ module Listen
22
23
  EOS
23
24
 
24
25
  def self.usable?
25
- require 'rb-fsevent'
26
26
  version = RbConfig::CONFIG['target_os'][OS_REGEXP, :major_version]
27
27
  return false unless version
28
28
  return true if version.to_i >= 13 # darwin13 is OS X 10.9
29
29
 
30
+ require 'rb-fsevent'
30
31
  fsevent_version = Gem::Version.new(FSEvent::VERSION)
31
32
  return true if fsevent_version <= Gem::Version.new('0.9.4')
32
33
  Kernel.warn INCOMPATIBLE_GEM_VERSION
@@ -35,57 +36,41 @@ module Listen
35
36
 
36
37
  private
37
38
 
38
- # NOTE: each directory gets a DIFFERENT callback!
39
39
  def _configure(dir, &callback)
40
- opts = { latency: options.latency }
41
-
42
- @workers ||= ::Queue.new
43
- @workers << FSEvent.new.tap do |worker|
44
- _log :debug, "fsevent: watching: #{dir.to_s.inspect}"
45
- worker.watch(dir.to_s, opts, &callback)
46
- end
40
+ @callbacks[dir] = callback
47
41
  end
48
42
 
49
43
  def _run
50
- first = @workers.pop
51
-
52
- # NOTE: _run is called within a thread, so run every other
53
- # worker in it's own thread
54
- _run_workers_in_background(_to_array(@workers))
55
- _run_worker(first)
44
+ require 'rb-fsevent'
45
+ worker = FSEvent.new
46
+ dirs_to_watch = @callbacks.keys.map(&:to_s)
47
+ Listen.logger.info { "fsevent: watching: #{dirs_to_watch.inspect}" }
48
+ worker.watch(dirs_to_watch, { latency: options.latency }, &method(:_process_changes))
49
+ @worker_thread = Listen::Thread.new("worker_thread") { worker.run }
56
50
  end
57
51
 
58
- def _process_event(dir, event)
59
- _log :debug, "fsevent: processing event: #{event.inspect}"
60
- event.each do |path|
61
- new_path = Pathname.new(path.sub(%r{\/$}, ''))
62
- _log :debug, "fsevent: #{new_path}"
63
- # TODO: does this preserve symlinks?
64
- rel_path = new_path.relative_path_from(dir).to_s
65
- _queue_change(:dir, dir, rel_path, recursive: true)
66
- end
67
- end
52
+ def _process_changes(dirs)
53
+ dirs.each do |dir|
54
+ dir = Pathname.new(dir.sub(%r{\/$}, ''))
68
55
 
69
- def _run_worker(worker)
70
- _log :debug, "fsevent: running worker: #{worker.inspect}"
71
- worker.run
72
- rescue
73
- format_string = 'fsevent: running worker failed: %s:%s called from: %s'
74
- _log_exception format_string, caller
56
+ @callbacks.each do |watched_dir, callback|
57
+ if watched_dir.eql?(dir) || Listen::Directory.ascendant_of?(watched_dir, dir)
58
+ callback.call(dir)
59
+ end
60
+ end
61
+ end
75
62
  end
76
63
 
77
- def _run_workers_in_background(workers)
78
- workers.each do |worker|
79
- # NOTE: while passing local variables to the block below is not
80
- # thread safe, using 'worker' from the enumerator above is ok
81
- Listen::Internals::ThreadPool.add { _run_worker(worker) }
82
- end
64
+ def _process_event(dir, path)
65
+ Listen.logger.debug { "fsevent: processing path: #{path.inspect}" }
66
+ # TODO: does this preserve symlinks?
67
+ rel_path = path.relative_path_from(dir).to_s
68
+ _queue_change(:dir, dir, rel_path, recursive: true)
83
69
  end
84
70
 
85
- def _to_array(queue)
86
- workers = []
87
- workers << queue.pop until queue.empty?
88
- workers
71
+ def _stop
72
+ @worker_thread&.kill
73
+ super
89
74
  end
90
75
  end
91
76
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Listen
2
4
  module Adapter
3
5
  # @see https://github.com/nex3/rb-inotify
@@ -9,6 +11,7 @@ module Listen
9
11
  :recursive,
10
12
  :attrib,
11
13
  :create,
14
+ :modify,
12
15
  :delete,
13
16
  :move,
14
17
  :close_write
@@ -35,9 +38,7 @@ module Listen
35
38
  end
36
39
 
37
40
  def _run
38
- Thread.current[:listen_blocking_read_thread] = true
39
41
  @worker.run
40
- Thread.current[:listen_blocking_read_thread] = false
41
42
  end
42
43
 
43
44
  def _process_event(dir, event)
@@ -47,7 +48,7 @@ module Listen
47
48
  path = Pathname.new(event.watcher.path) + event.name
48
49
  rel_path = path.relative_path_from(dir).to_s
49
50
 
50
- _log(:debug) { "inotify: #{rel_path} (#{event.flags.inspect})" }
51
+ Listen.logger.debug { "inotify: #{rel_path} (#{event.flags.inspect})" }
51
52
 
52
53
  if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
53
54
  if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
@@ -99,7 +100,9 @@ module Listen
99
100
  end
100
101
 
101
102
  def _stop
102
- @worker.close
103
+ @worker&.close
104
+
105
+ super
103
106
  end
104
107
  end
105
108
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Listen
2
4
  module Adapter
3
5
  # Polling Adapter that works cross-platform and
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Listen
2
4
  module Adapter
3
5
  # Adapter implementation for Windows `wdm`.
@@ -15,7 +17,7 @@ module Listen
15
17
  require 'wdm'
16
18
  true
17
19
  rescue LoadError
18
- _log :debug, format('wdm - load failed: %s:%s', $ERROR_INFO,
20
+ Listen.logger.debug format('wdm - load failed: %s:%s', $ERROR_INFO,
19
21
  $ERROR_POSITION * "\n")
20
22
 
21
23
  Kernel.warn BUNDLER_DECLARE_GEM
@@ -26,7 +28,7 @@ module Listen
26
28
 
27
29
  def _configure(dir)
28
30
  require 'wdm'
29
- _log :debug, 'wdm - starting...'
31
+ Listen.logger.debug 'wdm - starting...'
30
32
  @worker ||= WDM::Monitor.new
31
33
  @worker.watch_recursively(dir.to_s, :files) do |change|
32
34
  yield([:file, change])
@@ -47,7 +49,7 @@ module Listen
47
49
  end
48
50
 
49
51
  def _process_event(dir, event)
50
- _log :debug, "wdm - callback: #{event.inspect}"
52
+ Listen.logger.debug "wdm - callback: #{event.inspect}"
51
53
 
52
54
  type, change = event
53
55
 
@@ -80,7 +82,7 @@ module Listen
80
82
  end
81
83
  rescue
82
84
  details = event.inspect
83
- _log :error, format('wdm - callback (%s): %s:%s', details, $ERROR_INFO,
85
+ Listen.logger.error format('wdm - callback (%s): %s:%s', details, $ERROR_INFO,
84
86
  $ERROR_POSITION * "\n")
85
87
  raise
86
88
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'listen/adapter'
2
4
  require 'listen/adapter/base'
3
5
  require 'listen/adapter/config'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'listen/file'
2
4
  require 'listen/directory'
3
5
 
@@ -35,13 +37,13 @@ module Listen
35
37
  cookie = options[:cookie]
36
38
 
37
39
  if !cookie && config.silenced?(rel_path, type)
38
- Listen::Logger.debug { "(silenced): #{rel_path.inspect}" }
40
+ Listen.logger.debug { "(silenced): #{rel_path.inspect}" }
39
41
  return
40
42
  end
41
43
 
42
44
  path = watched_dir + rel_path
43
45
 
44
- Listen::Logger.debug do
46
+ Listen.logger.debug do
45
47
  log_details = options[:silence] && 'recording' || change || 'unknown'
46
48
  "#{log_details}: #{type}:#{path} (#{options.inspect})"
47
49
  end
@@ -65,7 +67,7 @@ module Listen
65
67
  __method__,
66
68
  exinspect,
67
69
  ex.backtrace * "\n")
68
- Listen::Logger.error(msg)
70
+ Listen.logger.error(msg)
69
71
  raise
70
72
  end
71
73
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
4
  require 'listen'
3
5
  require 'logger'
@@ -35,8 +37,7 @@ module Listen
35
37
  attr_reader :logger
36
38
  def initialize(options)
37
39
  @options = options
38
- @logger = ::Logger.new(STDOUT)
39
- @logger.level = ::Logger::INFO
40
+ @logger = ::Logger.new(STDOUT, level: ::Logger::INFO)
40
41
  @logger.formatter = proc { |_, _, _, msg| "#{msg}\n" }
41
42
  end
42
43
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  module Listen
@@ -14,7 +16,7 @@ module Listen
14
16
  path = dir + rel_path
15
17
  current = Set.new(_children(path))
16
18
 
17
- Listen::Logger.debug do
19
+ Listen.logger.debug do
18
20
  format('%s: %s(%s): %s -> %s',
19
21
  (options[:silence] ? 'Recording' : 'Scanning'),
20
22
  rel_path, options.inspect, previous.inspect, current.inspect)
@@ -47,12 +49,18 @@ module Listen
47
49
  _async_changes(snapshot, path, previous, options)
48
50
  _change(snapshot, :file, rel_path, options)
49
51
  rescue
50
- Listen::Logger.warn do
52
+ Listen.logger.warn do
51
53
  format('scan DIED: %s:%s', $ERROR_INFO, $ERROR_POSITION * "\n")
52
54
  end
53
55
  raise
54
56
  end
55
57
 
58
+ def self.ascendant_of?(base, other)
59
+ other.ascend do |ascendant|
60
+ break true if base == ascendant
61
+ end
62
+ end
63
+
56
64
  def self._async_changes(snapshot, path, previous, options)
57
65
  fail "Not a Pathname: #{path.inspect}" unless path.respond_to?(:children)
58
66
  previous.each do |entry, data|
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Listen
2
4
  module Event
3
5
  class Config
6
+ attr_reader :listener
7
+ attr_reader :event_queue
8
+ attr_reader :min_delay_between_events
9
+
4
10
  def initialize(
5
11
  listener,
6
12
  event_queue,
@@ -15,8 +21,8 @@ module Listen
15
21
  @block = block
16
22
  end
17
23
 
18
- def sleep(*args)
19
- Kernel.sleep(*args)
24
+ def sleep(seconds)
25
+ Kernel.sleep(seconds)
20
26
  end
21
27
 
22
28
  def call(*args)
@@ -27,8 +33,6 @@ module Listen
27
33
  Time.now.to_f
28
34
  end
29
35
 
30
- attr_reader :event_queue
31
-
32
36
  def callable?
33
37
  @block
34
38
  end
@@ -36,20 +40,6 @@ module Listen
36
40
  def optimize_changes(changes)
37
41
  @queue_optimizer.smoosh_changes(changes)
38
42
  end
39
-
40
- attr_reader :min_delay_between_events
41
-
42
- def stopped?
43
- listener.state == :stopped
44
- end
45
-
46
- def paused?
47
- listener.state == :paused
48
- end
49
-
50
- private
51
-
52
- attr_reader :listener
53
43
  end
54
44
  end
55
45
  end