sass 3.3.0 → 3.4.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -1
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/CONTRIBUTING.md +148 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +76 -62
  7. data/Rakefile +104 -24
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/VERSION_NAME +1 -1
  11. data/bin/sass +1 -1
  12. data/bin/scss +1 -1
  13. data/extra/sass-spec-ref.sh +32 -0
  14. data/extra/update_watch.rb +1 -1
  15. data/lib/sass/cache_stores/filesystem.rb +9 -5
  16. data/lib/sass/cache_stores/memory.rb +4 -5
  17. data/lib/sass/callbacks.rb +2 -2
  18. data/lib/sass/css.rb +12 -13
  19. data/lib/sass/deprecation.rb +55 -0
  20. data/lib/sass/engine.rb +106 -70
  21. data/lib/sass/environment.rb +39 -19
  22. data/lib/sass/error.rb +17 -20
  23. data/lib/sass/exec/base.rb +199 -0
  24. data/lib/sass/exec/sass_convert.rb +283 -0
  25. data/lib/sass/exec/sass_scss.rb +440 -0
  26. data/lib/sass/exec.rb +5 -771
  27. data/lib/sass/features.rb +9 -2
  28. data/lib/sass/importers/base.rb +8 -3
  29. data/lib/sass/importers/filesystem.rb +30 -38
  30. data/lib/sass/logger/base.rb +8 -2
  31. data/lib/sass/logger/delayed.rb +50 -0
  32. data/lib/sass/logger.rb +8 -3
  33. data/lib/sass/media.rb +1 -4
  34. data/lib/sass/plugin/compiler.rb +224 -90
  35. data/lib/sass/plugin/configuration.rb +38 -22
  36. data/lib/sass/plugin/merb.rb +2 -2
  37. data/lib/sass/plugin/rack.rb +3 -3
  38. data/lib/sass/plugin/rails.rb +1 -1
  39. data/lib/sass/plugin/staleness_checker.rb +4 -4
  40. data/lib/sass/plugin.rb +6 -5
  41. data/lib/sass/script/css_lexer.rb +1 -1
  42. data/lib/sass/script/css_parser.rb +2 -3
  43. data/lib/sass/script/css_variable_warning.rb +52 -0
  44. data/lib/sass/script/functions.rb +739 -318
  45. data/lib/sass/script/lexer.rb +134 -54
  46. data/lib/sass/script/parser.rb +252 -56
  47. data/lib/sass/script/tree/funcall.rb +13 -6
  48. data/lib/sass/script/tree/interpolation.rb +127 -4
  49. data/lib/sass/script/tree/list_literal.rb +31 -4
  50. data/lib/sass/script/tree/literal.rb +4 -0
  51. data/lib/sass/script/tree/node.rb +21 -3
  52. data/lib/sass/script/tree/operation.rb +54 -1
  53. data/lib/sass/script/tree/selector.rb +26 -0
  54. data/lib/sass/script/tree/string_interpolation.rb +59 -38
  55. data/lib/sass/script/tree/variable.rb +1 -1
  56. data/lib/sass/script/tree.rb +1 -0
  57. data/lib/sass/script/value/base.rb +17 -14
  58. data/lib/sass/script/value/bool.rb +0 -5
  59. data/lib/sass/script/value/color.rb +78 -42
  60. data/lib/sass/script/value/helpers.rb +119 -2
  61. data/lib/sass/script/value/list.rb +0 -15
  62. data/lib/sass/script/value/map.rb +1 -1
  63. data/lib/sass/script/value/null.rb +0 -5
  64. data/lib/sass/script/value/number.rb +112 -31
  65. data/lib/sass/script/value/string.rb +102 -13
  66. data/lib/sass/script/value.rb +0 -1
  67. data/lib/sass/script.rb +3 -3
  68. data/lib/sass/scss/css_parser.rb +24 -4
  69. data/lib/sass/scss/parser.rb +290 -383
  70. data/lib/sass/scss/rx.rb +17 -9
  71. data/lib/sass/scss/static_parser.rb +306 -4
  72. data/lib/sass/scss.rb +0 -2
  73. data/lib/sass/selector/abstract_sequence.rb +35 -18
  74. data/lib/sass/selector/comma_sequence.rb +114 -19
  75. data/lib/sass/selector/pseudo.rb +266 -0
  76. data/lib/sass/selector/sequence.rb +146 -40
  77. data/lib/sass/selector/simple.rb +22 -33
  78. data/lib/sass/selector/simple_sequence.rb +122 -39
  79. data/lib/sass/selector.rb +57 -197
  80. data/lib/sass/shared.rb +2 -2
  81. data/lib/sass/source/map.rb +31 -14
  82. data/lib/sass/source/position.rb +4 -4
  83. data/lib/sass/stack.rb +2 -8
  84. data/lib/sass/supports.rb +10 -13
  85. data/lib/sass/tree/at_root_node.rb +1 -0
  86. data/lib/sass/tree/charset_node.rb +1 -1
  87. data/lib/sass/tree/comment_node.rb +1 -1
  88. data/lib/sass/tree/css_import_node.rb +9 -1
  89. data/lib/sass/tree/directive_node.rb +8 -2
  90. data/lib/sass/tree/error_node.rb +18 -0
  91. data/lib/sass/tree/extend_node.rb +1 -1
  92. data/lib/sass/tree/function_node.rb +9 -0
  93. data/lib/sass/tree/import_node.rb +6 -5
  94. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  95. data/lib/sass/tree/node.rb +5 -3
  96. data/lib/sass/tree/prop_node.rb +6 -7
  97. data/lib/sass/tree/rule_node.rb +26 -11
  98. data/lib/sass/tree/visitors/check_nesting.rb +56 -32
  99. data/lib/sass/tree/visitors/convert.rb +59 -44
  100. data/lib/sass/tree/visitors/cssize.rb +34 -30
  101. data/lib/sass/tree/visitors/deep_copy.rb +6 -1
  102. data/lib/sass/tree/visitors/extend.rb +15 -13
  103. data/lib/sass/tree/visitors/perform.rb +87 -50
  104. data/lib/sass/tree/visitors/set_options.rb +15 -1
  105. data/lib/sass/tree/visitors/to_css.rb +72 -43
  106. data/lib/sass/util/multibyte_string_scanner.rb +0 -2
  107. data/lib/sass/util/normalized_map.rb +0 -1
  108. data/lib/sass/util/subset_map.rb +2 -3
  109. data/lib/sass/util.rb +334 -154
  110. data/lib/sass/version.rb +7 -7
  111. data/lib/sass.rb +10 -8
  112. data/test/sass/cache_test.rb +62 -20
  113. data/test/sass/callbacks_test.rb +1 -1
  114. data/test/sass/compiler_test.rb +24 -11
  115. data/test/sass/conversion_test.rb +241 -50
  116. data/test/sass/css2sass_test.rb +73 -5
  117. data/test/sass/css_variable_test.rb +132 -0
  118. data/test/sass/encoding_test.rb +219 -0
  119. data/test/sass/engine_test.rb +343 -260
  120. data/test/sass/exec_test.rb +12 -2
  121. data/test/sass/extend_test.rb +333 -44
  122. data/test/sass/functions_test.rb +353 -260
  123. data/test/sass/importer_test.rb +40 -21
  124. data/test/sass/logger_test.rb +1 -1
  125. data/test/sass/more_results/more_import.css +1 -1
  126. data/test/sass/more_templates/more1.sass +10 -10
  127. data/test/sass/more_templates/more_import.sass +2 -2
  128. data/test/sass/plugin_test.rb +24 -21
  129. data/test/sass/results/compact.css +1 -1
  130. data/test/sass/results/complex.css +4 -4
  131. data/test/sass/results/expanded.css +1 -1
  132. data/test/sass/results/import.css +1 -1
  133. data/test/sass/results/import_charset_ibm866.css +2 -2
  134. data/test/sass/results/mixins.css +17 -17
  135. data/test/sass/results/nested.css +1 -1
  136. data/test/sass/results/parent_ref.css +2 -2
  137. data/test/sass/results/script.css +5 -5
  138. data/test/sass/results/scss_import.css +1 -1
  139. data/test/sass/script_conversion_test.rb +71 -39
  140. data/test/sass/script_test.rb +714 -123
  141. data/test/sass/scss/css_test.rb +213 -30
  142. data/test/sass/scss/rx_test.rb +8 -4
  143. data/test/sass/scss/scss_test.rb +766 -22
  144. data/test/sass/source_map_test.rb +263 -95
  145. data/test/sass/superselector_test.rb +210 -0
  146. data/test/sass/templates/_partial.sass +1 -1
  147. data/test/sass/templates/basic.sass +10 -10
  148. data/test/sass/templates/bork1.sass +1 -1
  149. data/test/sass/templates/bork5.sass +1 -1
  150. data/test/sass/templates/compact.sass +10 -10
  151. data/test/sass/templates/complex.sass +187 -187
  152. data/test/sass/templates/compressed.sass +10 -10
  153. data/test/sass/templates/expanded.sass +10 -10
  154. data/test/sass/templates/import.sass +2 -2
  155. data/test/sass/templates/importee.sass +3 -3
  156. data/test/sass/templates/mixins.sass +22 -22
  157. data/test/sass/templates/multiline.sass +4 -4
  158. data/test/sass/templates/nested.sass +13 -13
  159. data/test/sass/templates/parent_ref.sass +12 -12
  160. data/test/sass/templates/script.sass +70 -70
  161. data/test/sass/templates/scss_import.scss +2 -1
  162. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
  163. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
  164. data/test/sass/templates/subdir/subdir.sass +3 -3
  165. data/test/sass/templates/units.sass +10 -10
  166. data/test/sass/test_helper.rb +1 -1
  167. data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
  168. data/test/sass/util/normalized_map_test.rb +1 -1
  169. data/test/sass/util/subset_map_test.rb +2 -2
  170. data/test/sass/util_test.rb +46 -45
  171. data/test/sass/value_helpers_test.rb +5 -7
  172. data/test/sass-spec.yml +3 -0
  173. data/test/test_helper.rb +7 -6
  174. data/vendor/listen/CHANGELOG.md +1 -228
  175. data/vendor/listen/Gemfile +5 -15
  176. data/vendor/listen/README.md +111 -77
  177. data/vendor/listen/Rakefile +0 -42
  178. data/vendor/listen/lib/listen/adapter.rb +195 -82
  179. data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
  180. data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
  181. data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
  182. data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
  183. data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
  184. data/vendor/listen/lib/listen/directory_record.rb +96 -61
  185. data/vendor/listen/lib/listen/listener.rb +135 -37
  186. data/vendor/listen/lib/listen/turnstile.rb +9 -5
  187. data/vendor/listen/lib/listen/version.rb +1 -1
  188. data/vendor/listen/lib/listen.rb +33 -19
  189. data/vendor/listen/listen.gemspec +6 -0
  190. data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
  191. data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
  192. data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
  193. data/vendor/listen/spec/listen/listener_spec.rb +128 -39
  194. data/vendor/listen/spec/listen_spec.rb +15 -21
  195. data/vendor/listen/spec/spec_helper.rb +4 -0
  196. data/vendor/listen/spec/support/adapter_helper.rb +52 -15
  197. data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
  198. data/vendor/listen/spec/support/listeners_helper.rb +30 -7
  199. metadata +310 -300
  200. data/CONTRIBUTING +0 -3
  201. data/ext/mkrf_conf.rb +0 -27
  202. data/lib/sass/script/value/deprecated_false.rb +0 -55
  203. data/lib/sass/scss/script_lexer.rb +0 -15
  204. data/lib/sass/scss/script_parser.rb +0 -25
  205. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  206. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  207. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  208. 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 :directory, :directory_record, :adapter
