sass 3.2.5 → 3.2.6

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.
Files changed (56) hide show
  1. data/VERSION +1 -1
  2. data/VERSION_DATE +1 -1
  3. data/bin/sass +2 -1
  4. data/bin/sass-convert +2 -1
  5. data/bin/scss +2 -1
  6. data/lib/sass/cache_stores/chain.rb +1 -1
  7. data/lib/sass/cache_stores/filesystem.rb +0 -1
  8. data/lib/sass/engine.rb +7 -1
  9. data/lib/sass/importers/filesystem.rb +1 -1
  10. data/lib/sass/media.rb +1 -4
  11. data/lib/sass/script/funcall.rb +43 -8
  12. data/lib/sass/script/lexer.rb +0 -2
  13. data/lib/sass/script/parser.rb +0 -2
  14. data/lib/sass/scss/parser.rb +13 -1
  15. data/lib/sass/selector/simple_sequence.rb +1 -1
  16. data/lib/sass/tree/comment_node.rb +2 -2
  17. data/lib/sass/tree/visitors/cssize.rb +10 -1
  18. data/lib/sass/tree/visitors/perform.rb +4 -2
  19. data/lib/sass/util.rb +54 -1
  20. data/lib/sass/util/multibyte_string_scanner.rb +29 -8
  21. data/test/sass/engine_test.rb +16 -0
  22. data/test/sass/extend_test.rb +15 -0
  23. data/test/sass/script_test.rb +3 -1
  24. data/vendor/listen/CHANGELOG.md +76 -2
  25. data/vendor/listen/CONTRIBUTING.md +38 -0
  26. data/vendor/listen/Gemfile +8 -1
  27. data/vendor/listen/Guardfile +1 -1
  28. data/vendor/listen/LICENSE +1 -1
  29. data/vendor/listen/README.md +8 -5
  30. data/vendor/listen/lib/listen.rb +7 -5
  31. data/vendor/listen/lib/listen/adapter.rb +76 -29
  32. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  33. data/vendor/listen/lib/listen/adapters/darwin.rb +11 -10
  34. data/vendor/listen/lib/listen/adapters/linux.rb +33 -30
  35. data/vendor/listen/lib/listen/adapters/polling.rb +2 -1
  36. data/vendor/listen/lib/listen/adapters/windows.rb +27 -21
  37. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  38. data/vendor/listen/lib/listen/directory_record.rb +63 -10
  39. data/vendor/listen/lib/listen/listener.rb +22 -0
  40. data/vendor/listen/lib/listen/multi_listener.rb +22 -0
  41. data/vendor/listen/lib/listen/version.rb +1 -1
  42. data/vendor/listen/listen.gemspec +0 -4
  43. data/vendor/listen/spec/listen/adapter_spec.rb +45 -4
  44. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  45. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +6 -0
  46. data/vendor/listen/spec/listen/adapters/linux_spec.rb +6 -0
  47. data/vendor/listen/spec/listen/adapters/windows_spec.rb +7 -1
  48. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  49. data/vendor/listen/spec/listen/directory_record_spec.rb +91 -4
  50. data/vendor/listen/spec/listen/listener_spec.rb +14 -0
  51. data/vendor/listen/spec/listen/multi_listener_spec.rb +19 -1
  52. data/vendor/listen/spec/spec_helper.rb +6 -3
  53. data/vendor/listen/spec/support/adapter_helper.rb +125 -212
  54. data/vendor/listen/spec/support/listeners_helper.rb +13 -1
  55. data/vendor/listen/spec/support/platform_helper.rb +4 -0
  56. metadata +9 -3
@@ -4,6 +4,10 @@ module Listen
4
4
  # Adapter implementation for Mac OS X `FSEvents`.
5
5
  #
6
6
  class Darwin < Adapter
7
+ extend DependencyManager
8
+
9
+ # Declare the adapter's dependencies
10
+ dependency 'rb-fsevent', '~> 0.9.1'
7
11
 
8
12
  LAST_SEPARATOR_REGEX = /\/$/
9
13
 
@@ -25,13 +29,14 @@ module Listen
25
29
  end
26
30
 
27
31
  @worker_thread = Thread.new { @worker.run }
28
- @poll_thread = Thread.new { poll_changed_dirs }
29
32
 
30
33
  # The FSEvent worker needs sometime to startup. Turnstiles can't
