sass 3.2.10 → 3.2.11
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -2
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass/cache_stores/filesystem.rb +1 -1
- data/lib/sass/exec.rb +10 -3
- data/lib/sass/plugin/staleness_checker.rb +19 -5
- data/lib/sass/script/functions.rb +18 -3
- data/lib/sass/script/list.rb +2 -1
- data/lib/sass/script/operation.rb +0 -1
- data/lib/sass/script/unary_operation.rb +8 -3
- data/lib/sass/scss/parser.rb +1 -0
- data/lib/sass/selector.rb +18 -15
- data/lib/sass/selector/sequence.rb +1 -1
- data/lib/sass/selector/simple.rb +1 -1
- data/lib/sass/selector/simple_sequence.rb +15 -8
- data/lib/sass/tree/visitors/cssize.rb +4 -4
- data/lib/sass/tree/visitors/to_css.rb +0 -1
- data/lib/sass/util.rb +23 -0
- data/test/sass/conversion_test.rb +17 -0
- data/test/sass/extend_test.rb +35 -0
- data/test/sass/functions_test.rb +6 -0
- data/test/sass/util_test.rb +48 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Sass
|
1
|
+
# Sass [![Gem Version](https://badge.fury.io/rb/sass.png)](http://badge.fury.io/rb/sass)
|
2
2
|
|
3
3
|
**Sass makes CSS fun again**. Sass is an extension of CSS3,
|
4
4
|
adding nested rules, variables, mixins, selector inheritance, and more.
|
@@ -56,7 +56,7 @@ to `config.ru`.
|
|
56
56
|
Then any Sass files in `public/stylesheets/sass`
|
57
57
|
will be compiled into CSS files in `public/stylesheets` on every request.
|
58
58
|
|
59
|
-
To use Sass
|
59
|
+
To use Sass programmatically,
|
60
60
|
check out the [YARD documentation](http://sass-lang.com/docs/yardoc/).
|
61
61
|
|
62
62
|
## Formatting
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.2.
|
1
|
+
3.2.11
|
data/VERSION_DATE
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
28 September 2013 01:15:19 UTC
|
@@ -36,7 +36,7 @@ module Sass
|
|
36
36
|
# return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename))
|
37
37
|
# return if File.exists?(compiled_filename) && !File.writable?(compiled_filename)
|
38
38
|
FileUtils.mkdir_p(File.dirname(compiled_filename))
|
39
|
-
|
39
|
+
Sass::Util.atomic_create_and_write_file(compiled_filename) do |f|
|
40
40
|
f.puts(version)
|
41
41
|
f.puts(sha)
|
42
42
|
f.write(contents)
|
data/lib/sass/exec.rb
CHANGED
@@ -293,8 +293,16 @@ END
|
|
293
293
|
@options[:for_engine][:cache] = false
|
294
294
|
end
|
295
295
|
|
296
|
-
|
297
|
-
|
296
|
+
encoding_desc = if ::Sass::Util.ruby1_8?
|
297
|
+
'Does not work in ruby 1.8.'
|
298
|
+
else
|
299
|
+
'Specify the default encoding for Sass files.'
|
300
|
+
end
|
301
|
+
opts.on('-E encoding', encoding_desc) do |encoding|
|
302
|
+
if ::Sass::Util.ruby1_8?
|
303
|
+
$stderr.puts "Specifying the encoding is not supported in ruby 1.8."
|
304
|
+
exit 1
|
305
|
+
else
|
298
306
|
Encoding.default_external = encoding
|
299
307
|
end
|
300
308
|
end
|
@@ -612,7 +620,6 @@ END
|
|
612
620
|
end
|
613
621
|
@options[:output] ||= @options[:input]
|
614
622
|
|
615
|
-
from = @options[:from]
|
616
623
|
if @options[:to] == @options[:from] && !@options[:in_place]
|
617
624
|
fmt = @options[:from]
|
618
625
|
raise "Error: converting from #{fmt} to #{fmt} without --in-place"
|
@@ -24,11 +24,13 @@ module Sass
|
|
24
24
|
# as its instance-level caches are never explicitly expired.
|
25
25
|
class StalenessChecker
|
26
26
|
@dependencies_cache = {}
|
27
|
+
@dependency_cache_mutex = Mutex.new
|
27
28
|
|
28
29
|
class << self
|
29
30
|
# TODO: attach this to a compiler instance.
|
30
31
|
# @private
|
31
32
|
attr_accessor :dependencies_cache
|
33
|
+
attr_reader :dependency_cache_mutex
|
32
34
|
end
|
33
35
|
|
34
36
|
# Creates a new StalenessChecker
|
@@ -37,8 +39,6 @@ module Sass
|
|
37
39
|
# @param options [{Symbol => Object}]
|
38
40
|
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
|
39
41
|
def initialize(options)
|
40
|
-
@dependencies = self.class.dependencies_cache
|
41
|
-
|
42
42
|
# URIs that are being actively checked for staleness. Protects against
|
43
43
|
# import loops.
|
44
44
|
@actively_checking = Set.new
|
@@ -131,7 +131,7 @@ module Sass
|
|
131
131
|
begin
|
132
132
|
mtime = importer.mtime(uri, @options)
|
133
133
|
if mtime.nil?
|
134
|
-
|
134
|
+
with_dependency_cache {|cache| cache.delete([uri, importer])}
|
135
135
|
nil
|
136
136
|
else
|
137
137
|
mtime
|
@@ -140,11 +140,14 @@ module Sass
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def dependencies(uri, importer)
|
143
|
-
stored_mtime, dependencies =
|
143
|
+
stored_mtime, dependencies =
|
144
|
+
with_dependency_cache {|cache| Sass::Util.destructure(cache[[uri, importer]])}
|
144
145
|
|
145
146
|
if !stored_mtime || stored_mtime < mtime(uri, importer)
|
146
147
|
dependencies = compute_dependencies(uri, importer)
|
147
|
-
|
148
|
+
with_dependency_cache do |cache|
|
149
|
+
cache[[uri, importer]] = [mtime(uri, importer), dependencies]
|
150
|
+
end
|
148
151
|
end
|
149
152
|
|
150
153
|
dependencies
|
@@ -178,6 +181,17 @@ module Sass
|
|
178
181
|
def tree(uri, importer)
|
179
182
|
@parse_trees[[uri, importer]] ||= importer.find(uri, @options).to_tree
|
180
183
|
end
|
184
|
+
|
185
|
+
# Get access to the global dependency cache in a threadsafe manner.
|
186
|
+
# Inside the block, no other thread can access the dependency cache.
|
187
|
+
#
|
188
|
+
# @yieldparam cache [Hash] The hash that is the global dependency cache
|
189
|
+
# @return The value returned by the block to which this method yields
|
190
|
+
def with_dependency_cache
|
191
|
+
StalenessChecker.dependency_cache_mutex.synchronize do
|
192
|
+
yield StalenessChecker.dependencies_cache
|
193
|
+
end
|
194
|
+
end
|
181
195
|
end
|
182
196
|
end
|
183
197
|
end
|
@@ -36,7 +36,7 @@ module Sass::Script
|
|
36
36
|
# : Creates a {Color} from hue, saturation, and lightness values.
|
37
37
|
#
|
38
38
|
# \{#hsla hsla($hue, $saturation, $lightness, $alpha)}
|
39
|
-
# : Creates a {Color} from hue, saturation, lightness,
|
39
|
+
# : Creates a {Color} from hue, saturation, lightness, and alpha
|
40
40
|
# values.
|
41
41
|
#
|
42
42
|
# \{#hue hue($color)}
|
@@ -461,7 +461,7 @@ module Sass::Script
|
|
461
461
|
end
|
462
462
|
declare :hsl, [:hue, :saturation, :lightness]
|
463
463
|
|
464
|
-
# Creates a {Color} from hue, saturation, lightness,
|
464
|
+
# Creates a {Color} from hue, saturation, lightness, and alpha
|
465
465
|
# values. Uses the algorithm from the [CSS3 spec][].
|
466
466
|
#
|
467
467
|
# [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
|
@@ -1488,7 +1488,7 @@ module Sass::Script
|
|
1488
1488
|
end
|
1489
1489
|
declare :if, [:condition, :if_true, :if_false]
|
1490
1490
|
|
1491
|
-
# This function only exists as a workaround for IE7's [`content:counter`
|
1491
|
+
# This function only exists as a workaround for IE7's [`content: counter`
|
1492
1492
|
# bug][bug]. It works identically to any other plain-CSS function, except it
|
1493
1493
|
# avoids adding spaces between the argument commas.
|
1494
1494
|
#
|
@@ -1503,6 +1503,21 @@ module Sass::Script
|
|
1503
1503
|
end
|
1504
1504
|
declare :counter, [], :var_args => true
|
1505
1505
|
|
1506
|
+
# This function only exists as a workaround for IE7's [`content: counters`
|
1507
|
+
# bug][bug]. It works identically to any other plain-CSS function, except it
|
1508
|
+
# avoids adding spaces between the argument commas.
|
1509
|
+
#
|
1510
|
+
# [bug]: http://jes.st/2013/ie7s-css-breaking-content-counter-bug/
|
1511
|
+
#
|
1512
|
+
# @example
|
1513
|
+
# counters(item, ".") => counters(item,".")
|
1514
|
+
# @overload counters($args...)
|
1515
|
+
# @return [String]
|
1516
|
+
def counters(*args)
|
1517
|
+
Sass::Script::String.new("counters(#{args.map {|a| a.to_s(options)}.join(',')})")
|
1518
|
+
end
|
1519
|
+
declare :counters, [], :var_args => true
|
1520
|
+
|
1506
1521
|
private
|
1507
1522
|
|
1508
1523
|
# This method implements the pattern of transforming a numeric value into
|
data/lib/sass/script/list.rb
CHANGED
@@ -49,7 +49,8 @@ module Sass::Script
|
|
49
49
|
return "()" if value.empty?
|
50
50
|
precedence = Sass::Script::Parser.precedence_of(separator)
|
51
51
|
value.reject {|e| e.is_a?(Null)}.map do |v|
|
52
|
-
if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence
|
52
|
+
if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
|
53
|
+
separator == :space && v.is_a?(UnaryOperation) && (v.operator == :minus || v.operator == :plus)
|
53
54
|
"(#{v.to_sass(opts)})"
|
54
55
|
else
|
55
56
|
v.to_sass(opts)
|
@@ -4,9 +4,14 @@ module Sass::Script
|
|
4
4
|
#
|
5
5
|
# Currently only `-`, `/`, and `not` are unary operators.
|
6
6
|
class UnaryOperation < Node
|
7
|
-
# @
|
8
|
-
|
9
|
-
|
7
|
+
# @return [Symbol] The operation to perform
|
8
|
+
attr_reader :operator
|
9
|
+
|
10
|
+
# @return [Script::Node] The parse-tree node for the object of the operator
|
11
|
+
attr_reader :operand
|
12
|
+
|
13
|
+
# @param operand [Script::Node] See \{#operand}
|
14
|
+
# @param operator [Symbol] See \{#operator}
|
10
15
|
def initialize(operand, operator)
|
11
16
|
@operand = operand
|
12
17
|
@operator = operator
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -1045,6 +1045,7 @@ MESSAGE
|
|
1045
1045
|
:qualified_name => "identifier",
|
1046
1046
|
:expr => "expression (e.g. 1px, bold)",
|
1047
1047
|
:_selector => "selector",
|
1048
|
+
:selector_comma_sequence => "selector",
|
1048
1049
|
:simple_selector_sequence => "selector",
|
1049
1050
|
:import_arg => "file to import (string or url())",
|
1050
1051
|
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
|
data/lib/sass/selector.rb
CHANGED
@@ -346,19 +346,19 @@ module Sass
|
|
346
346
|
# A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
|
347
347
|
# It can have arguments (e.g. `:nth-child(2n+1)`).
|
348
348
|
class Pseudo < Simple
|
349
|
-
#
|
350
|
-
#
|
351
|
-
#
|
349
|
+
# Some psuedo-class-syntax selectors are actually considered
|
350
|
+
# pseudo-elements and must be treated differently. This is a list of such
|
351
|
+
# selectors
|
352
352
|
#
|
353
|
-
# @return [
|
354
|
-
|
353
|
+
# @return [Array<String>]
|
354
|
+
ACTUALLY_ELEMENTS = %w[after before first-line first-letter]
|
355
355
|
|
356
|
-
#
|
357
|
-
#
|
358
|
-
#
|
356
|
+
# Like \{#type}, but returns the type of selector this looks like, rather
|
357
|
+
# than the type it is semantically. This only differs from type for
|
358
|
+
# selectors in \{ACTUALLY\_ELEMENTS}.
|
359
359
|
#
|
360
|
-
# @return [
|
361
|
-
|
360
|
+
# @return [Symbol]
|
361
|
+
attr_reader :syntactic_type
|
362
362
|
|
363
363
|
# The name of the selector.
|
364
364
|
#
|
@@ -380,18 +380,22 @@ module Sass
|
|
380
380
|
# @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
|
381
381
|
# or nil if no argument was given
|
382
382
|
def initialize(type, name, arg)
|
383
|
-
@
|
383
|
+
@syntactic_type = type
|
384
384
|
@name = name
|
385
385
|
@arg = arg
|
386
386
|
end
|
387
387
|
|
388
|
-
|
389
|
-
|
388
|
+
# The type of the selector. `:class` if this is a pseudoclass selector,
|
389
|
+
# `:element` if it's a pseudoelement.
|
390
|
+
#
|
391
|
+
# @return [Symbol]
|
392
|
+
def type
|
393
|
+
ACTUALLY_ELEMENTS.include?(name.first) ? :element : syntactic_type
|
390
394
|
end
|
391
395
|
|
392
396
|
# @see Selector#to_a
|
393
397
|
def to_a
|
394
|
-
res = [
|
398
|
+
res = [syntactic_type == :class ? ":" : "::"] + @name
|
395
399
|
(res << "(").concat(Sass::Util.strip_string_array(@arg)) << ")" if @arg
|
396
400
|
res
|
397
401
|
end
|
@@ -405,7 +409,6 @@ module Sass
|
|
405
409
|
sel.is_a?(Pseudo) && sel.type == :element &&
|
406
410
|
(sel.name != self.name || sel.arg != self.arg)
|
407
411
|
end
|
408
|
-
return sels + [self] if final?
|
409
412
|
super
|
410
413
|
end
|
411
414
|
|
@@ -255,7 +255,7 @@ module Sass
|
|
255
255
|
# is a supersequence of the other, use that, otherwise give up.
|
256
256
|
lcs = Sass::Util.lcs(ops1, ops2)
|
257
257
|
return unless lcs == ops1 || lcs == ops2
|
258
|
-
res.unshift
|
258
|
+
res.unshift(*(ops1.size > ops2.size ? ops1 : ops2).reverse)
|
259
259
|
return res
|
260
260
|
end
|
261
261
|
|
data/lib/sass/selector/simple.rb
CHANGED
@@ -85,7 +85,7 @@ module Sass
|
|
85
85
|
sels_with_ix = Sass::Util.enum_with_index(sels)
|
86
86
|
_, i =
|
87
87
|
if self.is_a?(Pseudo) || self.is_a?(SelectorPseudoClass)
|
88
|
-
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.
|
88
|
+
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.type == :element)}
|
89
89
|
else
|
90
90
|
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
|
91
91
|
end
|
@@ -37,11 +37,16 @@ module Sass
|
|
37
37
|
@base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal))
|
38
38
|
end
|
39
39
|
|
40
|
-
|
40
|
+
def pseudo_elements
|
41
|
+
@pseudo_elements ||= (members - [base]).
|
42
|
+
select {|sel| sel.is_a?(Pseudo) && sel.type == :element}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the non-base, non-pseudo-class selectors in this sequence.
|
41
46
|
#
|
42
47
|
# @return [Set<Simple>]
|
43
48
|
def rest
|
44
|
-
@rest ||= Set.new(
|
49
|
+
@rest ||= Set.new(members - [base] - pseudo_elements)
|
45
50
|
end
|
46
51
|
|
47
52
|
# Whether or not this compound selector is the subject of the parent
|
@@ -129,9 +134,9 @@ module Sass
|
|
129
134
|
# by the time extension and unification happen,
|
130
135
|
# this exception will only ever be raised as a result of programmer error
|
131
136
|
def unify(sels, other_subject)
|
132
|
-
return unless sseq = members.inject(sels) do |
|
133
|
-
return unless
|
134
|
-
sel.unify(
|
137
|
+
return unless sseq = members.inject(sels) do |member, sel|
|
138
|
+
return unless member
|
139
|
+
sel.unify(member)
|
135
140
|
end
|
136
141
|
SimpleSequence.new(sseq, other_subject || subject?)
|
137
142
|
end
|
@@ -145,7 +150,9 @@ module Sass
|
|
145
150
|
# @param sseq [SimpleSequence]
|
146
151
|
# @return [Boolean]
|
147
152
|
def superselector?(sseq)
|
148
|
-
(base.nil? || base.eql?(sseq.base)) &&
|
153
|
+
(base.nil? || base.eql?(sseq.base)) &&
|
154
|
+
pseudo_elements.eql?(sseq.pseudo_elements) &&
|
155
|
+
rest.subset?(sseq.rest)
|
149
156
|
end
|
150
157
|
|
151
158
|
# @see Simple#to_a
|
@@ -197,8 +204,8 @@ WARNING
|
|
197
204
|
end
|
198
205
|
|
199
206
|
def _eql?(other)
|
200
|
-
other.base.eql?(self.base) &&
|
201
|
-
other.subject? == self.subject?
|
207
|
+
other.base.eql?(self.base) && other.pseudo_elements == pseudo_elements &&
|
208
|
+
Sass::Util.set_eql?(other.rest, self.rest) && other.subject? == self.subject?
|
202
209
|
end
|
203
210
|
end
|
204
211
|
end
|
@@ -124,12 +124,12 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
|
|
124
124
|
end
|
125
125
|
|
126
126
|
sel = sseq.members
|
127
|
-
parent.resolved_rules.members.each do |
|
128
|
-
if !
|
129
|
-
raise Sass::SyntaxError.new("#{
|
127
|
+
parent.resolved_rules.members.each do |member|
|
128
|
+
if !member.members.last.is_a?(Sass::Selector::SimpleSequence)
|
129
|
+
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
|
130
130
|
end
|
131
131
|
|
132
|
-
@extends[sel] = Extend.new(
|
132
|
+
@extends[sel] = Extend.new(member, sel, node, @parent_directives.dup, :not_found)
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
@@ -160,7 +160,6 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
160
160
|
|
161
161
|
to_return = ''
|
162
162
|
old_spaces = ' ' * @tabs
|
163
|
-
spaces = ' ' * (@tabs + 1)
|
164
163
|
if node.style != :compressed
|
165
164
|
if node.options[:debug_info] && !@in_directive
|
166
165
|
to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
|
data/lib/sass/util.rb
CHANGED
@@ -868,6 +868,29 @@ MSG
|
|
868
868
|
end
|
869
869
|
end
|
870
870
|
|
871
|
+
# This creates a temp file and yields it for writing. When the
|
872
|
+
# write is complete, the file is moved into the desired location.
|
873
|
+
# The atomicity of this operation is provided by the filesystem's
|
874
|
+
# rename operation.
|
875
|
+
#
|
876
|
+
# @param filename [String] The file to write to.
|
877
|
+
# @yieldparam tmpfile [Tempfile] The temp file that can be written to.
|
878
|
+
# @return The value returned by the block.
|
879
|
+
def atomic_create_and_write_file(filename)
|
880
|
+
require 'tempfile'
|
881
|
+
tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
|
882
|
+
tmp_path = tmpfile.path
|
883
|
+
tmpfile.binmode if tmpfile.respond_to?(:binmode)
|
884
|
+
result = yield tmpfile
|
885
|
+
File.rename tmpfile.path, filename
|
886
|
+
result
|
887
|
+
ensure
|
888
|
+
# close and remove the tempfile if it still exists,
|
889
|
+
# presumably due to an error during write
|
890
|
+
tmpfile.close if tmpfile
|
891
|
+
tmpfile.unlink if tmpfile
|
892
|
+
end
|
893
|
+
|
871
894
|
private
|
872
895
|
|
873
896
|
# Calculates the memoization table for the Least Common Subsequence algorithm.
|
@@ -1692,6 +1692,23 @@ foo {
|
|
1692
1692
|
SCSS
|
1693
1693
|
end
|
1694
1694
|
|
1695
|
+
def test_ambiguous_negation
|
1696
|
+
assert_renders(<<SASS, <<SCSS, :indent => ' ')
|
1697
|
+
foo
|
1698
|
+
ok: -$foo
|
1699
|
+
comma: 10px, -$foo
|
1700
|
+
needs-parens: 10px (-$foo)
|
1701
|
+
no-parens: a 50px + 60px b
|
1702
|
+
SASS
|
1703
|
+
foo {
|
1704
|
+
ok: -$foo;
|
1705
|
+
comma: 10px, -$foo;
|
1706
|
+
needs-parens: 10px (-$foo);
|
1707
|
+
no-parens: a 50px + 60px b;
|
1708
|
+
}
|
1709
|
+
SCSS
|
1710
|
+
end
|
1711
|
+
|
1695
1712
|
private
|
1696
1713
|
|
1697
1714
|
def assert_sass_to_sass(sass, options = {})
|
data/test/sass/extend_test.rb
CHANGED
@@ -1172,6 +1172,41 @@ SCSS
|
|
1172
1172
|
|
1173
1173
|
# Regression Tests
|
1174
1174
|
|
1175
|
+
def test_pseudo_element_superselector
|
1176
|
+
# Pseudo-elements shouldn't be removed in superselector calculations.
|
1177
|
+
assert_equal <<CSS, render(<<SCSS)
|
1178
|
+
a#bar, a#bar::fblthp {
|
1179
|
+
a: b; }
|
1180
|
+
CSS
|
1181
|
+
%x#bar {a: b} // Add an id to make the results have high specificity
|
1182
|
+
%y, %y::fblthp {@extend %x}
|
1183
|
+
a {@extend %y}
|
1184
|
+
SCSS
|
1185
|
+
|
1186
|
+
# Pseudo-classes can be removed when the second law allows.
|
1187
|
+
assert_equal <<CSS, render(<<SCSS)
|
1188
|
+
a#bar {
|
1189
|
+
a: b; }
|
1190
|
+
CSS
|
1191
|
+
%x#bar {a: b}
|
1192
|
+
%y, %y:fblthp {@extend %x}
|
1193
|
+
a {@extend %y}
|
1194
|
+
SCSS
|
1195
|
+
|
1196
|
+
# A few pseudo-elements can be written as pseudo-elements for historical
|
1197
|
+
# reasons. See http://www.w3.org/TR/selectors4/#pseudo-elements.
|
1198
|
+
%w[first-line first-letter before after].each do |pseudo|
|
1199
|
+
assert_equal <<CSS, render(<<SCSS)
|
1200
|
+
a#bar, a#bar:#{pseudo} {
|
1201
|
+
a: b; }
|
1202
|
+
CSS
|
1203
|
+
%x#bar {a: b}
|
1204
|
+
%y, %y:#{pseudo} {@extend %x}
|
1205
|
+
a {@extend %y}
|
1206
|
+
SCSS
|
1207
|
+
end
|
1208
|
+
end
|
1209
|
+
|
1175
1210
|
def test_nested_sibling_extend
|
1176
1211
|
assert_equal <<CSS, render(<<SCSS)
|
1177
1212
|
.parent .bar, .parent .foo {
|
data/test/sass/functions_test.rb
CHANGED
@@ -1046,6 +1046,12 @@ MSG
|
|
1046
1046
|
assert_equal('counter(item,".")', evaluate('counter(item,".")'))
|
1047
1047
|
end
|
1048
1048
|
|
1049
|
+
def test_counters
|
1050
|
+
assert_equal("counters(foo)", evaluate("counters(foo)"))
|
1051
|
+
assert_equal('counters(item,".")', evaluate('counters(item, ".")'))
|
1052
|
+
assert_equal('counters(item,".")', evaluate('counters(item,".")'))
|
1053
|
+
end
|
1054
|
+
|
1049
1055
|
def test_keyword_args_rgb
|
1050
1056
|
assert_equal(%Q{white}, evaluate("rgb($red: 255, $green: 255, $blue: 255)"))
|
1051
1057
|
end
|
data/test/sass/util_test.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require File.dirname(__FILE__) + '/../test_helper'
|
3
3
|
require 'pathname'
|
4
|
+
require 'tmpdir'
|
4
5
|
|
5
6
|
class UtilTest < Test::Unit::TestCase
|
6
7
|
include Sass::Util
|
@@ -310,4 +311,51 @@ class UtilTest < Test::Unit::TestCase
|
|
310
311
|
"UtilTest::FooBar must implement #foo") {FooBar.new.foo}
|
311
312
|
end
|
312
313
|
|
314
|
+
def test_atomic_writes
|
315
|
+
# when using normal writes, this test fails about 90% of the time.
|
316
|
+
filename = File.join(Dir.tmpdir, "test_atomic")
|
317
|
+
5.times do
|
318
|
+
writes_to_perform = %w(1 2 3 4 5 6 7 8 9).map {|i| "#{i}\n" * 100_000}
|
319
|
+
threads = writes_to_perform.map do |to_write|
|
320
|
+
Thread.new do
|
321
|
+
# To see this test fail with a normal write,
|
322
|
+
# change to the standard file open mechanism:
|
323
|
+
# open(filename, "w") do |f|
|
324
|
+
atomic_create_and_write_file(filename) do |f|
|
325
|
+
f.write(to_write)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
loop do
|
330
|
+
contents = File.exist?(filename) ? File.read(filename) : nil
|
331
|
+
next if contents.nil? || contents.size == 0
|
332
|
+
unless writes_to_perform.include?(contents)
|
333
|
+
if contents.size != writes_to_perform.first.size
|
334
|
+
fail "Incomplete write detected: was #{contents.size} characters, " +
|
335
|
+
"should have been #{writes_to_perform.first.size}"
|
336
|
+
else
|
337
|
+
fail "Corrupted read/write detected"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
break if threads.all? {|t| !t.alive?}
|
341
|
+
end
|
342
|
+
threads.each {|t| t.join}
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
class FakeError < RuntimeError; end
|
347
|
+
|
348
|
+
def test_atomic_writes_handles_exceptions
|
349
|
+
filename = File.join(Dir.tmpdir, "test_atomic_exception")
|
350
|
+
FileUtils.rm_f(filename)
|
351
|
+
tmp_filename = nil
|
352
|
+
assert_raises FakeError do
|
353
|
+
atomic_create_and_write_file(filename) do |f|
|
354
|
+
tmp_filename = f.path
|
355
|
+
raise FakeError.new "Borken"
|
356
|
+
end
|
357
|
+
end
|
358
|
+
assert !File.exist?(filename)
|
359
|
+
assert !File.exist?(tmp_filename)
|
360
|
+
end
|
313
361
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.11
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-
|
14
|
+
date: 2013-09-28 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: yard
|