listen 2.10.1 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 617639ec10e92398c4bb2ac0ee386f9f51301b77
4
- data.tar.gz: 56e366a55edf6c6f926f7d70a898ed86e02e0a76
3
+ metadata.gz: c4dbb678714e27c96553c3eeb428437dec7fdd06
4
+ data.tar.gz: 852d550603e6e96e47266c54d60a14cf2d680f01
5
5
  SHA512:
6
- metadata.gz: cc5839d65676759b511acbecbaef944fb8f23d4add9f5c210392b50cfab51f4ed8f4296584131f264a6b7d30218ab27c3e0c63ec6bfed0439d382112d1a34a1b
7
- data.tar.gz: 63c675e1adeae3ba7fa84e35f0729558db2eefcf3ad5a0447773bbfed7328412da623cccbea50ff00ead0f4ff9a8c205793da1b6d2609b6a90f52f27e986e8f5
6
+ metadata.gz: 080e123c08eb97243cf24d3dbb8f989c89fe797c5be172253d393b814550c50c66f765981125a825d47b61153c69715d6501b59d4f0bbabc36883d03c65623c1
7
+ data.tar.gz: a601fa4b17c2fcb5345fe4cb3fa8f10aaa28f6e31b22c5d456ede3dcf8ac8b641ee3c6b31fe53f411c5f69c3bcca8cd0a51b354e7ec778f08e5486d9081327e0
data/README.md CHANGED
@@ -8,7 +8,10 @@ The Listen gem listens to file modifications and notifies you about the changes.
8
8
 
9
9
  ## Known issues / Quickfixes / Workarounds
10
10
 
11
- Just head over here: https://github.com/guard/listen/wiki/Quickfixes,-known-issues-and-workarounds
11
+ *NOTE: TCP functionality has been removed from Listen 3.x - please use Listen
12
+ 2.x until alternative server and client gems are created/released for 3.x.*
13
+
14
+ For other issues, just head over here: https://github.com/guard/listen/wiki/Quickfixes,-known-issues-and-workarounds
12
15
 
13
16
  ## Tips and Techniques
14
17
 
@@ -20,15 +23,17 @@ Make sure you know these few basic tricks: https://github.com/guard/listen/wiki/
20
23
  * Detects file modification, addition and removal.
21
24
  * You can watch multiple directories.
22
25
  * Regexp-patterns for ignoring paths for more accuracy and speed