31
34
  # be used to wait for it as it runs in a loop.
32
35
  # TODO: Find a better way to block until the worker starts.
33
- sleep @latency
34
- @poll_thread.join if blocking
36
+ sleep 0.1
37
+
38
+ @poll_thread = Thread.new { poll_changed_dirs } if @report_changes
39
+ @worker_thread.join if blocking
35
40
  end
36
41
 
37
42
  # Stops the adapter.
@@ -43,8 +48,8 @@ module Listen
43
48
  end
44
49
 
45
50
  @worker.stop
46
- Thread.kill(@worker_thread) if @worker_thread
47
- @poll_thread.join
51
+ @worker_thread.join if @worker_thread
52
+ @poll_thread.join if @poll_thread
48
53
  end
49
54
 
50
55
  # Checks if the adapter is usable on the current OS.
@@ -53,11 +58,7 @@ module Listen
53
58
  #
54
59
  def self.usable?
55
60
  return false unless RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
56
-
57
- require 'rb-fsevent'
58
- true
59
- rescue LoadError
60
- false
61
+ super
61
62
  end
62
63
 
63
64
  private
@@ -4,6 +4,10 @@ module Listen
4
4
  # Listener implementation for Linux `inotify`.
5
5
  #
6
6
  class Linux < Adapter
7
+ extend DependencyManager
8
+
9
+ # Declare the adapter's dependencies
10
+ dependency 'rb-inotify', '~> 0.8.8'
7
11
 
8
12
  # Watched inotify events
9
13
  #
@@ -41,8 +45,9 @@ module Listen
41
45
  end
42
46
 
43
47
  @worker_thread = Thread.new { @worker.run }
44
- @poll_thread = Thread.new { poll_changed_dirs }
45
- @poll_thread.join if blocking
48
+ @poll_thread = Thread.new { poll_changed_dirs } if @report_changes
49
+
50
+ @worker_thread.join if blocking
46
51
  end
47
52
 
48
53
  # Stops the adapter.
@@ -55,20 +60,16 @@ module Listen
55
60
 
56
61
  @worker.stop
57
62
  Thread.kill(@worker_thread) if @worker_thread
58
- @poll_thread.join
63
+ @poll_thread.join if @poll_thread
59
64
  end
60
65
 
61
- # Check if the adapter is usable on the current OS.
66
+ # Checks if the adapter is usable on the current OS.
62
67
  #
63
68
  # @return [Boolean] whether usable or not
64
69
  #
65
70
  def self.usable?
66
71
  return false unless RbConfig::CONFIG['target_os'] =~ /linux/i
67
-
68
- require 'rb-inotify'
69
- true
70
- rescue LoadError
71
- false
72
+ super
72
73
  end
73
74
 
74
75
  private
@@ -79,29 +80,31 @@ module Listen
79
80
  # @return [INotify::Notifier] initialized worker
80
81
  #
81
82
  def init_worker
82
- worker = INotify::Notifier.new
83
- @directories.each do |directory|
84
- worker.watch(directory, *EVENTS.map(&:to_sym)) do |event|
85
- if @paused || (
86
- # Event on root directory
87
- event.name == ""
88
- ) || (
89
- # INotify reports changes to files inside directories as events
90
- # on the directories themselves too.
91
- #
92
- # @see http://linux.die.net/man/7/inotify
93
- event.flags.include?(:isdir) and event.flags & [:close, :modify] != []
94
- )
95
- # Skip all of these!
96
- next
97
- end
98
-
99
- @mutex.synchronize do
100
- @changed_dirs << File.dirname(event.absolute_name)
101
- end
83
+ callback = lambda do |event|
84
+ if @paused || (
85
+ # Event on root directory
86
+ event.name == ""
87
+ ) || (
88
+ # INotify reports changes to files inside directories as events
89
+ # on the directories themselves too.
90
+ #
91
+ # @see http://linux.die.net/man/7/inotify
92
+ event.flags.include?(:isdir) and event.flags & [:close, :modify] != []
93
+ )
94
+ # Skip all of these!
95
+ next
96
+ end
97
+
98
+ @mutex.synchronize do
99
+ @changed_dirs << File.dirname(event.absolute_name)
100
+ end
101
+ end
102
+
103
+ INotify::Notifier.new.tap do |worker|
104
+ @directories.each do |directory|
105
+ worker.watch(directory, *EVENTS.map(&:to_sym), &callback)
102
106
  end