5
+ attr_reader :directories, :directories_records, :block, :adapter, :adapter_options, :use_relative_paths
6
6
 
7
- # The default value for using relative paths in the callback.
8
- DEFAULT_TO_RELATIVE_PATHS = false
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
- # Initializes the directory listener.
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 directory to listen to
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(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
+ 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
- @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]
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
- # @param [Boolean] blocking whether or not to block the current thread after starting
48
+ # @see Listen::Listener#start!
44
49
  #
45
- def start(blocking = true)
46
- t = Thread.new { @directory_record.build }
47
- @adapter = initialize_adapter
48
- t.join
49
- @adapter.start(blocking)
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
- @adapter.stop
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
- @adapter.paused = true
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
- @directory_record.build
73
- @adapter.paused = false
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
- !!@adapter && @adapter.paused == true
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
- @directory_record.ignore(*regexps)
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
- @directory_record.ignore!(*regexps)
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
- @directory_record.filter(*regexps)
137
+ directories_records.each { |r| r.filter(*regexps) }
115
138
  self
116
139
  end
117
140
 
118
- # Replacing filtering patterns in the listener.
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
- @directory_record.filter!(*regexps)
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 of disable it.
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 = @directory_record.fetch_changes(directories, options.merge(
209
- :relative_paths => @use_relative_paths
210
- ))
250
+ changes = fetch_records_changes(directories, options)
211
251
  unless changes.values.all? { |paths| paths.empty? }