23
- * Forwarding file events over TCP, [more info](#forwarding-file-events-over-tcp) below.
24
26
  * Increased change detection accuracy on OS X HFS and VFAT volumes.
25
27
  * Tested on MRI Ruby environments (1.9+ only) via [Travis CI](https://travis-ci.org/guard/listen),
26
28
 
27
- Please note that:
29
+ NOTE: TCP functionality has been moved to a separate gem (listen-server and listen-client)
30
+
31
+ NOTES:
28
32
  - Some filesystems won't work without polling (VM/Vagrant Shared folders, NFS, Samba, sshfs, etc.)
29
33
  - Specs suite on JRuby and Rubinius aren't reliable on Travis CI, but should work.
30
34
  - Windows and \*BSD adapter aren't continuously and automaticaly tested.
31
35
 
36
+
32
37
  ## Pending features / issues
33
38
 
34
39
  * symlinked directories aren't fully transparent yet: https://github.com/guard/listen/issues/279
@@ -42,9 +47,10 @@ Pull request or help is very welcome for these.
42
47
  The simplest way to install Listen is to use [Bundler](http://bundler.io).
43
48
 
44
49
  ```ruby
45
- gem 'listen', '~> 2.7' # this prevents upgrading to 3.x
50
+ gem 'listen', '~> 3.0' # NOTE: for TCP functionality, use '~> 2.10' for now
46
51
  ```
47
52
 
53
+
48
54
  ## Usage
49
55
 
50
56
  Call `Listen.to` with either a single directory or multiple directories, then define the "changes" callback in a block.
@@ -166,7 +172,7 @@ force_polling: true # Force the use of the polling a
166
172
  relative: false # Whether changes should be relative to current dir or not
167
173
  # default: false
168
174
 
169
- debug: true # Enable Celluloid logger
175
+ debug: true # Enable Listen logger
170
176
  # default: false
171
177
 
172
178
  polling_fallback_message: 'custom message' # Set a custom polling fallback message (or disable it with false)
@@ -247,47 +253,6 @@ Also, if the directories you're watching contain many files, make sure you're:
247
253
 
248
254
  When in doubt, LISTEN_GEM_DEBUGGING=2 can help discover the actual events and time they happened.
249
255
 
250
- ## Forwarding file events over TCP
251
-
252
- Listen is capable of forwarding file events over the network using a messaging protocol. This can be useful for virtualized development environments when file events are unavailable, as is the case with shared folders in VMs.
253
-
254
- [Vagrant](https://github.com/mitchellh/vagrant) uses Listen in it's rsync-auto mode to solve this issue.
255
-
256
- To broadcast events over TCP programmatically, use the `forward_to` option with an address - just a port or a hostname/port combination:
257
-
258
- ```ruby
259
- listener = Listen.to 'path/to/app', forward_to: '10.0.0.2:4000' do |modified, added, removed|
260
- # After broadcasting the changes to any connected recipients,
261
- # this block will still be called
262
- end
263
- listener.start
264
- sleep
265
- ```
266
-
267
- As a convenience, the `listen` script is supplied which listens to a directory and forwards the events to a network address
268
-
269
- ```bash
270
- listen -f "10.0.0.2:4000" # changes in current directory are sent as absolute paths
271
- listen -r -f "10.0.0.2:4000" # changes in current directory are sent as relative paths
272
- listen -v -d "/projects/my_project" -f "10.0.0.2:4000" # changes in given directory are also shown
273
- ```
274
-
275
- *NOTE: if you are using a gem like `guard` and the paths on host and guest are not exactly the same, you'll generally want to use the `-r` option for relative paths*
276
-
277
- To connect to a broadcasting listener as a recipient, specify its address using `Listen.on`:
278
-
279
- ```ruby
280
- listener = Listen.on '10.0.0.2:4000' do |modified, added, removed|
281
- # This block will be called
282
- end
283
- listener.start
284
- sleep
285
- ```
286
-
287
- ### Security considerations
288
-
289
- Since file events potentially expose sensitive information, care must be taken when specifying the broadcaster address. It is recommended to **always** specify a hostname and make sure it is as specific as possible to reduce any undesirable eavesdropping.
290
-
291
256
  ## Development
292
257
 
293
258
  * Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/listen/master/frames).
data/lib/listen.rb CHANGED
@@ -1,16 +1,27 @@
1
- require 'celluloid'
1
+ require 'logger'
2
+ require 'listen/logger'
2
3
  require 'listen/listener'
3
4
 
4
5
  require 'listen/internals/thread_pool'
5
6
 
7
+ # Always set up logging by default first time file is required
8
+ #
9
+ # NOTE: If you need to clear the logger completely, do so *after*
10
+ # requiring this file. If you need to set a custom logger,
11
+ # require the listen/logger file and set the logger before requiring
12
+ # this file.
13
+ Listen.setup_default_logger_if_unset
14
+
15
+ # Won't print anything by default because of level - unless you've set
16
+ # LISTEN_GEM_DEBUGGING or provided your own logger with a high enough level
17
+ Listen::Logger.info "Listen loglevel set to: #{Listen.logger.level}"
18
+ Listen::Logger.info "Listen version: #{Listen::VERSION}"
19
+
6
20
  module Listen
7
21
  class << self
8
22
  # Listens to file system modifications on a either single directory or
9
23
  # multiple directories.
10
24
  #
11
- # When :forward_to is specified, this listener will broadcast modifications
12
- # over TCP.
13
- #
14
25
  # @param (see Listen::Listener#new)
15
26
  #
16
27
  # @yield [modified, added, removed] the changed files
@@ -21,18 +32,12 @@ module Listen
21
32
  # @return [Listen::Listener] the listener
22
33
  #
23
34
  def to(*args, &block)
24
- Celluloid.boot unless Celluloid.running?
25
- options = args.last.is_a?(Hash) ? args.last : {}
26
- target = options.delete(:forward_to)
27
- args = ([target, :broadcaster] + args) if target
28
- _add_listener(*args, &block)
35
+ @listeners ||= []
36
+ Listener.new(*args, &block).tap do |listener|
37
+ @listeners << listener
38
+ end
29
39
  end
30
40
 
31
- # Stop all listeners & Celluloid
32
- #
33
- # Use it for testing purpose or when you are sure that Celluloid could be
34
- # ended.
35
- #
36
41
  # This is used by the `listen` binary to handle Ctrl-C
37
42
  #
38
43
  def stop
@@ -45,32 +50,6 @@ module Listen
45
50
  listener.stop
46
51
  end
47
52
  @listeners = nil
48
-
49
- Celluloid.shutdown
50
- end
51
-
52
- # Listens to file system modifications broadcast over TCP.
53
- #
54
- # @param [String/Fixnum] target to listen on (hostname:port or port)
55
- #
56
- # @yield [modified, added, removed] the changed files
57
- # @yieldparam [Array<String>] modified the list of modified files
58
- # @yieldparam [Array<String>] added the list of added files
59
- # @yieldparam [Array<String>] removed the list of removed files
60
- #
61
- # @return [Listen::Listener] the listener
62
- #
63
- def on(target, *args, &block)
64
- _add_listener(target, :recipient, *args, &block)
65
- end
66
-
67
- private
68
-
69
- def _add_listener(*args, &block)
70
- @listeners ||= []
71
- Listener.new(*args, &block).tap do |listener|
72
- @listeners << listener
73
- end
74
53
  end
75
54
  end
76
55
  end
@@ -12,8 +12,6 @@ module Listen
12
12
  'Learn more at https://github.com/guard/listen#listen-adapters.'
13
13
 
14
14
  def self.select(options = {})
15
- _log :debug, 'Adapter: considering TCP ...'
16
- return TCP if options[:force_tcp]
17
15
  _log :debug, 'Adapter: considering polling ...'
18
16
  return Polling if options[:force_polling]
19
17
  _log :debug, 'Adapter: considering optimized backend...'
@@ -39,7 +37,7 @@ module Listen
39
37
  end
40
38
 
41
39
  def self._log(type, message)
42
- Celluloid::Logger.send(type, message)
40
+ Listen::Logger.send(type, message)
43
41
  end
44
42
  end
45
43
  end
@@ -1,32 +1,27 @@
1
1
  require 'listen/options'
2
+ require 'listen/record'
3
+ require 'listen/change'
2
4
 
3
5
  module Listen
4
6
  module Adapter
5
7
  class Base
6
- include Celluloid
7
-
8
8
  attr_reader :options
9
9
 
10
10
  # TODO: only used by tests
11
11
  DEFAULTS = {}
12
12
 
13
- def initialize(opts)
14
- @configured = nil
15
- options = opts.dup
16
- @mq = options.delete(:mq)
17
- @directories = options.delete(:directories)
13
+ attr_reader :config
18
14
 
19
- Array(@directories).each do |dir|
20
- next if dir.is_a?(Pathname)
21
- fail ArgumentError, "not a Pathname: #{dir.inspect}"
22
- end
15
+ def initialize(config)
16
+ @started = false
17
+ @config = config
23
18
 
24
- # TODO: actually use this in every adapter
25
- @recursion = options.delete(:recursion)
26
- @recursion = true if @recursion.nil?
19
+ @configured = nil
20
+
21
+ fail 'No directories to watch!' if config.directories.empty?
27
22
 
28
23
  defaults = self.class.const_get('DEFAULTS')
29
- @options = Listen::Options.new(options, defaults)
24
+ @options = Listen::Options.new(config.adapter_options, defaults)
30
25
  rescue
31
26
  _log_exception 'adapter config failed: %s:%s called from: %s', caller
32
27
  raise
@@ -34,50 +29,82 @@ module Listen
34
29
 
35
30
  # TODO: it's a separate method as a temporary workaround for tests
36
31
  def configure
37
- return if @configured
32
+ if @configured
33
+ _log(:warn, 'Adapter already configured!')
34
+ return
35
+ end
36
+
38
37
  @configured = true
39
38
 
40
39
  @callbacks ||= {}
41
- @directories.each do |dir|
42
- unless dir.is_a?(Pathname)
43
- fail ArgumentError, "not a Pathname: #{dir.inspect}"
44
- end
45
-
40
+ config.directories.each do |dir|
46
41
  callback = @callbacks[dir] || lambda do |event|
47
42
  _process_event(dir, event)
48
43
  end
49
44
  @callbacks[dir] = callback
50
45
  _configure(dir, &callback)
51
46
  end
47
+
48
+ @snapshots ||= {}
49
+ # TODO: separate config per directory (some day maybe)
50
+ change_config = Change::Config.new(config.queue, config.silencer)
51
+ config.directories.each do |dir|
52
+ record = Record.new(dir)
53
+ snapshot = Change.new(change_config, record)
54
+ @snapshots[dir] = snapshot
55
+ end
56
+ end
57
+
58
+ def started?
59
+ @started
52
60
  end
53
61
 
54
62
  def start
55
63
  configure
64
+
65
+ if started?
66
+ _log(:warn, 'Adapter already started!')
67
+ return
68
+ end
69
+
70
+ @started = true
71
+
56
72
  calling_stack = caller.dup
57
73
  Listen::Internals::ThreadPool.add do
58
74
  begin
75
+ @snapshots.values.each do |snapshot|
76
+ _timed('Record.build()') { snapshot.record.build }
77
+ end
59
78
  _run
60
79
  rescue
61
- _log_exception "run() in thread failed: %s:\n %s\n\ncalled from:\n %s", calling_stack
62
- raise
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
63
84
  end
64
85
  end
65
86
  end
66
87
 
67
- def self.local_fs?
68
- true
69
- end
70
-
71
88
  def self.usable?
72
89
  const_get('OS_REGEXP') =~ RbConfig::CONFIG['target_os']
73
90
  end
74
91
 
75
92
  private
76
93
 
94
+ def _timed(title)
95
+ start = Time.now.to_f
96
+ yield
97
+ diff = Time.now.to_f - start
98
+ Listen::Logger.info format('%s: %.05f seconds', title, diff)
99
+ rescue
100
+ Listen::Logger.warn "#{title} crashed: #{$ERROR_INFO.inspect}"
101
+ raise
102
+ end
103
+
104
+ # TODO: allow backend adapters to pass specific invalidation objects
105
+ # e.g. Darwin -> DirRescan, INotify -> MoveScan, etc.
77
106
  def _queue_change(type, dir, rel_path, options)
78
- # TODO: temporary workaround to remove dependency on Change through
79
- # Celluloid in tests
80
- @mq.send(:_queue_raw_change, type, dir, rel_path, options)
107
+ @snapshots[dir].invalidate(type, rel_path, options)
81
108
  end
82
109
 
83
110
  def _log(*args, &block)
@@ -85,15 +112,18 @@ module Listen
85
112
  end
86
113
 
87
114
  def _log_exception(msg, caller_stack)
88
- _log :error, format(msg, $ERROR_INFO, $ERROR_POSITION * "\n", caller_stack * "\n")
115
+ formatted = format(
116
+ msg,
117
+ $ERROR_INFO,
118
+ $ERROR_POSITION * "\n",
119
+ caller_stack * "\n"
120
+ )
121
+
122
+ _log(:error, formatted)
89
123
  end
90
124
 
91
125
  def self._log(*args, &block)
92
- if block
93
- Celluloid::Logger.send(*args, block.call)
94
- else
95
- Celluloid::Logger.send(*args)
96
- end
126
+ Listen::Logger.send(*args, &block)
97
127
  end
98
128
  end
99
129
  end
@@ -0,0 +1,21 @@
1
+ module Listen
2
+ module Adapter
3
+ class Config
4
+ attr_reader :directories
5
+ attr_reader :silencer
6
+ attr_reader :queue
7
+ attr_reader :adapter_options
8
+
9
+ def initialize(directories, queue, silencer, adapter_options)
10
+ # TODO: fix (flatten, array, compact?)
11
+ @directories = directories.map do |directory|
12
+ Pathname.new(directory.to_s).realpath
13
+ end
14
+
15
+ @silencer = silencer
16
+ @queue = queue
17
+ @adapter_options = adapter_options
18
+ end
19
+ end
20
+ end
21
+ end
@@ -12,7 +12,8 @@ module Listen
12
12
  :delete,
13
13
  :move,
14
14
  :close_write
15
- ]
15
+ ],
16
+ wait_for_delay: 0.1
16
17
  }
