listen 2.7.4 → 2.7.5
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 +4 -4
- data/.rubocop.yml +232 -0
- data/.travis.yml +6 -3
- data/Gemfile +9 -1
- data/Guardfile +6 -1
- data/README.md +17 -4
- data/lib/listen.rb +9 -4
- data/lib/listen/adapter.rb +5 -7
- data/lib/listen/adapter/base.rb +8 -5
- data/lib/listen/adapter/bsd.rb +58 -21
- data/lib/listen/adapter/darwin.rb +2 -4
- data/lib/listen/adapter/linux.rb +20 -10
- data/lib/listen/adapter/polling.rb +0 -2
- data/lib/listen/adapter/tcp.rb +6 -5
- data/lib/listen/adapter/windows.rb +8 -6
- data/lib/listen/change.rb +1 -2
- data/lib/listen/cli.rb +25 -22
- data/lib/listen/directory.rb +8 -6
- data/lib/listen/listener.rb +25 -19
- data/lib/listen/record.rb +4 -2
- data/lib/listen/silencer.rb +55 -25
- data/lib/listen/tcp.rb +9 -0
- data/lib/listen/tcp/broadcaster.rb +0 -2
- data/lib/listen/tcp/listener.rb +13 -8
- data/lib/listen/tcp/message.rb +0 -2
- data/lib/listen/version.rb +1 -1
- data/listen.gemspec +4 -3
- data/spec/acceptance/listen_spec.rb +190 -109
- data/spec/acceptance/tcp_spec.rb +28 -26
- data/spec/lib/listen/adapter/base_spec.rb +14 -12
- data/spec/lib/listen/adapter/bsd_spec.rb +5 -2
- data/spec/lib/listen/adapter/darwin_spec.rb +5 -2
- data/spec/lib/listen/adapter/linux_spec.rb +40 -25
- data/spec/lib/listen/adapter/polling_spec.rb +29 -14
- data/spec/lib/listen/adapter/tcp_spec.rb +24 -6
- data/spec/lib/listen/adapter/windows_spec.rb +5 -2
- data/spec/lib/listen/adapter_spec.rb +20 -17
- data/spec/lib/listen/change_spec.rb +36 -26
- data/spec/lib/listen/directory_spec.rb +128 -71
- data/spec/lib/listen/file_spec.rb +67 -34
- data/spec/lib/listen/listener_spec.rb +135 -105
- data/spec/lib/listen/record_spec.rb +32 -29
- data/spec/lib/listen/silencer_spec.rb +78 -56
- data/spec/lib/listen/tcp/broadcaster_spec.rb +3 -2
- data/spec/lib/listen/tcp/listener_spec.rb +17 -11
- data/spec/lib/listen/tcp/message_spec.rb +1 -1
- data/spec/lib/listen_spec.rb +18 -6
- data/spec/spec_helper.rb +5 -1
- data/spec/support/acceptance_helper.rb +3 -3
- data/spec/support/fixtures_helper.rb +10 -9
- metadata +17 -15
data/lib/listen/adapter/bsd.rb
CHANGED
@@ -1,26 +1,46 @@
|
|
1
|
+
# Listener implementation for BSD's `kqueue`.
|
2
|
+
# @see http://www.freebsd.org/cgi/man.cgi?query=kqueue
|
3
|
+
# @see https://github.com/mat813/rb-kqueue/blob/master/lib/rb-kqueue/queue.rb
|
4
|
+
#
|
1
5
|
module Listen
|
2
6
|
module Adapter
|
3
|
-
|
4
|
-
# Listener implementation for BSD's `kqueue`.
|
5
|
-
#
|
6
7
|
class BSD < Base
|
7
8
|
# Watched kqueue events
|
8
9
|
#
|
9
|
-
# @see http://www.freebsd.org/cgi/man.cgi?query=kqueue
|
10
|
-
# @see https://github.com/mat813/rb-kqueue/blob/master/lib/rb-kqueue/queue.rb
|
11
|
-
#
|
12
10
|
EVENTS = [:delete, :write, :extend, :attrib, :rename] # :link, :revoke
|
13
11
|
|
12
|
+
BSD_OS_REGEXP = /bsd|dragonfly/i
|
13
|
+
|
14
14
|
# The message to show when wdm gem isn't available
|
15
15
|
#
|
16
16
|
BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
|
17
17
|
Please add the following to your Gemfile to avoid polling for changes:
|
18
18
|
require 'rbconfig'
|
19
|
-
|
19
|
+
if RbConfig::CONFIG['target_os'] =~ #{BSD_OS_REGEXP}
|
20
|
+
gem 'rb-kqueue', '>= 0.2'
|
21
|
+
|
22
|
+
# Base versions have known conflicts/bugs
|
23
|
+
# Even master branches may not work...
|
24
|
+
gem 'ffi', github: 'carpetsmoker/ffi', ref: 'ac63e07f7'
|
25
|
+
gem 'celluloid', github: 'celluloid/celluloid', ref: '7fdef04'
|
26
|
+
end
|
27
|
+
EOS
|
28
|
+
|
29
|
+
BSD_EXPERIMENTAL = <<-EOS.gsub(/^ {6}/, '')
|
30
|
+
NOTE *BSD SUPPORT IS EXPERIMENTAL!
|
31
|
+
|
32
|
+
In fact, it likely WONT WORK!!!!
|
33
|
+
|
34
|
+
(see: https://github.com/guard/listen/issues/220)
|
35
|
+
|
36
|
+
If you're brave enough, feel free to suggest pull requests and
|
37
|
+
experiment on your own. For help, browse existing issues marked 'bsd'
|
38
|
+
for clues, tips and workaround.
|
20
39
|
EOS
|
21
40
|
|
22
41
|
def self.usable?
|
23
|
-
if RbConfig::CONFIG['target_os'] =~
|
42
|
+
if RbConfig::CONFIG['target_os'] =~ BSD_OS_REGEXP
|
43
|
+
Kernel.warn BSD_EXPERIMENTAL
|
24
44
|
require 'rb-kqueue'
|
25
45
|
require 'find'
|
26
46
|
true
|
@@ -32,7 +52,7 @@ module Listen
|
|
32
52
|
|
33
53
|
def start
|
34
54
|
worker = _init_worker
|
35
|
-
Thread.new { worker.
|
55
|
+
Thread.new { worker.run }
|
36
56
|
end
|
37
57
|
|
38
58
|
private
|
@@ -45,27 +65,38 @@ module Listen
|
|
45
65
|
def _init_worker
|
46
66
|
KQueue::Queue.new.tap do |queue|
|
47
67
|
_directories_path.each do |path|
|
48
|
-
|
68
|
+
# use Record to make a snapshot of dir, so we
|
69
|
+
# can detect new files
|
70
|
+
_find(path) { |file_path| _watch_file(file_path, queue) }
|
49
71
|
end
|
50
72
|
end
|
51
73
|
end
|
52
74
|
|
53
75
|
def _worker_callback
|
54
76
|
lambda do |event|
|
55
|
-
|
77
|
+
change = _change(event.flags)
|
78
|
+
path = _event_path(event)
|
79
|
+
if path.directory?
|
80
|
+
# Force dir content tracking to kick in, or we won't have
|
81
|
+
# names of added files
|
82
|
+
_notify_change(path, type: 'Directory', recursive: true)
|
83
|
+
else
|
84
|
+
_notify_change(path, type: 'File', change: change)
|
85
|
+
end
|
56
86
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
87
|
+
# If it is a directory, and it has a write flag, it means a
|
88
|
+
# file has been added so find out which and deal with it.
|
89
|
+
# No need to check for removed files, kqueue will forget them
|
90
|
+
# when the vfs does.
|
91
|
+
_watch_for_new_file(event) if _new_file_added?(event)
|
62
92
|
end
|
63
93
|
end
|
64
94
|
|
65
95
|
def _change(event_flags)
|
66
96
|
{ modified: [:attrib, :extend],
|
67
97
|
added: [:write],
|
68
|
-
removed: [:rename, :delete]
|
98
|
+
removed: [:rename, :delete]
|
99
|
+
}.each do |change, flags|
|
69
100
|
return change unless (flags & event_flags).empty?
|
70
101
|
end
|
71
102
|
nil
|
@@ -76,20 +107,26 @@ module Listen
|
|
76
107
|
end
|
77
108
|
|
78
109
|
def _new_file_added?(event)
|
79
|
-
File.directory?(event.watcher.path) && event.flags.include?(:write)
|
110
|
+
::File.directory?(event.watcher.path) && event.flags.include?(:write)
|
80
111
|
end
|
81
112
|
|
82
113
|
def _watch_for_new_file(event)
|
83
114
|
queue = event.watcher.queue
|
84
|
-
|
85
|
-
|
115
|
+
_find(_event_path(event).to_s) do |file_path|
|
116
|
+
unless queue.watchers.detect { |_, v| v.path == file_path.to_s }
|
117
|
+
_watch_file(file_path, queue)
|
118
|
+
end
|
86
119
|
end
|
87
120
|
end
|
88
121
|
|
89
122
|
def _watch_file(path, queue)
|
90
123
|
queue.watch_file(path, *EVENTS, &_worker_callback)
|
91
124
|
end
|
92
|
-
end
|
93
125
|
|
126
|
+
# Quick rubocop workaround
|
127
|
+
def _find(*paths)
|
128
|
+
Find.send(:find, *paths)
|
129
|
+
end
|
130
|
+
end
|
94
131
|
end
|
95
132
|
end
|
@@ -1,10 +1,8 @@
|
|
1
1
|
module Listen
|
2
2
|
module Adapter
|
3
|
-
|
4
3
|
# Adapter implementation for Mac OS X `FSEvents`.
|
5
4
|
#
|
6
5
|
class Darwin < Base
|
7
|
-
|
8
6
|
def self.usable?
|
9
7
|
RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
|
10
8
|
end
|
@@ -27,7 +25,8 @@ module Listen
|
|
27
25
|
def _init_worker
|
28
26
|
FSEvent.new.tap do |worker|
|
29
27
|
worker.watch(_directories_path, latency: _latency) do |changes|
|
30
|
-
_changes_path(changes)
|
28
|
+
paths = _changes_path(changes)
|
29
|
+
paths.each { |path| _notify_change(path, type: 'Dir') }
|
31
30
|
end
|
32
31
|
end
|
33
32
|
end
|
@@ -39,6 +38,5 @@ module Listen
|
|
39
38
|
end
|
40
39
|
end
|
41
40
|
end
|
42
|
-
|
43
41
|
end
|
44
42
|
end
|
data/lib/listen/adapter/linux.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
module Listen
|
2
2
|
module Adapter
|
3
|
-
|
4
3
|
# Listener implementation for Linux `inotify`.
|
5
4
|
#
|
6
5
|
class Linux < Base
|
7
6
|
# Watched inotify events
|
8
7
|
#
|
9
8
|
# @see http://www.tin.org/bin/man.cgi?section=7&topic=inotify
|
10
|
-
# @see https://github.com/nex3/rb-inotify
|
9
|
+
# @see https://github.com/nex3/rb-inotify
|
11
10
|
#
|
12
11
|
EVENTS = [:recursive, :attrib, :create, :delete, :move, :close_write]
|
13
12
|
|
14
13
|
# The message to show when the limit of inotify watchers is not enough
|
15
14
|
#
|
15
|
+
WIKI_URL = 'https://github.com/guard/listen'\
|
16
|
+
'/wiki/Increasing-the-amount-of-inotify-watchers'
|
17
|
+
|
16
18
|
INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '')
|
17
19
|
FATAL: Listen error: unable to monitor directories for changes.
|
18
20
|
|
19
|
-
Please head to
|
21
|
+
Please head to #{WIKI_URL}
|
20
22
|
for information on how to solve this issue.
|
21
23
|
EOS
|
22
24
|
|
@@ -47,7 +49,9 @@ module Listen
|
|
47
49
|
#
|
48
50
|
def _init_worker
|
49
51
|
INotify::Notifier.new.tap do |worker|
|
50
|
-
_directories_path.each
|
52
|
+
_directories_path.each do |path|
|
53
|
+
worker.watch(path, *EVENTS, &_worker_callback)
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
@@ -58,24 +62,25 @@ module Listen
|
|
58
62
|
path = _event_path(event)
|
59
63
|
cookie_opts = event.cookie.zero? ? {} : { cookie: event.cookie }
|
60
64
|
|
61
|
-
|
65
|
+
_log(event)
|
62
66
|
|
63
67
|
if _dir_event?(event)
|
64
|
-
_notify_change(path, { type: 'Dir'}.merge(cookie_opts))
|
68
|
+
_notify_change(path, { type: 'Dir' }.merge(cookie_opts))
|
65
69
|
else
|
66
|
-
|
70
|
+
options = { type: 'File', change: _change(event.flags) }
|
71
|
+
_notify_change(path, options.merge(cookie_opts))
|
67
72
|
end
|
68
73
|
end
|
69
74
|
end
|
70
75
|
|
71
76
|
def _skip_event?(event)
|
72
77
|
# Event on root directory
|
73
|
-
return true if event.name ==
|
78
|
+
return true if event.name == ''
|
74
79
|
# INotify reports changes to files inside directories as events
|
75
80
|
# on the directories themselves too.
|
76
81
|
#
|
77
82
|
# @see http://linux.die.net/man/7/inotify
|
78
|
-
|
83
|
+
_dir_event?(event) && (event.flags & [:close, :modify]).any?
|
79
84
|
end
|
80
85
|
|
81
86
|
def _change(event_flags)
|
@@ -96,7 +101,12 @@ module Listen
|
|
96
101
|
def _event_path(event)
|
97
102
|
Pathname.new(event.absolute_name)
|
98
103
|
end
|
99
|
-
end
|
100
104
|
|
105
|
+
def _log(event)
|
106
|
+
name = event.name
|
107
|
+
flags = event.flags.inspect
|
108
|
+
Celluloid.logger.info "inotify event: #{flags}: #{name}"
|
109
|
+
end
|
110
|
+
end
|
101
111
|
end
|
102
112
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Listen
|
2
2
|
module Adapter
|
3
|
-
|
4
3
|
# Polling Adapter that works cross-platform and
|
5
4
|
# has no dependencies. This is the adapter that
|
6
5
|
# uses the most CPU processing power and has higher
|
@@ -44,6 +43,5 @@ module Listen
|
|
44
43
|
sleep(nap_time) if nap_time > 0
|
45
44
|
end
|
46
45
|
end
|
47
|
-
|
48
46
|
end
|
49
47
|
end
|
data/lib/listen/adapter/tcp.rb
CHANGED
@@ -2,7 +2,6 @@ require 'celluloid/io'
|
|
2
2
|
|
3
3
|
module Listen
|
4
4
|
module Adapter
|
5
|
-
|
6
5
|
# Adapter to receive file system modifications over TCP
|
7
6
|
class TCP < Base
|
8
7
|
include Celluloid::IO
|
@@ -18,7 +17,7 @@ module Listen
|
|
18
17
|
# Initializes and starts a Celluloid::IO-powered TCP-recipient
|
19
18
|
def start
|
20
19
|
@socket = TCPSocket.new(listener.host, listener.port)
|
21
|
-
@buffer =
|
20
|
+
@buffer = ''
|
22
21
|
run
|
23
22
|
end
|
24
23
|
|
@@ -36,7 +35,7 @@ module Listen
|
|
36
35
|
|
37
36
|
# Continuously receive and asynchronously handle data
|
38
37
|
def run
|
39
|
-
while data = @socket.recv(RECEIVE_WINDOW)
|
38
|
+
while (data = @socket.recv(RECEIVE_WINDOW))
|
40
39
|
async.handle_data(data)
|
41
40
|
end
|
42
41
|
end
|
@@ -44,7 +43,7 @@ module Listen
|
|
44
43
|
# Buffers incoming data and handles messages accordingly
|
45
44
|
def handle_data(data)
|
46
45
|
@buffer << data
|
47
|
-
while message = Listen::TCP::Message.from_buffer(@buffer)
|
46
|
+
while (message = Listen::TCP::Message.from_buffer(@buffer))
|
48
47
|
handle_message(message)
|
49
48
|
end
|
50
49
|
end
|
@@ -58,7 +57,9 @@ module Listen
|
|
58
57
|
end
|
59
58
|
end
|
60
59
|
|
60
|
+
def self.local_fs?
|
61
|
+
false
|
62
|
+
end
|
61
63
|
end
|
62
|
-
|
63
64
|
end
|
64
65
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module Listen
|
2
2
|
module Adapter
|
3
|
-
|
4
3
|
# Adapter implementation for Windows `wdm`.
|
5
4
|
#
|
6
5
|
class Windows < Base
|
7
|
-
|
8
6
|
# The message to show when wdm gem isn't available
|
9
7
|
#
|
10
8
|
BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
|
11
9
|
Please add the following to your Gemfile to avoid polling for changes:
|
12
10
|
require 'rbconfig'
|
13
|
-
|
11
|
+
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw|cygwin/i
|
12
|
+
gem 'wdm', '>= 0.1.0'
|
13
|
+
end
|
14
14
|
EOS
|
15
15
|
|
16
16
|
def self.usable?
|
@@ -37,13 +37,16 @@ module Listen
|
|
37
37
|
#
|
38
38
|
def _init_worker
|
39
39
|
WDM::Monitor.new.tap do |worker|
|
40
|
-
_directories_path.each
|
40
|
+
_directories_path.each do |path|
|
41
|
+
worker.watch_recursively(path, &_worker_callback)
|
42
|
+
end
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
46
|
def _worker_callback
|
45
47
|
lambda do |change|
|
46
|
-
|
48
|
+
options = { type: 'File', change: _change(change.type) }
|
49
|
+
_notify_change(_path(change.path), options)
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -60,6 +63,5 @@ module Listen
|
|
60
63
|
nil
|
61
64
|
end
|
62
65
|
end
|
63
|
-
|
64
66
|
end
|
65
67
|
end
|
data/lib/listen/change.rb
CHANGED
@@ -16,7 +16,7 @@ module Listen
|
|
16
16
|
cookie = options[:cookie]
|
17
17
|
|
18
18
|
unless cookie
|
19
|
-
#TODO: remove silencing here (it's done later)
|
19
|
+
# TODO: remove silencing here (it's done later)
|
20
20
|
return if _silencer.silenced?(path, options[:type])
|
21
21
|
end
|
22
22
|
|
@@ -47,6 +47,5 @@ module Listen
|
|
47
47
|
def _silencer
|
48
48
|
listener.registry[:silencer]
|
49
49
|
end
|
50
|
-
|
51
50
|
end
|
52
51
|
end
|
data/lib/listen/cli.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'listen'
|
3
|
+
require 'logger'
|
3
4
|
|
4
5
|
module Listen
|
5
6
|
class CLI < Thor
|
@@ -8,44 +9,46 @@ module Listen
|
|
8
9
|
desc 'start', 'Starts Listen'
|
9
10
|
|
10
11
|
class_option :verbose,
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
type: :boolean,
|
13
|
+
default: false,
|
14
|
+
aliases: '-v',
|
15
|
+
banner: 'Verbose'
|
15
16
|
|
16
17
|
class_option :forward,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
type: :string,
|
19
|
+
default: '127.0.0.1:4000',
|
20
|
+
aliases: '-f',
|
21
|
+
banner: 'The address to forward filesystem events'
|
21
22
|
|
22
23
|
class_option :directory,
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def start
|
30
|
-
Listen::Forwarder.new(options).start
|
31
|
-
end
|
24
|
+
type: :string,
|
25
|
+
default: '.',
|
26
|
+
aliases: '-d',
|
27
|
+
banner: 'The directory to listen to'
|
32
28
|
|
29
|
+
def start
|
30
|
+
Listen::Forwarder.new(options).start
|
31
|
+
end
|
33
32
|
end
|
34
33
|
|
35
34
|
class Forwarder
|
35
|
+
attr_reader :logger
|
36
36
|
def initialize(options)
|
37
37
|
@options = options
|
38
|
+
@logger = Logger.new(STDOUT)
|
39
|
+
@logger.level = Logger::INFO
|
40
|
+
@logger.formatter = proc { |_, _, _, msg| "#{msg}\n" }
|
38
41
|
end
|
39
42
|
|
40
43
|
def start
|
41
|
-
|
44
|
+
logger.info 'Starting listen...'
|
42
45
|
address = @options[:forward]
|
43
46
|
directory = @options[:directory]
|
44
|
-
callback =
|
47
|
+
callback = proc do |modified, added, removed|
|
45
48
|
if @options[:verbose]
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
+
logger.info "+ #{added}" unless added.empty?
|
50
|
+
logger.info "- #{removed}" unless removed.empty?
|
51
|
+
logger.info "> #{modified}" unless modified.empty?
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|