212
- @block.call(changes[:modified],changes[:added],changes[:removed])
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 { |changed_dirs, options| self.on_change(changed_dirs, options) }
222
- Adapter.select_and_initialize(@directory, @adapter_options, &callback)
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 ruby offers semahpores, only queues can be used
13
+ # Until Ruby offers semahpores, only queues can be used
12
14
  # to implement a turnstile.
13
- @q = Queue.new
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
- @q.pop if @q.num_waiting == 0
21
+ queue.pop if queue.num_waiting == 0
20
22
  end
21
23
 
22
- # Unblocks the waiting thread if there is one.
24
+ # Unblocks the waiting thread if any.
23
25
  #
24
26
  def signal
25
- @q.push :dummy if @q.num_waiting == 1
27
+ queue.push(:dummy) if queue.num_waiting == 1
26
28
  end
29
+
27
30
  end
31
+
28
32
  end
@@ -1,3 +1,3 @@
1
1
  module Listen
2
- VERSION = '0.7.3'
2
+ VERSION = '1.3.1'
3
3
  end
@@ -1,24 +1,20 @@
1
- module Listen
1
+ require 'listen/turnstile'
2
+ require 'listen/listener'
3
+ require 'listen/directory_record'
4
+ require 'listen/adapter'
2
5
 