17
18
 
18
19
  private
@@ -26,13 +27,10 @@ module Listen
26
27
  EOS
27
28
 
28
29
  def _configure(directory, &callback)
29
- require 'rb-inotify'
30
- @worker ||= INotify::Notifier.new
30
+ Kernel.require 'rb-inotify'
31
+ @worker ||= ::INotify::Notifier.new
31
32
  @worker.watch(directory.to_s, *options.events, &callback)
32
33
  rescue Errno::ENOSPC
33
- # workaround - Celluloid catches abort and prints nothing
34
- STDERR.puts INOTIFY_LIMIT_MESSAGE
35
- STDERR.flush
36
34
  abort(INOTIFY_LIMIT_MESSAGE)
37
35
  end
38
36
 
@@ -47,7 +45,7 @@ module Listen
47
45
  path = Pathname.new(event.watcher.path) + event.name
48
46
  rel_path = path.relative_path_from(dir).to_s
49
47
 
50
- _log :debug, "inotify: #{rel_path} (#{event.flags.inspect})"
48
+ _log(:debug) { "inotify: #{rel_path} (#{event.flags.inspect})" }
51
49
 
52
50
  if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
53
51
  if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
@@ -8,7 +8,7 @@ module Listen
8
8
  class Polling < Base
9
9
  OS_REGEXP = // # match every OS
10
10
 
11
- DEFAULTS = { latency: 1.0 }
11
+ DEFAULTS = { latency: 1.0, wait_for_delay: 0.05 }
12
12
 
13
13
  private
14
14
 
@@ -23,6 +23,7 @@ module Listen
23
23
  @polling_callbacks.each do |callback|
24
24
  callback.call(nil)
25
25
  nap_time = options.latency - (Time.now.to_f - start)
26
+ # TODO: warn if nap_time is negative (polling too slow)
26
27
  sleep(nap_time) if nap_time > 0
27
28
  end
28
29
  end