sass 3.3.0 → 3.4.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 +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +58 -50
- data/Rakefile +1 -4
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass/cache_stores/filesystem.rb +6 -2
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +37 -46
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +424 -0
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/features.rb +7 -0
- data/lib/sass/importers/base.rb +7 -2
- data/lib/sass/importers/filesystem.rb +9 -25
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +200 -83
- data/lib/sass/plugin/staleness_checker.rb +1 -1
- data/lib/sass/plugin.rb +3 -3
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +622 -268
- data/lib/sass/script/lexer.rb +99 -34
- data/lib/sass/script/parser.rb +24 -23
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +20 -2
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +1 -1
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/value/base.rb +7 -5
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +39 -21
- data/lib/sass/script/value/helpers.rb +107 -0
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +62 -14
- data/lib/sass/script/value/string.rb +59 -11
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +190 -328
- data/lib/sass/scss/rx.rb +15 -6
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +92 -13
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +94 -24
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +97 -33
- data/lib/sass/selector.rb +57 -194
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +26 -12
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +2 -3
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/directive_node.rb +8 -2
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +4 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/prop_node.rb +1 -1
- data/lib/sass/tree/rule_node.rb +12 -7
- data/lib/sass/tree/visitors/check_nesting.rb +38 -10
- data/lib/sass/tree/visitors/convert.rb +16 -18
- data/lib/sass/tree/visitors/cssize.rb +29 -29
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +45 -33
- data/lib/sass/tree/visitors/set_options.rb +14 -0
- data/lib/sass/tree/visitors/to_css.rb +15 -14
- data/lib/sass/util/subset_map.rb +1 -1
- data/lib/sass/util.rb +222 -99
- data/lib/sass/version.rb +5 -5
- data/lib/sass.rb +0 -5
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +19 -10
- data/test/sass/conversion_test.rb +58 -1
- data/test/sass/css2sass_test.rb +23 -4
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +136 -199
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +236 -19
- data/test/sass/functions_test.rb +295 -253
- data/test/sass/importer_test.rb +31 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +14 -13
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +10 -7
- data/test/sass/script_test.rb +288 -74
- data/test/sass/scss/css_test.rb +141 -24
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +457 -18
- data/test/sass/source_map_test.rb +115 -25
- data/test/sass/superselector_test.rb +191 -0
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/normalized_map_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +31 -1
- data/test/sass/value_helpers_test.rb +5 -7
- data/test/test_helper.rb +2 -2
- data/vendor/listen/CHANGELOG.md +1 -228
- data/vendor/listen/Gemfile +5 -15
- data/vendor/listen/README.md +111 -77
- data/vendor/listen/Rakefile +0 -42
- data/vendor/listen/lib/listen/adapter.rb +195 -82
- data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
- data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
- data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
- data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
- data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
- data/vendor/listen/lib/listen/directory_record.rb +96 -61
- data/vendor/listen/lib/listen/listener.rb +135 -37
- data/vendor/listen/lib/listen/turnstile.rb +9 -5
- data/vendor/listen/lib/listen/version.rb +1 -1
- data/vendor/listen/lib/listen.rb +33 -19
- data/vendor/listen/listen.gemspec +6 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
- data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
- data/vendor/listen/spec/listen/listener_spec.rb +128 -39
- data/vendor/listen/spec/listen_spec.rb +15 -21
- data/vendor/listen/spec/spec_helper.rb +4 -0
- data/vendor/listen/spec/support/adapter_helper.rb +52 -15
- data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
- data/vendor/listen/spec/support/listeners_helper.rb +30 -7
- metadata +25 -22
- data/ext/mkrf_conf.rb +0 -27
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
- data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
- data/vendor/listen/lib/listen/multi_listener.rb +0 -143
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
- data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -2,14 +2,18 @@ require 'pathname'
|
|
2
2
|
|
3
3
|
module Listen
|
4
4
|
class Listener
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :directories, :directories_records, :block, :adapter, :adapter_options, :use_relative_paths
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
BLOCKING_PARAMETER_DEPRECATION_MESSAGE = <<-EOS.gsub(/^\s*/, '')
|
8
|
+
The blocking parameter of Listen::Listener#start is deprecated.\n
|
9
|
+
Please use Listen::Adapter#start for a non-blocking listener and Listen::Listener#start! for a blocking one.
|
10
|
+
EOS
|
9
11
|
|
10
|
-
|
12
|
+
RELATIVE_PATHS_WITH_MULTIPLE_DIRECTORIES_WARNING_MESSAGE = "The relative_paths option doesn't work when listening to multiple diretories."
|
13
|
+
|
14
|
+
# Initializes the directories listener.
|
11
15
|
#
|
12
|
-
# @param [String] directory the
|
16
|
+
# @param [String] directory the directories to listen to
|
13
17
|
# @param [Hash] options the listen options
|
14
18
|
# @option options [Regexp] ignore a pattern for ignoring paths
|
15
19
|
# @option options [Regexp] filter a pattern for filtering paths
|
@@ -17,42 +21,55 @@ module Listen
|
|
17
21
|
# @option options [Boolean] relative_paths whether or not to use relative-paths in the callback
|
18
22
|
# @option options [Boolean] force_polling whether to force the polling adapter or not
|
19
23
|
# @option options [String, Boolean] polling_fallback_message to change polling fallback message or remove it
|
24
|
+
# @option options [Class] force_adapter force the use of this adapter class, skipping usual adapter selection
|
20
25
|
#
|
21
26
|
# @yield [modified, added, removed] the changed files
|
22
27
|
# @yieldparam [Array<String>] modified the list of modified files
|
23
28
|
# @yieldparam [Array<String>] added the list of added files
|
24
29
|
# @yieldparam [Array<String>] removed the list of removed files
|
25
30
|
#
|
26
|
-
def initialize(
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
def initialize(*args, &block)
|
32
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
33
|
+
directories = args.flatten
|
34
|
+
initialize_directories_and_directories_records(directories)
|
35
|
+
initialize_relative_paths_usage(options)
|
36
|
+
@block = block
|
31
37
|
|
32
|
-
|
33
|
-
|
34
|
-
@directory_record.filter(*options.delete(:filter)) if options[:filter]
|
38
|
+
ignore(*options.delete(:ignore))
|
39
|
+
filter(*options.delete(:filter))
|
35
40
|
|
36
41
|
@adapter_options = options
|
37
42
|
end
|
38
43
|
|
39
44
|
# Starts the listener by initializing the adapter and building
|
40
45
|
# the directory record concurrently, then it starts the adapter to watch
|
41
|
-
# for changes.
|
46
|
+
# for changes. The current thread is not blocked after starting.
|
42
47
|
#
|
43
|
-
# @
|
48
|
+
# @see Listen::Listener#start!
|
44
49
|
#
|
45
|
-
def start(
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
def start(deprecated_blocking = nil)
|
51
|
+
Kernel.warn "[Listen warning]:\n#{BLOCKING_PARAMETER_DEPRECATION_MESSAGE}" unless deprecated_blocking.nil?
|
52
|
+
setup
|
53
|
+
adapter.start
|
54
|
+
end
|
55
|
+
|
56
|
+
# Starts the listener by initializing the adapter and building
|
57
|
+
# the directory record concurrently, then it starts the adapter to watch
|
58
|
+
# for changes. The current thread is blocked after starting.
|
59
|
+
#
|
60
|
+
# @see Listen::Listener#start
|
61
|
+
#
|
62
|
+
# @since 1.0.0
|
63
|
+
#
|
64
|
+
def start!
|
65
|
+
setup
|
66
|
+
adapter.start!
|
50
67
|
end
|
51
68
|
|
52
69
|
# Stops the listener.
|
53
70
|
#
|
54
71
|
def stop
|
55
|
-
|
72
|
+
adapter && adapter.stop
|
56
73
|
end
|
57
74
|
|
58
75
|
# Pauses the listener.
|
@@ -60,7 +77,7 @@ module Listen
|
|
60
77
|
# @return [Listen::Listener] the listener
|
61
78
|
#
|
62
79
|
def pause
|
63
|
-
|
80
|
+
adapter.pause
|
64
81
|
self
|
65
82
|
end
|
66
83
|
|
@@ -69,8 +86,8 @@ module Listen
|
|
69
86
|
# @return [Listen::Listener] the listener
|
70
87
|
#
|
71
88
|
def unpause
|
72
|
-
|
73
|
-
|
89
|
+
build_directories_records
|
90
|
+
adapter.unpause
|
74
91
|
self
|
75
92
|
end
|
76
93
|
|
@@ -79,7 +96,7 @@ module Listen
|
|
79
96
|
# @return [Boolean] adapter paused status
|
80
97
|
#
|
81
98
|
def paused?
|
82
|
-
|
99
|
+
!!adapter && adapter.paused?
|
83
100
|
end
|
84
101
|
|
85
102
|
# Adds ignoring patterns to the listener.
|
@@ -88,8 +105,10 @@ module Listen
|
|
88
105
|
#
|
89
106
|
# @return [Listen::Listener] the listener
|
90
107
|
#
|
108
|
+
# @see Listen::DirectoryRecord#ignore
|
109
|
+
#
|
91
110
|
def ignore(*regexps)
|
92
|
-
|
111
|
+
directories_records.each { |r| r.ignore(*regexps) }
|
93
112
|
self
|
94
113
|
end
|
95
114
|
|
@@ -99,8 +118,10 @@ module Listen
|
|
99
118
|
#
|
100
119
|
# @return [Listen::Listener] the listener
|
101
120
|
#
|
121
|
+
# @see Listen::DirectoryRecord#ignore!
|
122
|
+
#
|
102
123
|
def ignore!(*regexps)
|
103
|
-
|
124
|
+
directories_records.each { |r| r.ignore!(*regexps) }
|
104
125
|
self
|
105
126
|
end
|
106
127
|
|
@@ -110,19 +131,23 @@ module Listen
|
|
110
131
|
#
|
111
132
|
# @return [Listen::Listener] the listener
|
112
133
|
#
|
134
|
+
# @see Listen::DirectoryRecord#filter
|
135
|
+
#
|
113
136
|
def filter(*regexps)
|
114
|
-
|
137
|
+
directories_records.each { |r| r.filter(*regexps) }
|
115
138
|
self
|
116
139
|
end
|
117
140
|
|
118
|
-
#
|
141
|
+
# Replaces filtering patterns in the listener.
|
119
142
|
#
|
120
143
|
# @param (see Listen::DirectoryRecord#filter!)
|
121
144
|
#
|
122
145
|
# @return [Listen::Listener] the listener
|
123
146
|
#
|
147
|
+
# @see Listen::DirectoryRecord#filter!
|
148
|
+
#
|
124
149
|
def filter!(*regexps)
|
125
|
-
|
150
|
+
directories_records.each { |r| r.filter!(*regexps) }
|
126
151
|
self
|
127
152
|
end
|
128
153
|
|
@@ -156,6 +181,21 @@ module Listen
|
|
156
181
|
self
|
157
182
|
end
|
158
183
|
|
184
|
+
# Sets whether to force the use of a particular adapter, rather than
|
185
|
+
# going through usual adapter selection process on start.
|
186
|
+
#
|
187
|
+
# @example Force use of Linux polling
|
188
|
+
# force_adapter Listen::Adapters::Linux
|
189
|
+
#
|
190
|
+
# @param [Class] adapter class to use for file system event notification.
|
191
|
+
#
|
192
|
+
# @return [Listen::Listener] the listener
|
193
|
+
#
|
194
|
+
def force_adapter(adapter_class)
|
195
|
+
@adapter_options[:force_adapter] = adapter_class
|
196
|
+
self
|
197
|
+
end
|
198
|
+
|
159
199
|
# Sets whether the paths in the callback should be
|
160
200
|
# relative or absolute.
|
161
201
|
#
|
@@ -171,7 +211,7 @@ module Listen
|
|
171
211
|
self
|
172
212
|
end
|
173
213
|
|
174
|
-
# Defines a custom polling fallback message
|
214
|
+
# Defines a custom polling fallback message or disable it.
|
175
215
|
#
|
176
216
|
# @example Disabling the polling fallback message
|
177
217
|
# polling_fallback_message false
|
@@ -204,22 +244,80 @@ module Listen
|
|
204
244
|
#
|
205
245
|
# @param (see Listen::DirectoryRecord#fetch_changes)
|
206
246
|
#
|
247
|
+
# @see Listen::DirectoryRecord#fetch_changes
|
248
|
+
#
|
207
249
|
def on_change(directories, options = {})
|
208
|
-
changes =
|
209
|
-
:relative_paths => @use_relative_paths
|
210
|
-
))
|
250
|
+
changes = fetch_records_changes(directories, options)
|
211
251
|
unless changes.values.all? { |paths| paths.empty? }
|
212
|
-
|
252
|
+
block.call(changes[:modified], changes[:added], changes[:removed])
|
213
253
|
end
|
254
|
+
rescue => ex
|
255
|
+
Kernel.warn "[Listen warning]: Change block raise an execption: #{$!}"
|
256
|
+
Kernel.warn "Backtrace:\n\t#{ex.backtrace.join("\n\t")}"
|
214
257
|
end
|
215
258
|
|
216
259
|
private
|
217
260
|
|
261
|
+
# Initializes the directories to watch as well as the directories records.
|
262
|
+
#
|
263
|
+
# @see Listen::DirectoryRecord
|
264
|
+
#
|
265
|
+
def initialize_directories_and_directories_records(directories)
|
266
|
+
@directories = directories.map { |d| Pathname.new(d).realpath.to_s }
|
267
|
+
@directories_records = directories.map { |d| DirectoryRecord.new(d) }
|
268
|
+
end
|
269
|
+
|
270
|
+
# Initializes whether or not using relative paths.
|
271
|
+
#
|
272
|
+
def initialize_relative_paths_usage(options)
|
273
|
+
if directories.size > 1 && options[:relative_paths]
|
274
|
+
Kernel.warn "[Listen warning]: #{RELATIVE_PATHS_WITH_MULTIPLE_DIRECTORIES_WARNING_MESSAGE}"
|
275
|
+
end
|
276
|
+
@use_relative_paths = directories.one? && options.delete(:relative_paths) { false }
|
277
|
+
end
|
278
|
+
|
279
|
+
# Build the directory record concurrently and initialize the adapter.
|
280
|
+
#
|
281
|
+
def setup
|
282
|
+
t = Thread.new { build_directories_records }
|
283
|
+
@adapter = initialize_adapter
|
284
|
+
t.join
|
285
|
+
end
|
286
|
+
|
218
287
|
# Initializes an adapter passing it the callback and adapters' options.
|
219
288
|
#
|
220
289
|
def initialize_adapter
|
221
|
-
callback = lambda { |
|
222
|
-
Adapter.select_and_initialize(
|
290
|
+
callback = lambda { |changed_directories, options| self.on_change(changed_directories, options) }
|
291
|
+
Adapter.select_and_initialize(directories, adapter_options, &callback)
|
223
292
|
end
|
293
|
+
|
294
|
+
# Build the watched directories' records.
|
295
|
+
#
|
296
|
+
def build_directories_records
|
297
|
+
directories_records.each { |r| r.build }
|
298
|
+
end
|
299
|
+
|
300
|
+
# Returns the sum of all the changes to the directories records
|
301
|
+
#
|
302
|
+
# @param (see Listen::DirectoryRecord#fetch_changes)
|
303
|
+
#
|
304
|
+
# @return [Hash] the changes
|
305
|
+
#
|
306
|
+
def fetch_records_changes(directories_to_search, options)
|
307
|
+
directories_records.inject({}) do |h, r|
|
308
|
+
# directory records skips paths outside their range, so passing the
|
309
|
+
# whole `directories` array is not a problem.
|
310
|
+
record_changes = r.fetch_changes(directories_to_search, options.merge(:relative_paths => use_relative_paths))
|
311
|
+
|
312
|
+
if h.empty?
|
313
|
+
h.merge!(record_changes)
|
314
|
+
else
|
315
|
+
h.each { |k, v| h[k] += record_changes[k] }
|
316
|
+
end
|
317
|
+
|
318
|
+
h
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
224
322
|
end
|
225
323
|
end
|
@@ -1,28 +1,32 @@
|
|
1
1
|
module Listen
|
2
|
+
|
2
3
|
# Allows two threads to wait on eachother.
|
3
4
|
#
|
4
5
|
# @note Only two threads can be used with this Turnstile
|
5
6
|
# because of the current implementation.
|
6
7
|
class Turnstile
|
8
|
+
attr_accessor :queue
|
7
9
|
|
8
10
|
# Initialize the turnstile.
|
9
11
|
#
|
10
12
|
def initialize
|
11
|
-
# Until
|
13
|
+
# Until Ruby offers semahpores, only queues can be used
|
12
14
|
# to implement a turnstile.
|
13
|
-
@
|
15
|
+
@queue = Queue.new
|
14
16
|
end
|
15
17
|
|
16
18
|
# Blocks the current thread until a signal is received.
|
17
19
|
#
|
18
20
|
def wait
|
19
|
-
|
21
|
+
queue.pop if queue.num_waiting == 0
|
20
22
|
end
|
21
23
|
|
22
|
-
# Unblocks the waiting thread if
|
24
|
+
# Unblocks the waiting thread if any.
|
23
25
|
#
|
24
26
|
def signal
|
25
|
-
|
27
|
+
queue.push(:dummy) if queue.num_waiting == 1
|
26
28
|
end
|
29
|
+
|
27
30
|
end
|
31
|
+
|
28
32
|
end
|
data/vendor/listen/lib/listen.rb
CHANGED
@@ -1,24 +1,20 @@
|
|
1
|
-
|
1
|
+
require 'listen/turnstile'
|
2
|
+
require 'listen/listener'
|
3
|
+
require 'listen/directory_record'
|
4
|
+
require 'listen/adapter'
|
2
5
|
|
3
|
-
|
4
|
-
autoload :Listener, 'listen/listener'
|
5
|
-
autoload :MultiListener, 'listen/multi_listener'
|
6
|
-
autoload :DirectoryRecord, 'listen/directory_record'
|
7
|
-
autoload :DependencyManager, 'listen/dependency_manager'
|
8
|
-
autoload :Adapter, 'listen/adapter'
|
6
|
+
module Listen
|
9
7
|
|
10
8
|
module Adapters
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
autoload :Windows, 'listen/adapters/windows'
|
15
|
-
autoload :Polling, 'listen/adapters/polling'
|
9
|
+
Adapter::ADAPTERS.each do |adapter|
|
10
|
+
require "listen/adapters/#{adapter.downcase}"
|
11
|
+
end
|
16
12
|
end
|
17
13
|
|
18
|
-
# Listens to
|
14
|
+
# Listens to file system modifications on a either single directory or multiple directories.
|
15
|
+
# When calling this method, the current thread is not blocked.
|
19
16
|
#
|
20
17
|
# @param (see Listen::Listener#new)
|
21
|
-
# @param (see Listen::MultiListener#new)
|
22
18
|
#
|
23
19
|
# @yield [modified, added, removed] the changed files
|
24
20
|
# @yieldparam [Array<String>] modified the list of modified files
|
@@ -28,13 +24,31 @@ module Listen
|
|
28
24
|
# @return [Listen::Listener] the file listener if no block given
|
29
25
|
#
|
30
26
|
def self.to(*args, &block)
|
31
|
-
listener =
|
32
|
-
Listener.new(*args, &block)
|
33
|
-
else
|
34
|
-
MultiListener.new(*args, &block)
|
35
|
-
end
|
27
|
+
listener = _init_listener(*args, &block)
|
36
28
|
|
37
29
|
block ? listener.start : listener
|
38
30
|
end
|
39
31
|
|
32
|
+
# Listens to file system modifications on a either single directory or multiple directories.
|
33
|
+
# When calling this method, the current thread is blocked.
|
34
|
+
#
|
35
|
+
# @param (see Listen::Listener#new)
|
36
|
+
#
|
37
|
+
# @yield [modified, added, removed] the changed files
|
38
|
+
# @yieldparam [Array<String>] modified the list of modified files
|
39
|
+
# @yieldparam [Array<String>] added the list of added files
|
40
|
+
# @yieldparam [Array<String>] removed the list of removed files
|
41
|
+
#
|
42
|
+
# @since 1.0.0
|
43
|
+
#
|
44
|
+
def self.to!(*args, &block)
|
45
|
+
_init_listener(*args, &block).start!
|
46
|
+
end
|
47
|
+
|
48
|
+
# @private
|
49
|
+
#
|
50
|
+
def self._init_listener(*args, &block)
|
51
|
+
Listener.new(*args, &block)
|
52
|
+
end
|
53
|
+
|
40
54
|
end
|
@@ -9,13 +9,19 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ['Thibaud Guillaume-Gentil', 'Maher Sallam']
|
10
10
|
s.email = ['thibaud@thibaud.me', 'maher@sallam.me']
|
11
11
|
s.homepage = 'https://github.com/guard/listen'
|
12
|
+
s.license = 'MIT'
|
12
13
|
s.summary = 'Listen to file modifications'
|
13
14
|
s.description = 'The Listen gem listens to file modifications and notifies you about the changes. Works everywhere!'
|
14
15
|
|
15
16
|
s.required_rubygems_version = '>= 1.3.6'
|
16
17
|
s.rubyforge_project = 'listen'
|
17
18
|
|
19
|
+
s.add_dependency 'rb-fsevent', '>= 0.9.3'
|
20
|
+
s.add_dependency 'rb-inotify', '>= 0.9'
|
21
|
+
s.add_dependency 'rb-kqueue', '>= 0.2'
|
22
|
+
|
18
23
|
s.add_development_dependency 'bundler'
|
24
|
+
s.add_development_dependency 'rspec'
|
19
25
|
|
20
26
|
s.files = Dir.glob('{lib}/**/*') + %w[CHANGELOG.md LICENSE README.md]
|
21
27
|
s.require_path = 'lib'
|
@@ -19,10 +19,23 @@ describe Listen::Adapter do
|
|
19
19
|
|
20
20
|
describe ".select_and_initialize" do
|
21
21
|
before do
|
22
|
-
Listen::
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
Listen::Adapter::OPTIMIZED_ADAPTERS.each do |adapter|
|
23
|
+
Listen::Adapters.const_get(adapter).stub(:usable_and_works?) { false }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with force_adapter option" do
|
28
|
+
it "returns an instance of the given adapter class" do
|
29
|
+
expected_adapter = Object.new
|
30
|
+
adapter_class = double('adapter_class')
|
31
|
+
options = {:force_adapter => adapter_class}
|
32
|
+
|
33
|
+
adapter_class.should_receive(:load_dependent_adapter)
|
34
|
+
adapter_class.should_receive(:new).with('dir', options) { expected_adapter }
|
35
|
+
|
36
|
+
adapter = described_class.select_and_initialize('dir', options)
|
37
|
+
adapter.should be(expected_adapter)
|
38
|
+
end
|
26
39
|
end
|
27
40
|
|
28
41
|
context "with no specific adapter usable" do
|
@@ -37,20 +50,6 @@ describe Listen::Adapter do
|
|
37
50
|
described_class.select_and_initialize('dir')
|
38
51
|
end
|
39
52
|
|
40
|
-
context 'when the dependencies of an adapter are not satisfied' do
|
41
|
-
before do
|
42
|
-
Listen::Adapters::Darwin.stub(:usable_and_works?).and_raise(Listen::DependencyManager::Error)
|
43
|
-
Listen::Adapters::Linux.stub(:usable_and_works?).and_raise(Listen::DependencyManager::Error)
|
44
|
-
Listen::Adapters::BSD.stub(:usable_and_works?).and_raise(Listen::DependencyManager::Error)
|
45
|
-
Listen::Adapters::Windows.stub(:usable_and_works?).and_raise(Listen::DependencyManager::Error)
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'invites the user to satisfy the dependencies of the adapter in the warning' do
|
49
|
-
Kernel.should_receive(:warn).with(/#{Listen::Adapter::MISSING_DEPENDENCY_MESSAGE}/)
|
50
|
-
described_class.select_and_initialize('dir')
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
53
|
context "with custom polling_fallback_message option" do
|
55
54
|
it "warns with the custom polling fallback message" do
|
56
55
|
Kernel.should_receive(:warn).with(/custom/)
|
@@ -66,83 +65,50 @@ describe Listen::Adapter do
|
|
66
65
|
end
|
67
66
|
end
|
68
67
|
|
69
|
-
|
70
|
-
|
68
|
+
Listen::Adapter::OPTIMIZED_ADAPTERS.each do |adapter|
|
69
|
+
adapter_class = Listen::Adapters.const_get(adapter)
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
described_class.select_and_initialize('dir')
|
75
|
-
end
|
71
|
+
context "on #{adapter}" do
|
72
|
+
before { adapter_class.stub(:usable_and_works?) { true } }
|
76
73
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
described_class.select_and_initialize('dir', :force_polling => true)
|
74
|
+
it "uses Listen::Adapters::#{adapter}" do
|
75
|
+
adapter_class.should_receive(:new).with('dir', {})
|
76
|
+
described_class.select_and_initialize('dir')
|
81
77
|
end
|
82
|
-
end
|
83
|
-
end
|
84
78
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
described_class.select_and_initialize('dir')
|
91
|
-
end
|
92
|
-
|
93
|
-
context 'when the use of the polling adapter is forced' do
|
94
|
-
it 'uses Listen::Adapters::Polling' do
|
95
|
-
Listen::Adapters::Polling.should_receive(:new).with('dir', {})
|
96
|
-
described_class.select_and_initialize('dir', :force_polling => true)
|
79
|
+
context 'when the use of the polling adapter is forced' do
|
80
|
+
it 'uses Listen::Adapters::Polling' do
|
81
|
+
Listen::Adapters::Polling.should_receive(:new).with('dir', {})
|
82
|
+
described_class.select_and_initialize('dir', :force_polling => true)
|
83
|
+
end
|
97
84
|
end
|
98
85
|
end
|
99
86
|
end
|
87
|
+
end
|
100
88
|
|
101
|
-
|
102
|
-
|
89
|
+
describe '.load_dependend_adapter' do
|
90
|
+
after(:each) { described_class.instance_variable_set('@loaded', nil) }
|
103
91
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
92
|
+
it 'returns true (success) even if the adapter_gem has already been required' do
|
93
|
+
described_class.stub(:adapter_gem => 'already_loaded_gem')
|
94
|
+
described_class.stub(:require => false)
|
108
95
|
|
109
|
-
|
110
|
-
it 'uses Listen::Adapters::Polling' do
|
111
|
-
Listen::Adapters::Polling.should_receive(:new).with('dir', {})
|
112
|
-
described_class.select_and_initialize('dir', :force_polling => true)
|
113
|
-
end
|
114
|
-
end
|
96
|
+
described_class.load_dependent_adapter.should be_true
|
115
97
|
end
|
116
98
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
Listen::Adapters::Windows.should_receive(:new).with('dir', {})
|
122
|
-
described_class.select_and_initialize('dir')
|
99
|
+
it 'returns false (failure) if the adapter_gem cannot be required' do
|
100
|
+
described_class.stub(:adapter_gem => 'unloadable_gem')
|
101
|
+
described_class.stub(:require) do
|
102
|
+
raise LoadError.new('no such file to load -- unloadable_gem')
|
123
103
|
end
|
124
104
|
|
125
|
-
|
126
|
-
it 'uses Listen::Adapters::Polling' do
|
127
|
-
Listen::Adapters::Polling.should_receive(:new).with('dir', {})
|
128
|
-
described_class.select_and_initialize('dir', :force_polling => true)
|
129
|
-
end
|
130
|
-
end
|
105
|
+
described_class.load_dependent_adapter.should be_false
|
131
106
|
end
|
132
107
|
end
|
133
108
|
|
134
|
-
|
135
|
-
|
136
|
-
|adapter_class|
|
109
|
+
Listen::Adapter::OPTIMIZED_ADAPTERS.each do |adapter|
|
110
|
+
adapter_class = Listen::Adapters.const_get(adapter)
|
137
111
|
if adapter_class.usable?
|
138
|
-
describe '.usable?' do
|
139
|
-
it 'checks the dependencies' do
|
140
|
-
adapter_class.should_receive(:load_depenencies)
|
141
|
-
adapter_class.should_receive(:dependencies_loaded?)
|
142
|
-
adapter_class.usable?
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
112
|
describe '.usable_and_works?' do
|
147
113
|
it 'checks if the adapter is usable' do
|
148
114
|
adapter_class.stub(:works?)
|
@@ -12,8 +12,8 @@ describe Listen::Adapters::Polling do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
describe '#poll' do
|
15
|
-
let(:listener) {
|
16
|
-
let(:callback) { lambda { |
|
15
|
+
let(:listener) { double(Listen::Listener) }
|
16
|
+
let(:callback) { lambda { |changed_directories, options| @called = true; listener.on_change(changed_directories, options) } }
|
17
17
|
|
18
18
|
after { subject.stop }
|
19
19
|
|
@@ -22,20 +22,20 @@ describe Listen::Adapters::Polling do
|
|
22
22
|
|
23
23
|
it 'calls listener.on_change' do
|
24
24
|
listener.should_receive(:on_change).at_least(1).times.with(['dir'], :recursive => true)
|
25
|
-
subject.start
|
25
|
+
subject.start
|
26
26
|
subject.wait_for_callback
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'calls listener.on_change continuously' do
|
30
30
|
subject.latency = 0.001
|
31
31
|
listener.should_receive(:on_change).at_least(10).times.with(['dir'], :recursive => true)
|
32
|
-
subject.start
|
32
|
+
subject.start
|
33
33
|
10.times { subject.wait_for_callback }
|
34
34
|
end
|
35
35
|
|
36
36
|
it "doesn't call listener.on_change if paused" do
|
37
37
|
subject.paused = true
|
38
|
-
subject.start
|
38
|
+
subject.start
|
39
39
|
subject.wait_for_callback
|
40
40
|
@called.should be_nil
|
41
41
|
end
|
@@ -46,20 +46,20 @@ describe Listen::Adapters::Polling do
|
|
46
46
|
|
47
47
|
it 'calls listener.on_change' do
|
48
48
|
listener.should_receive(:on_change).at_least(1).times.with(%w{dir1 dir2}, :recursive => true)
|
49
|
-
subject.start
|
49
|
+
subject.start
|
50
50
|
subject.wait_for_callback
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'calls listener.on_change continuously' do
|
54
54
|
subject.latency = 0.001
|
55
55
|
listener.should_receive(:on_change).at_least(10).times.with(%w{dir1 dir2}, :recursive => true)
|
56
|
-
subject.start
|
56
|
+
subject.start
|
57
57
|
10.times { subject.wait_for_callback }
|
58
58
|
end
|
59
59
|
|
60
60
|
it "doesn't call listener.on_change if paused" do
|
61
61
|
subject.paused = true
|
62
|
-
subject.start
|
62
|
+
subject.start
|
63
63
|
subject.wait_for_callback
|
64
64
|
@called.should be_nil
|
65
65
|
end
|