103
107
  end
104
- worker
105
108
  end
106
109
 
107
110
  end
@@ -10,6 +10,7 @@ module Listen
10
10
  # file IO that the other implementations.
11
11
  #
12
12
  class Polling < Adapter
13
+ extend DependencyManager
13
14
 
14
15
  # Initialize the Adapter. See {Listen::Adapter#initialize} for more info.
15
16
  #
@@ -49,7 +50,7 @@ module Listen
49
50
  #
50
51
  def poll
51
52
  until @stop
52
- sleep(0.1) && next if @paused
53
+ next if @paused
53
54
 
54
55
  start = Time.now.to_f
55
56
  @callback.call(@directories.dup, :recursive => true)
@@ -3,9 +3,13 @@ require 'set'
3
3
  module Listen
4
4
  module Adapters
5
5
 
6
- # Adapter implementation for Windows `fchange`.
6
+ # Adapter implementation for Windows `wdm`.
7
7
  #
8
8
  class Windows < Adapter
9
+ extend DependencyManager
10
+
11
+ # Declare the adapter's dependencies
12
+ dependency 'wdm', '~> 0.0.3'
9
13
 
10
14
  # Initializes the Adapter. See {Listen::Adapter#initialize} for more info.
11
15
  #
@@ -24,9 +28,15 @@ module Listen
24
28
  super
25
29
  end
26
30
 
27
- @worker_thread = Thread.new { @worker.run }
28
- @poll_thread = Thread.new { poll_changed_dirs(true) }
29
- @poll_thread.join if blocking
31
+ @worker_thread = Thread.new { @worker.run! }
32
+
33
+ # Wait for the worker to start. This is needed to avoid a deadlock
34
+ # when stopping immediately after starting.
35
+ sleep 0.1
36
+
37
+ @poll_thread = Thread.new { poll_changed_dirs } if @report_changes
38
+
39
+ @worker_thread.join if blocking
30
40
  end
31
41
 
32
42
  # Stops the adapter.
@@ -38,8 +48,8 @@ module Listen
38
48
  end
39
49
 
40
50
  @worker.stop
41
- Thread.kill(@worker_thread) if @worker_thread
42
- @poll_thread.join
51
+ @worker_thread.join if @worker_thread
52
+ @poll_thread.join if @poll_thread
43
53
  end
44
54
 
45
55
  # Checks if the adapter is usable on the current OS.
@@ -48,31 +58,27 @@ module Listen
48
58
  #
49
59
  def self.usable?
50
60
  return false unless RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
51
-
52
- require 'rb-fchange'
53
- true
54
- rescue LoadError
55
- false
61
+ super
56
62
  end
57
63
 
58
64
  private
59
65
 
60
- # Initializes a FChange worker and adds a watcher for
66
+ # Initializes a WDM monitor and adds a watcher for
61
67
  # each directory passed to the adapter.
62
68
  #
63
- # @return [FChange::Notifier] initialized worker
69
+ # @return [WDM::Monitor] initialized worker
64
70
  #
65
71
  def init_worker
66
- FChange::Notifier.new.tap do |worker|
67
- @directories.each do |directory|
68
- worker.watch(directory, :all_events, :recursive) do |event|
69
- next if @paused
70
- @mutex.synchronize do
71
- @changed_dirs << File.expand_path(event.watcher.path)
72
- end
73
- end
72
+ callback = Proc.new do |change|
73
+ next if @paused
74
+ @mutex.synchronize do
75
+ @changed_dirs << File.dirname(change.path)
74
76
  end
75
77
  end
78
+
79
+ WDM::Monitor.new.tap do |worker|
80
+ @directories.each { |d| worker.watch_recursively(d, &callback) }
81
+ end
76
82
  end
77
83
 
78
84
  end
