sass 3.2.5 → 3.2.6

Sign up to get free protection for your applications and to get access to all the features.
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