3
- autoload :Turnstile, 'listen/turnstile'
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
- autoload :Darwin, 'listen/adapters/darwin'
12
- autoload :Linux, 'listen/adapters/linux'
13
- autoload :BSD, 'listen/adapters/bsd'
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 filesystem modifications on a either single directory or multiple directories.
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 = if args.length == 1 || ! args[1].is_a?(String)
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::Adapters::Darwin.stub(:usable_and_works?) { false }
23
- Listen::Adapters::Linux.stub(:usable_and_works?) { false }
24
- Listen::Adapters::BSD.stub(:usable_and_works?) { false }
25
- Listen::Adapters::Windows.stub(:usable_and_works?) { false }
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
- context "on Mac OX >= 10.6" do
70
- before { Listen::Adapters::Darwin.stub(:usable_and_works?) { true } }
68
+ Listen::Adapter::OPTIMIZED_ADAPTERS.each do |adapter|
69
+ adapter_class = Listen::Adapters.const_get(adapter)
71
70
 
72
- it "uses Listen::Adapters::Darwin" do
73
- Listen::Adapters::Darwin.should_receive(:new).with('dir', {})
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
- context 'when the use of the polling adapter is forced' do
78
- it 'uses Listen::Adapters::Polling' do
79
- Listen::Adapters::Polling.should_receive(:new).with('dir', {})
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
- context "on Linux" do
86
- before { Listen::Adapters::Linux.stub(:usable_and_works?) { true } }
87
-
88
- it "uses Listen::Adapters::Linux" do
89
- Listen::Adapters::Linux.should_receive(:new).with('dir', {})
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
- context "on BSD" do
102
- before { Listen::Adapters::BSD.stub(:usable_and_works?) { true } }
89
+ describe '.load_dependend_adapter' do
90
+ after(:each) { described_class.instance_variable_set('@loaded', nil) }
103
91
 
104
- it "uses Listen::Adapters::BSD" do
105
- Listen::Adapters::BSD.should_receive(:new).with('dir', {})
106
- described_class.select_and_initialize('dir')
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
- context 'when the use of the polling adapter is forced' do
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
- context "on Windows" do
118
- before { Listen::Adapters::Windows.stub(:usable_and_works?) { true } }
119
-
120
- it "uses Listen::Adapters::Windows" do
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
- context 'when the use of the polling adapter is forced' do
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
- [Listen::Adapters::Darwin, Listen::Adapters::Linux,
135
- Listen::Adapters::BSD, Listen::Adapters::Windows].each do
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) { mock(Listen::Listener) }
16
- let(:callback) { lambda { |changed_dirs, options| @called = true; listener.on_change(changed_dirs, options) } }
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(false)
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(false)
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(false)
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(false)
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(false)
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(false)
62
+ subject.start
63
63
  subject.wait_for_callback
64
64
  @called.should be_nil
65
65
  end