listen 0.5.1 → 0.5.2
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.
- data/CHANGELOG.md +173 -167
- data/LICENSE +20 -20
- data/lib/listen/adapters/polling.rb +1 -1
- data/lib/listen/directory_record.rb +344 -318
- data/lib/listen/listener.rb +203 -203
- data/lib/listen/multi_listener.rb +121 -121
- data/lib/listen/turnstile.rb +28 -28
- data/lib/listen/version.rb +3 -3
- metadata +11 -15
data/lib/listen/listener.rb
CHANGED
@@ -1,203 +1,203 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
|
3
|
-
module Listen
|
4
|
-
class Listener
|
5
|
-
attr_reader :directory, :directory_record, :adapter
|
6
|
-
|
7
|
-
# The default value for using relative paths in the callback.
|
8
|
-
DEFAULT_TO_RELATIVE_PATHS = false
|
9
|
-
|
10
|
-
# Initializes the directory listener.
|
11
|
-
#
|
12
|
-
# @param [String] directory the directory to listen to
|
13
|
-
# @param [Hash] options the listen options
|
14
|
-
# @option options [Regexp] ignore a pattern for ignoring paths
|
15
|
-
# @option options [Regexp] filter a pattern for filtering paths
|
16
|
-
# @option options [Float] latency the delay between checking for changes in seconds
|
17
|
-
# @option options [Boolean] relative_paths whether or not to use relative-paths in the callback
|
18
|
-
# @option options [Boolean] force_polling whether to force the polling adapter or not
|
19
|
-
# @option options [String, Boolean] polling_fallback_message to change polling fallback message or remove it
|
20
|
-
#
|
21
|
-
# @yield [modified, added, removed] the changed files
|
22
|
-
# @yieldparam [Array<String>] modified the list of modified files
|
23
|
-
# @yieldparam [Array<String>] added the list of added files
|
24
|
-
# @yieldparam [Array<String>] removed the list of removed files
|
25
|
-
#
|
26
|
-
def initialize(directory, options = {}, &block)
|
27
|
-
@block = block
|
28
|
-
@directory = Pathname.new(directory).realpath.to_s
|
29
|
-
@directory_record = DirectoryRecord.new(@directory)
|
30
|
-
@use_relative_paths = DEFAULT_TO_RELATIVE_PATHS
|
31
|
-
|
32
|
-
@use_relative_paths = options.delete(:relative_paths) if options[:relative_paths]
|
33
|
-
@directory_record.ignore(*options.delete(:ignore)) if options[:ignore]
|
34
|
-
@directory_record.filter(*options.delete(:filter)) if options[:filter]
|
35
|
-
|
36
|
-
@adapter_options = options
|
37
|
-
end
|
38
|
-
|
39
|
-
# Starts the listener by initializing the adapter and building
|
40
|
-
# the directory record concurrently, then it starts the adapter to watch
|
41
|
-
# for changes.
|
42
|
-
#
|
43
|
-
# @param [Boolean] blocking whether or not to block the current thread after starting
|
44
|
-
#
|
45
|
-
def start(blocking = true)
|
46
|
-
t = Thread.new { @directory_record.build }
|
47
|
-
@adapter = initialize_adapter
|
48
|
-
t.join
|
49
|
-
@adapter.start(blocking)
|
50
|
-
end
|
51
|
-
|
52
|
-
# Stops the listener.
|
53
|
-
#
|
54
|
-
def stop
|
55
|
-
@adapter.stop
|
56
|
-
end
|
57
|
-
|
58
|
-
# Pauses the listener.
|
59
|
-
#
|
60
|
-
# @return [Listen::Listener] the listener
|
61
|
-
#
|
62
|
-
def pause
|
63
|
-
@adapter.paused = true
|
64
|
-
self
|
65
|
-
end
|
66
|
-
|
67
|
-
# Unpauses the listener.
|
68
|
-
#
|
69
|
-
# @return [Listen::Listener] the listener
|
70
|
-
#
|
71
|
-
def unpause
|
72
|
-
@directory_record.build
|
73
|
-
@adapter.paused = false
|
74
|
-
self
|
75
|
-
end
|
76
|
-
|
77
|
-
# Returns whether the listener is paused or not.
|
78
|
-
#
|
79
|
-
# @return [Boolean] adapter paused status
|
80
|
-
#
|
81
|
-
def paused?
|
82
|
-
!!@adapter && @adapter.paused == true
|
83
|
-
end
|
84
|
-
|
85
|
-
# Adds ignoring patterns to the listener.
|
86
|
-
#
|
87
|
-
# @param (see Listen::DirectoryRecord#ignore)
|
88
|
-
#
|
89
|
-
# @return [Listen::Listener] the listener
|
90
|
-
#
|
91
|
-
def ignore(*regexps)
|
92
|
-
@directory_record.ignore(*regexps)
|
93
|
-
self
|
94
|
-
end
|
95
|
-
|
96
|
-
# Adds filtering patterns to the listener.
|
97
|
-
#
|
98
|
-
# @param (see Listen::DirectoryRecord#filter)
|
99
|
-
#
|
100
|
-
# @return [Listen::Listener] the listener
|
101
|
-
#
|
102
|
-
def filter(*regexps)
|
103
|
-
@directory_record.filter(*regexps)
|
104
|
-
self
|
105
|
-
end
|
106
|
-
|
107
|
-
# Sets the latency for the adapter. This is a helper method
|
108
|
-
# to simplify changing the latency directly from the listener.
|
109
|
-
#
|
110
|
-
# @example Wait 0.5 seconds each time before checking changes
|
111
|
-
# latency 0.5
|
112
|
-
#
|
113
|
-
# @param [Float] seconds the amount of delay, in seconds
|
114
|
-
#
|
115
|
-
# @return [Listen::Listener] the listener
|
116
|
-
#
|
117
|
-
def latency(seconds)
|
118
|
-
@adapter_options[:latency] = seconds
|
119
|
-
self
|
120
|
-
end
|
121
|
-
|
122
|
-
# Sets whether the use of the polling adapter
|
123
|
-
# should be forced or not.
|
124
|
-
#
|
125
|
-
# @example Forcing the use of the polling adapter
|
126
|
-
# force_polling true
|
127
|
-
#
|
128
|
-
# @param [Boolean] value whether to force the polling adapter or not
|
129
|
-
#
|
130
|
-
# @return [Listen::Listener] the listener
|
131
|
-
#
|
132
|
-
def force_polling(value)
|
133
|
-
@adapter_options[:force_polling] = value
|
134
|
-
self
|
135
|
-
end
|
136
|
-
|
137
|
-
# Sets whether the paths in the callback should be
|
138
|
-
# relative or absolute.
|
139
|
-
#
|
140
|
-
# @example Enabling relative paths in the callback
|
141
|
-
# relative_paths true
|
142
|
-
#
|
143
|
-
# @param [Boolean] value whether to enable relative paths in the callback or not
|
144
|
-
#
|
145
|
-
# @return [Listen::Listener] the listener
|
146
|
-
#
|
147
|
-
def relative_paths(value)
|
148
|
-
@use_relative_paths = value
|
149
|
-
self
|
150
|
-
end
|
151
|
-
|
152
|
-
# Defines a custom polling fallback message of disable it.
|
153
|
-
#
|
154
|
-
# @example Disabling the polling fallback message
|
155
|
-
# polling_fallback_message false
|
156
|
-
#
|
157
|
-
# @param [String, Boolean] value to change polling fallback message or remove it
|
158
|
-
#
|
159
|
-
# @return [Listen::Listener] the listener
|
160
|
-
#
|
161
|
-
def polling_fallback_message(value)
|
162
|
-
@adapter_options[:polling_fallback_message] = value
|
163
|
-
self
|
164
|
-
end
|
165
|
-
|
166
|
-
# Sets the callback that gets called on changes.
|
167
|
-
#
|
168
|
-
# @example Assign a callback to be called on changes
|
169
|
-
# callback = lambda { |modified, added, removed| ... }
|
170
|
-
# change &callback
|
171
|
-
#
|
172
|
-
# @param [Proc] block the callback proc
|
173
|
-
#
|
174
|
-
# @return [Listen::Listener] the listener
|
175
|
-
#
|
176
|
-
def change(&block) # modified, added, removed
|
177
|
-
@block = block
|
178
|
-
self
|
179
|
-
end
|
180
|
-
|
181
|
-
# Runs the callback passing it the changes if there are any.
|
182
|
-
#
|
183
|
-
# @param (see Listen::DirectoryRecord#fetch_changes)
|
184
|
-
#
|
185
|
-
def on_change(directories, options = {})
|
186
|
-
changes = @directory_record.fetch_changes(directories, options.merge(
|
187
|
-
:relative_paths => @use_relative_paths
|
188
|
-
))
|
189
|
-
unless changes.values.all? { |paths| paths.empty? }
|
190
|
-
@block.call(changes[:modified],changes[:added],changes[:removed])
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
private
|
195
|
-
|
196
|
-
# Initializes an adapter passing it the callback and adapters' options.
|
197
|
-
#
|
198
|
-
def initialize_adapter
|
199
|
-
callback = lambda { |changed_dirs, options| self.on_change(changed_dirs, options) }
|
200
|
-
Adapter.select_and_initialize(@directory, @adapter_options, &callback)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Listen
|
4
|
+
class Listener
|
5
|
+
attr_reader :directory, :directory_record, :adapter
|
6
|
+
|
7
|
+
# The default value for using relative paths in the callback.
|
8
|
+
DEFAULT_TO_RELATIVE_PATHS = false
|
9
|
+
|
10
|
+
# Initializes the directory listener.
|
11
|
+
#
|
12
|
+
# @param [String] directory the directory to listen to
|
13
|
+
# @param [Hash] options the listen options
|
14
|
+
# @option options [Regexp] ignore a pattern for ignoring paths
|
15
|
+
# @option options [Regexp] filter a pattern for filtering paths
|
16
|
+
# @option options [Float] latency the delay between checking for changes in seconds
|
17
|
+
# @option options [Boolean] relative_paths whether or not to use relative-paths in the callback
|
18
|
+
# @option options [Boolean] force_polling whether to force the polling adapter or not
|
19
|
+
# @option options [String, Boolean] polling_fallback_message to change polling fallback message or remove it
|
20
|
+
#
|
21
|
+
# @yield [modified, added, removed] the changed files
|
22
|
+
# @yieldparam [Array<String>] modified the list of modified files
|
23
|
+
# @yieldparam [Array<String>] added the list of added files
|
24
|
+
# @yieldparam [Array<String>] removed the list of removed files
|
25
|
+
#
|
26
|
+
def initialize(directory, options = {}, &block)
|
27
|
+
@block = block
|
28
|
+
@directory = Pathname.new(directory).realpath.to_s
|
29
|
+
@directory_record = DirectoryRecord.new(@directory)
|
30
|
+
@use_relative_paths = DEFAULT_TO_RELATIVE_PATHS
|
31
|
+
|
32
|
+
@use_relative_paths = options.delete(:relative_paths) if options[:relative_paths]
|
33
|
+
@directory_record.ignore(*options.delete(:ignore)) if options[:ignore]
|
34
|
+
@directory_record.filter(*options.delete(:filter)) if options[:filter]
|
35
|
+
|
36
|
+
@adapter_options = options
|
37
|
+
end
|
38
|
+
|
39
|
+
# Starts the listener by initializing the adapter and building
|
40
|
+
# the directory record concurrently, then it starts the adapter to watch
|
41
|
+
# for changes.
|
42
|
+
#
|
43
|
+
# @param [Boolean] blocking whether or not to block the current thread after starting
|
44
|
+
#
|
45
|
+
def start(blocking = true)
|
46
|
+
t = Thread.new { @directory_record.build }
|
47
|
+
@adapter = initialize_adapter
|
48
|
+
t.join
|
49
|
+
@adapter.start(blocking)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Stops the listener.
|
53
|
+
#
|
54
|
+
def stop
|
55
|
+
@adapter.stop
|
56
|
+
end
|
57
|
+
|
58
|
+
# Pauses the listener.
|
59
|
+
#
|
60
|
+
# @return [Listen::Listener] the listener
|
61
|
+
#
|
62
|
+
def pause
|
63
|
+
@adapter.paused = true
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Unpauses the listener.
|
68
|
+
#
|
69
|
+
# @return [Listen::Listener] the listener
|
70
|
+
#
|
71
|
+
def unpause
|
72
|
+
@directory_record.build
|
73
|
+
@adapter.paused = false
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns whether the listener is paused or not.
|
78
|
+
#
|
79
|
+
# @return [Boolean] adapter paused status
|
80
|
+
#
|
81
|
+
def paused?
|
82
|
+
!!@adapter && @adapter.paused == true
|
83
|
+
end
|
84
|
+
|
85
|
+
# Adds ignoring patterns to the listener.
|
86
|
+
#
|
87
|
+
# @param (see Listen::DirectoryRecord#ignore)
|
88
|
+
#
|
89
|
+
# @return [Listen::Listener] the listener
|
90
|
+
#
|
91
|
+
def ignore(*regexps)
|
92
|
+
@directory_record.ignore(*regexps)
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# Adds filtering patterns to the listener.
|
97
|
+
#
|
98
|
+
# @param (see Listen::DirectoryRecord#filter)
|
99
|
+
#
|
100
|
+
# @return [Listen::Listener] the listener
|
101
|
+
#
|
102
|
+
def filter(*regexps)
|
103
|
+
@directory_record.filter(*regexps)
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
# Sets the latency for the adapter. This is a helper method
|
108
|
+
# to simplify changing the latency directly from the listener.
|
109
|
+
#
|
110
|
+
# @example Wait 0.5 seconds each time before checking changes
|
111
|
+
# latency 0.5
|
112
|
+
#
|
113
|
+
# @param [Float] seconds the amount of delay, in seconds
|
114
|
+
#
|
115
|
+
# @return [Listen::Listener] the listener
|
116
|
+
#
|
117
|
+
def latency(seconds)
|
118
|
+
@adapter_options[:latency] = seconds
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
# Sets whether the use of the polling adapter
|
123
|
+
# should be forced or not.
|
124
|
+
#
|
125
|
+
# @example Forcing the use of the polling adapter
|
126
|
+
# force_polling true
|
127
|
+
#
|
128
|
+
# @param [Boolean] value whether to force the polling adapter or not
|
129
|
+
#
|
130
|
+
# @return [Listen::Listener] the listener
|
131
|
+
#
|
132
|
+
def force_polling(value)
|
133
|
+
@adapter_options[:force_polling] = value
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
# Sets whether the paths in the callback should be
|
138
|
+
# relative or absolute.
|
139
|
+
#
|
140
|
+
# @example Enabling relative paths in the callback
|
141
|
+
# relative_paths true
|
142
|
+
#
|
143
|
+
# @param [Boolean] value whether to enable relative paths in the callback or not
|
144
|
+
#
|
145
|
+
# @return [Listen::Listener] the listener
|
146
|
+
#
|
147
|
+
def relative_paths(value)
|
148
|
+
@use_relative_paths = value
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Defines a custom polling fallback message of disable it.
|
153
|
+
#
|
154
|
+
# @example Disabling the polling fallback message
|
155
|
+
# polling_fallback_message false
|
156
|
+
#
|
157
|
+
# @param [String, Boolean] value to change polling fallback message or remove it
|
158
|
+
#
|
159
|
+
# @return [Listen::Listener] the listener
|
160
|
+
#
|
161
|
+
def polling_fallback_message(value)
|
162
|
+
@adapter_options[:polling_fallback_message] = value
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
# Sets the callback that gets called on changes.
|
167
|
+
#
|
168
|
+
# @example Assign a callback to be called on changes
|
169
|
+
# callback = lambda { |modified, added, removed| ... }
|
170
|
+
# change &callback
|
171
|
+
#
|
172
|
+
# @param [Proc] block the callback proc
|
173
|
+
#
|
174
|
+
# @return [Listen::Listener] the listener
|
175
|
+
#
|
176
|
+
def change(&block) # modified, added, removed
|
177
|
+
@block = block
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
# Runs the callback passing it the changes if there are any.
|
182
|
+
#
|
183
|
+
# @param (see Listen::DirectoryRecord#fetch_changes)
|
184
|
+
#
|
185
|
+
def on_change(directories, options = {})
|
186
|
+
changes = @directory_record.fetch_changes(directories, options.merge(
|
187
|
+
:relative_paths => @use_relative_paths
|
188
|
+
))
|
189
|
+
unless changes.values.all? { |paths| paths.empty? }
|
190
|
+
@block.call(changes[:modified],changes[:added],changes[:removed])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
# Initializes an adapter passing it the callback and adapters' options.
|
197
|
+
#
|
198
|
+
def initialize_adapter
|
199
|
+
callback = lambda { |changed_dirs, options| self.on_change(changed_dirs, options) }
|
200
|
+
Adapter.select_and_initialize(@directory, @adapter_options, &callback)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -1,121 +1,121 @@
|
|
1
|
-
module Listen
|
2
|
-
class MultiListener < Listener
|
3
|
-
attr_reader :directories, :directories_records, :adapter
|
4
|
-
|
5
|
-
# Initializes the multiple directories listener.
|
6
|
-
#
|
7
|
-
# @param [String] directories the directories to listen to
|
8
|
-
# @param [Hash] options the listen options
|
9
|
-
# @option options [Regexp] ignore a pattern for ignoring paths
|
10
|
-
# @option options [Regexp] filter a pattern for filtering paths
|
11
|
-
# @option options [Float] latency the delay between checking for changes in seconds
|
12
|
-
# @option options [Boolean] force_polling whether to force the polling adapter or not
|
13
|
-
# @option options [String, Boolean] polling_fallback_message to change polling fallback message or remove it
|
14
|
-
#
|
15
|
-
# @yield [modified, added, removed] the changed files
|
16
|
-
# @yieldparam [Array<String>] modified the list of modified files
|
17
|
-
# @yieldparam [Array<String>] added the list of added files
|
18
|
-
# @yieldparam [Array<String>] removed the list of removed files
|
19
|
-
#
|
20
|
-
def initialize(*args, &block)
|
21
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
22
|
-
directories = args
|
23
|
-
|
24
|
-
@block = block
|
25
|
-
@directories = directories.map { |d| Pathname.new(d).realpath.to_s }
|
26
|
-
@directories_records = @directories.map { |d| DirectoryRecord.new(d) }
|
27
|
-
|
28
|
-
ignore(*options.delete(:ignore)) if options[:ignore]
|
29
|
-
filter(*options.delete(:filter)) if options[:filter]
|
30
|
-
|
31
|
-
@adapter_options = options
|
32
|
-
end
|
33
|
-
|
34
|
-
# Starts the listener by initializing the adapter and building
|
35
|
-
# the directory record concurrently, then it starts the adapter to watch
|
36
|
-
# for changes.
|
37
|
-
#
|
38
|
-
# @param [Boolean] blocking whether or not to block the current thread after starting
|
39
|
-
#
|
40
|
-
def start(blocking = true)
|
41
|
-
t = Thread.new { @directories_records.each { |r| r.build } }
|
42
|
-
@adapter = initialize_adapter
|
43
|
-
t.join
|
44
|
-
@adapter.start(blocking)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Unpauses the listener.
|
48
|
-
#
|
49
|
-
# @return [Listen::Listener] the listener
|
50
|
-
#
|
51
|
-
def unpause
|
52
|
-
@directories_records.each { |r| r.build }
|
53
|
-
@adapter.paused = false
|
54
|
-
self
|
55
|
-
end
|
56
|
-
|
57
|
-
# Adds ignored paths to the listener.
|
58
|
-
#
|
59
|
-
# @param (see Listen::DirectoryRecord#ignore)
|
60
|
-
#
|
61
|
-
# @return [Listen::Listener] the listener
|
62
|
-
#
|
63
|
-
def ignore(*paths)
|
64
|
-
@directories_records.each { |r| r.ignore(*paths) }
|
65
|
-
self
|
66
|
-
end
|
67
|
-
|
68
|
-
# Adds file filters to the listener.
|
69
|
-
#
|
70
|
-
# @param (see Listen::DirectoryRecord#filter)
|
71
|
-
#
|
72
|
-
# @return [Listen::Listener] the listener
|
73
|
-
#
|
74
|
-
def filter(*regexps)
|
75
|
-
@directories_records.each { |r| r.filter(*regexps) }
|
76
|
-
self
|
77
|
-
end
|
78
|
-
|
79
|
-
# Runs the callback passing it the changes if there are any.
|
80
|
-
#
|
81
|
-
# @param (see Listen::DirectoryRecord#fetch_changes)
|
82
|
-
#
|
83
|
-
def on_change(directories_to_search, options = {})
|
84
|
-
changes = fetch_records_changes(directories_to_search, options)
|
85
|
-
unless changes.values.all? { |paths| paths.empty? }
|
86
|
-
@block.call(changes[:modified],changes[:added],changes[:removed])
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
# Initializes an adapter passing it the callback and adapters' options.
|
93
|
-
#
|
94
|
-
def initialize_adapter
|
95
|
-
callback = lambda { |changed_dirs, options| self.on_change(changed_dirs, options) }
|
96
|
-
Adapter.select_and_initialize(@directories, @adapter_options, &callback)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Returns the sum of all the changes to the directories records
|
100
|
-
#
|
101
|
-
# @param (see Listen::DirectoryRecord#fetch_changes)
|
102
|
-
#
|
103
|
-
# @return [Hash] the changes
|
104
|
-
#
|
105
|
-
def fetch_records_changes(directories_to_search, options)
|
106
|
-
@directories_records.inject({}) do |h, r|
|
107
|
-
# directory records skips paths outside their range, so passing the
|
108
|
-
# whole `directories` array is not a problem.
|
109
|
-
record_changes = r.fetch_changes(directories_to_search, options.merge(:relative_paths => DEFAULT_TO_RELATIVE_PATHS))
|
110
|
-
|
111
|
-
if h.empty?
|
112
|
-
h.merge!(record_changes)
|
113
|
-
else
|
114
|
-
h.each { |k, v| h[k] += record_changes[k] }
|
115
|
-
end
|
116
|
-
|
117
|
-
h
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
1
|
+
module Listen
|
2
|
+
class MultiListener < Listener
|
3
|
+
attr_reader :directories, :directories_records, :adapter
|
4
|
+
|
5
|
+
# Initializes the multiple directories listener.
|
6
|
+
#
|
7
|
+
# @param [String] directories the directories to listen to
|
8
|
+
# @param [Hash] options the listen options
|
9
|
+
# @option options [Regexp] ignore a pattern for ignoring paths
|
10
|
+
# @option options [Regexp] filter a pattern for filtering paths
|
11
|
+
# @option options [Float] latency the delay between checking for changes in seconds
|
12
|
+
# @option options [Boolean] force_polling whether to force the polling adapter or not
|
13
|
+
# @option options [String, Boolean] polling_fallback_message to change polling fallback message or remove it
|
14
|
+
#
|
15
|
+
# @yield [modified, added, removed] the changed files
|
16
|
+
# @yieldparam [Array<String>] modified the list of modified files
|
17
|
+
# @yieldparam [Array<String>] added the list of added files
|
18
|
+
# @yieldparam [Array<String>] removed the list of removed files
|
19
|
+
#
|
20
|
+
def initialize(*args, &block)
|
21
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
22
|
+
directories = args
|
23
|
+
|
24
|
+
@block = block
|
25
|
+
@directories = directories.map { |d| Pathname.new(d).realpath.to_s }
|
26
|
+
@directories_records = @directories.map { |d| DirectoryRecord.new(d) }
|
27
|
+
|
28
|
+
ignore(*options.delete(:ignore)) if options[:ignore]
|
29
|
+
filter(*options.delete(:filter)) if options[:filter]
|
30
|
+
|
31
|
+
@adapter_options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
# Starts the listener by initializing the adapter and building
|
35
|
+
# the directory record concurrently, then it starts the adapter to watch
|
36
|
+
# for changes.
|
37
|
+
#
|
38
|
+
# @param [Boolean] blocking whether or not to block the current thread after starting
|
39
|
+
#
|
40
|
+
def start(blocking = true)
|
41
|
+
t = Thread.new { @directories_records.each { |r| r.build } }
|
42
|
+
@adapter = initialize_adapter
|
43
|
+
t.join
|
44
|
+
@adapter.start(blocking)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Unpauses the listener.
|
48
|
+
#
|
49
|
+
# @return [Listen::Listener] the listener
|
50
|
+
#
|
51
|
+
def unpause
|
52
|
+
@directories_records.each { |r| r.build }
|
53
|
+
@adapter.paused = false
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds ignored paths to the listener.
|
58
|
+
#
|
59
|
+
# @param (see Listen::DirectoryRecord#ignore)
|
60
|
+
#
|
61
|
+
# @return [Listen::Listener] the listener
|
62
|
+
#
|
63
|
+
def ignore(*paths)
|
64
|
+
@directories_records.each { |r| r.ignore(*paths) }
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Adds file filters to the listener.
|
69
|
+
#
|
70
|
+
# @param (see Listen::DirectoryRecord#filter)
|
71
|
+
#
|
72
|
+
# @return [Listen::Listener] the listener
|
73
|
+
#
|
74
|
+
def filter(*regexps)
|
75
|
+
@directories_records.each { |r| r.filter(*regexps) }
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Runs the callback passing it the changes if there are any.
|
80
|
+
#
|
81
|
+
# @param (see Listen::DirectoryRecord#fetch_changes)
|
82
|
+
#
|
83
|
+
def on_change(directories_to_search, options = {})
|
84
|
+
changes = fetch_records_changes(directories_to_search, options)
|
85
|
+
unless changes.values.all? { |paths| paths.empty? }
|
86
|
+
@block.call(changes[:modified],changes[:added],changes[:removed])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Initializes an adapter passing it the callback and adapters' options.
|
93
|
+
#
|
94
|
+
def initialize_adapter
|
95
|
+
callback = lambda { |changed_dirs, options| self.on_change(changed_dirs, options) }
|
96
|
+
Adapter.select_and_initialize(@directories, @adapter_options, &callback)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the sum of all the changes to the directories records
|
100
|
+
#
|
101
|
+
# @param (see Listen::DirectoryRecord#fetch_changes)
|
102
|
+
#
|
103
|
+
# @return [Hash] the changes
|
104
|
+
#
|
105
|
+
def fetch_records_changes(directories_to_search, options)
|
106
|
+
@directories_records.inject({}) do |h, r|
|
107
|
+
# directory records skips paths outside their range, so passing the
|
108
|
+
# whole `directories` array is not a problem.
|
109
|
+
record_changes = r.fetch_changes(directories_to_search, options.merge(:relative_paths => DEFAULT_TO_RELATIVE_PATHS))
|
110
|
+
|
111
|
+
if h.empty?
|
112
|
+
h.merge!(record_changes)
|
113
|
+
else
|
114
|
+
h.each { |k, v| h[k] += record_changes[k] }
|
115
|
+
end
|
116
|
+
|
117
|
+
h
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|