@@ -0,0 +1,126 @@
1
+ require 'set'
2
+
3
+ module Listen
4
+
5
+ # The dependency-manager offers a simple DSL which allows
6
+ # classes to declare their gem dependencies and load them when
7
+ # needed.
8
+ # It raises a user-friendly exception when the dependencies
9
+ # can't be loaded which has the install command in the message.
10
+ #
11
+ module DependencyManager
12
+
13
+ GEM_LOAD_MESSAGE = <<-EOS.gsub(/^ {6}/, '')
14
+ Missing dependency '%s' (version '%s')!
15
+ EOS
16
+
17
+ GEM_INSTALL_COMMAND = <<-EOS.gsub(/^ {6}/, '')
18
+ Please run the following to satisfy the dependency:
19
+ gem install --version '%s' %s
20
+ EOS
21
+
22
+ BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
23
+ Please add the following to your Gemfile to satisfy the dependency:
24
+ gem '%s', '%s'
25
+ EOS
26
+
27
+ Dependency = Struct.new(:name, :version)
28
+
29
+ # The error raised when a dependency can't be loaded.
30
+ class Error < StandardError; end
31
+
32
+ # A list of all loaded dependencies in the dependency manager.
33
+ @_loaded_dependencies = Set.new
34
+
35
+ # class methods
36
+ class << self
37
+
38
+ # Initializes the extended class.
39
+ #
40
+ # @param [Class] the class for which some dependencies must be managed
41
+ #
42
+ def extended(base)
43
+ base.class_eval do
44
+ @_dependencies = Set.new
45
+ end
46
+ end
47
+
48
+ # Adds a loaded dependency to a list so that it doesn't have
49
+ # to be loaded again by another classes.
50
+ #
51
+ # @param [Dependency] dependency
52
+ #
53
+ def add_loaded(dependency)
54
+ @_loaded_dependencies << dependency
55
+ end
56
+
57
+ # Returns whether the dependency is alread loaded or not.
58
+ #
59
+ # @param [Dependency] dependency
60
+ # @return [Boolean]
61
+ #
62
+ def already_loaded?(dependency)
63
+ @_loaded_dependencies.include?(dependency)
64
+ end
65
+
66
+ # Clears the list of loaded dependencies.
67
+ #
68
+ def clear_loaded
69
+ @_loaded_dependencies.clear
70
+ end
71
+ end
72
+
73
+ # Registers a new dependency.
74
+ #
75
+ # @param [String] name the name of the gem
76
+ # @param [String] version the version of the gem
77
+ #
78
+ def dependency(name, version)
79
+ @_dependencies << Dependency.new(name, version)
80
+ end
81
+
82
+ # Loads the registered dependencies.
83
+ #
84
+ # @raise DependencyManager::Error if the dependency can't be loaded.
85
+ #
86
+ def load_depenencies
87
+ @_dependencies.each do |dependency|
88
+ begin
89
+ next if DependencyManager.already_loaded?(dependency)
90
+ gem(dependency.name, dependency.version)
91
+ require(dependency.name)
92
+ DependencyManager.add_loaded(dependency)
93
+ @_dependencies.delete(dependency)
94
+ rescue Gem::LoadError
95
+ args = [dependency.name, dependency.version]
96
+ command = if running_under_bundler?
97
+ BUNDLER_DECLARE_GEM % args
98
+ else
99
+ GEM_INSTALL_COMMAND % args.reverse
100
+ end
101
+ message = GEM_LOAD_MESSAGE % args
102
+
103
+ raise Error.new(message + command)
104
+ end
105
+ end
106
+ end
107
+
108
+ # Returns whether all the dependencies has been loaded or not.
109
+ #
110
+ # @return [Boolean]
111
+ #
112
+ def dependencies_loaded?
113
+ @_dependencies.empty?
114
+ end
115
+
116
+ private
117
+
118
+ # Returns whether we are running under bundler or not
119
+ #
120
+ # @return [Boolean]
121
+ #
122
+ def running_under_bundler?
123
+ !!(File.exists?('Gemfile') && ENV['BUNDLE_GEMFILE'])
124
+ end
125
+ end
126
+ end
@@ -18,7 +18,11 @@ module Listen
18
18
  # Defines the used precision based on the type of mtime returned by the
19
19
  # system (whether its in milliseconds or just seconds)
20
20
  #
21
- HIGH_PRECISION_SUPPORTED = File.mtime(__FILE__).to_f.to_s[-2..-1] != '.0'
21
+ begin
22
+ HIGH_PRECISION_SUPPORTED = File.mtime(__FILE__).to_f.to_s[-2..-1] != '.0'
23
+ rescue
24
+ HIGH_PRECISION_SUPPORTED = false
25
+ end
22
26
 
23
27
  # Data structure used to save meta data about a path
24
28
  #
