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.
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/bin/sass +2 -1
- data/bin/sass-convert +2 -1
- data/bin/scss +2 -1
- data/lib/sass/cache_stores/chain.rb +1 -1
- data/lib/sass/cache_stores/filesystem.rb +0 -1
- data/lib/sass/engine.rb +7 -1
- data/lib/sass/importers/filesystem.rb +1 -1
- data/lib/sass/media.rb +1 -4
- data/lib/sass/script/funcall.rb +43 -8
- data/lib/sass/script/lexer.rb +0 -2
- data/lib/sass/script/parser.rb +0 -2
- data/lib/sass/scss/parser.rb +13 -1
- data/lib/sass/selector/simple_sequence.rb +1 -1
- data/lib/sass/tree/comment_node.rb +2 -2
- data/lib/sass/tree/visitors/cssize.rb +10 -1
- data/lib/sass/tree/visitors/perform.rb +4 -2
- data/lib/sass/util.rb +54 -1
- data/lib/sass/util/multibyte_string_scanner.rb +29 -8
- data/test/sass/engine_test.rb +16 -0
- data/test/sass/extend_test.rb +15 -0
- data/test/sass/script_test.rb +3 -1
- data/vendor/listen/CHANGELOG.md +76 -2
- data/vendor/listen/CONTRIBUTING.md +38 -0
- data/vendor/listen/Gemfile +8 -1
- data/vendor/listen/Guardfile +1 -1
- data/vendor/listen/LICENSE +1 -1
- data/vendor/listen/README.md +8 -5
- data/vendor/listen/lib/listen.rb +7 -5
- data/vendor/listen/lib/listen/adapter.rb +76 -29
- data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
- data/vendor/listen/lib/listen/adapters/darwin.rb +11 -10
- data/vendor/listen/lib/listen/adapters/linux.rb +33 -30
- data/vendor/listen/lib/listen/adapters/polling.rb +2 -1
- data/vendor/listen/lib/listen/adapters/windows.rb +27 -21
- data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
- data/vendor/listen/lib/listen/directory_record.rb +63 -10
- data/vendor/listen/lib/listen/listener.rb +22 -0
- data/vendor/listen/lib/listen/multi_listener.rb +22 -0
- data/vendor/listen/lib/listen/version.rb +1 -1
- data/vendor/listen/listen.gemspec +0 -4
- data/vendor/listen/spec/listen/adapter_spec.rb +45 -4
- data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
- data/vendor/listen/spec/listen/adapters/darwin_spec.rb +6 -0
- data/vendor/listen/spec/listen/adapters/linux_spec.rb +6 -0
- data/vendor/listen/spec/listen/adapters/windows_spec.rb +7 -1
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
- data/vendor/listen/spec/listen/directory_record_spec.rb +91 -4
- data/vendor/listen/spec/listen/listener_spec.rb +14 -0
- data/vendor/listen/spec/listen/multi_listener_spec.rb +19 -1
- data/vendor/listen/spec/spec_helper.rb +6 -3
- data/vendor/listen/spec/support/adapter_helper.rb +125 -212
- data/vendor/listen/spec/support/listeners_helper.rb +13 -1
- data/vendor/listen/spec/support/platform_helper.rb +4 -0
- 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
|
34
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
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 `
|
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
|
-
|
29
|
-
|
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
|
-
|
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
|
66
|
+
# Initializes a WDM monitor and adds a watcher for
|
61
67
|
# each directory passed to the adapter.
|
62
68
|
#
|
63
|
-
# @return [
|
69
|
+
# @return [WDM::Monitor] initialized worker
|
64
70
|
#
|
65
71
|
def init_worker
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
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
|
202
|
-
# Update the
|
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 =
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
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
|