listen 2.10.1 → 3.0.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
  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