@@ -88,6 +92,17 @@ module Listen
88
92
  @ignoring_patterns.merge(regexps)
89
93
  end
90
94
 
95
+ # Replaces ignoring patterns in the record.
96
+ #
97
+ # @example Ignore only these paths
98
+ # ignore! %r{^ignored/path/}, /man/
99
+ #
100
+ # @param [Regexp] regexp a pattern for ignoring paths
101
+ #
102
+ def ignore!(*regexps)
103
+ @ignoring_patterns.replace(regexps)
104
+ end
105
+
91
106
  # Adds filtering patterns to the listener.
92
107
  #
93
108
  # @example Filter some files
@@ -99,6 +114,17 @@ module Listen
99
114
  @filtering_patterns.merge(regexps)
100
115
  end
101
116
 
117
+ # Replaces filtering patterns in the listener.
118
+ #
119
+ # @example Filter only these files
120
+ # ignore /\.txt$/, /.*\.zip/
121
+ #
122
+ # @param [Regexp] regexp a pattern for filtering paths
123
+ #
124
+ def filter!(*regexps)
125
+ @filtering_patterns.replace(regexps)
126
+ end
127
+
102
128
  # Returns whether a path should be ignored or not.
103
129
  #
104
130
  # @param [String] path the path to test.
@@ -163,6 +189,7 @@ module Listen
163
189
  #
164
190
  def relative_to_base(path)
165
191
  return nil unless path[@directory]
192
+ path = path.force_encoding("BINARY") if path.respond_to?(:force_encoding)
166
193
  path.sub(%r{^#{Regexp.quote(@directory)}#{File::SEPARATOR}?}, '')
167
194
  end
168
195
 
@@ -198,8 +225,11 @@ module Listen
198
225
 
199
226
  # First check if we are in the same second (to update checksums)
200
227
  # before checking the time difference
201
- if (meta_data.mtime.to_i == new_mtime.to_i && content_modified?(path)) || meta_data.mtime < new_mtime
202
- # Update the meta data of the files
228
+ if (meta_data.mtime.to_i == new_mtime.to_i && content_modified?(path)) || meta_data.mtime < new_mtime
229
+ # Update the sha1 checksum of the file
230
+ insert_sha1_checksum(path)
231
+
232
+ # Update the meta data of the file
203
233
  meta_data.mtime = new_mtime
204
234
  @paths[directory][basename] = meta_data
205
235
 
@@ -247,17 +277,40 @@ module Listen
247
277
 
248
278
  # Returns whether or not a file's content has been modified by
249
279
  # comparing the SHA1-checksum to a stored one.
280
+ # Ensure that the SHA1-checksum is inserted to the sha1_checksums
281
+ # array for later comparaison if false.
250
282
  #
251
283
  # @param [String] path the file path
252
284
  #
253
285
  def content_modified?(path)
254
- sha1_checksum = Digest::SHA1.file(path).to_s
255
- return false if @sha1_checksums[path] == sha1_checksum
256
- @sha1_checksums.key?(path)
257
- rescue Errno::EACCES, Errno::ENOENT
258
- false
259
- ensure
260
- @sha1_checksums[path] = sha1_checksum if sha1_checksum
286
+ @sha1_checksum = sha1_checksum(path)
287
+ if @sha1_checksums[path] == @sha1_checksum || !@sha1_checksums.key?(path)
288
+ insert_sha1_checksum(path)
289
+ false
290
+ else
291
+ true
292
+ end
293
+ end
294
+
295
+ # Inserts a SHA1-checksum path in @SHA1-checksums hash.
296
+ #
297
+ # @param [String] path the SHA1-checksum path to insert in @sha1_checksums.
298
+ #
299
+ def insert_sha1_checksum(path)
300
+ if @sha1_checksum ||= sha1_checksum(path)
301
+ @sha1_checksums[path] = @sha1_checksum
302
+ @sha1_checksum = nil
303
+ end
304
+ end
305
+
306
+ # Returns the SHA1-checksum for the file path.
307
+ #
308
+ # @param [String] path the file path
309
+ #
310
+ def sha1_checksum(path)
311
+ Digest::SHA1.file(path).to_s
312
+ rescue Errno::EACCES, Errno::ENOENT, Errno::ENXIO, Errno::EOPNOTSUPP
313
+ nil
261
314
  end
262
315
 
263
316
  # Traverses the base directory looking for paths that should