sass-embedded 0.15.0 → 0.17.0

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/ext/sass/extconf.rb +3 -48
  3. data/ext/sass/package.json +5 -0
  4. data/lib/sass/compile_error.rb +1 -10
  5. data/lib/sass/compile_result.rb +1 -5
  6. data/lib/sass/embedded/channel.rb +2 -2
  7. data/lib/sass/embedded/compile_context/function_registry.rb +85 -0
  8. data/lib/sass/embedded/compile_context/importer_registry.rb +99 -0
  9. data/lib/sass/embedded/compile_context/logger_registry.rb +45 -0
  10. data/lib/sass/embedded/compile_context/value_protofier.rb +237 -0
  11. data/lib/sass/embedded/compile_context.rb +31 -236
  12. data/lib/sass/embedded/compiler.rb +10 -43
  13. data/lib/sass/embedded/{render.rb → legacy.rb} +41 -0
  14. data/lib/sass/embedded/observer.rb +2 -0
  15. data/lib/sass/embedded/protofier.rb +91 -0
  16. data/lib/sass/embedded/structifier.rb +30 -0
  17. data/lib/sass/embedded/varint.rb +33 -0
  18. data/lib/sass/embedded/version.rb +1 -1
  19. data/lib/sass/embedded/version_context.rb +2 -3
  20. data/lib/sass/embedded.rb +39 -52
  21. data/lib/sass/embedded_protocol.rb +0 -6
  22. data/lib/sass/logger/source_location.rb +3 -9
  23. data/lib/sass/logger/source_span.rb +1 -13
  24. data/lib/sass/logger.rb +3 -3
  25. data/lib/sass/script_error.rb +5 -0
  26. data/lib/sass/value/argument_list.rb +24 -0
  27. data/lib/sass/value/boolean.rb +42 -0
  28. data/lib/sass/value/color.rb +213 -0
  29. data/lib/sass/value/function.rb +35 -0
  30. data/lib/sass/value/fuzzy_math.rb +81 -0
  31. data/lib/sass/value/list.rb +60 -0
  32. data/lib/sass/value/map.rb +57 -0
  33. data/lib/sass/value/null.rb +39 -0
  34. data/lib/sass/value/number/unit.rb +186 -0
  35. data/lib/sass/value/number.rb +272 -0
  36. data/lib/sass/value/string.rb +43 -0
  37. data/lib/sass/value.rb +92 -0
  38. data/lib/sass.rb +51 -48
  39. metadata +31 -53
  40. data/lib/sass/embedded/compiler/requirements.rb +0 -9
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's map type.
6
+ class Map < Sass::Value
7
+ def initialize(contents = {}) # rubocop:disable Lint/MissingSuper
8
+ @contents = contents.freeze
9
+ end
10
+
11
+ attr_reader :contents
12
+
13
+ def separator
14
+ contents.empty? ? nil : ','
15
+ end
16
+
17
+ def assert_map(_name = nil)
18
+ self
19
+ end
20
+
21
+ def to_map
22
+ self
23
+ end
24
+
25
+ def to_a
26
+ contents.to_a.map { |entry| Sass::Value::List.new(entry, separator: ' ') }
27
+ end
28
+
29
+ def at(index)
30
+ if index.is_a? Numeric
31
+ index = index.floor
32
+ index = to_a.length + index if index.negative?
33
+ return nil if index.negative? || index >= to_a.length
34
+
35
+ to_a[index]
36
+ else
37
+ contents[index]
38
+ end
39
+ end
40
+
41
+ def ==(other)
42
+ (other.is_a?(Sass::Value::Map) && other.contents == contents) ||
43
+ (contents.empty? && other.is_a?(Sass::Value::List) && other.to_a.empty?)
44
+ end
45
+
46
+ def hash
47
+ @hash ||= contents.hash
48
+ end
49
+
50
+ private
51
+
52
+ def to_a_length
53
+ contents.length
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's null type.
6
+ class Null < Sass::Value
7
+ def initialize # rubocop:disable Lint/MissingSuper
8
+ @value = nil
9
+ end
10
+
11
+ attr_reader :value
12
+
13
+ alias to_nil value
14
+
15
+ def to_bool
16
+ false
17
+ end
18
+
19
+ def ==(other)
20
+ other.is_a?(Sass::Value::Null)
21
+ end
22
+
23
+ def hash
24
+ @hash ||= value.hash
25
+ end
26
+
27
+ def !
28
+ Boolean::TRUE
29
+ end
30
+
31
+ # Sass's null value.
32
+ NULL = Null.new
33
+
34
+ def self.new
35
+ NULL
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ class Number
6
+ # The {Unit} module.
7
+ module Unit
8
+ CONVERSIONS = {
9
+ # Length
10
+ 'in' => {
11
+ 'in' => Rational(1),
12
+ 'cm' => Rational(1, 2.54),
13
+ 'pc' => Rational(1, 6),
14
+ 'mm' => Rational(1, 25.4),
15
+ 'q' => Rational(1, 101.6),
16
+ 'pt' => Rational(1, 72),
17
+ 'px' => Rational(1, 96)
18
+ },
19
+ 'cm' => {
20
+ 'in' => Rational(2.54),
21
+ 'cm' => Rational(1),
22
+ 'pc' => Rational(2.54, 6),
23
+ 'mm' => Rational(1, 10),
24
+ 'q' => Rational(1, 40),
25
+ 'pt' => Rational(2.54, 72),
26
+ 'px' => Rational(2.54, 96)
27
+ },
28
+ 'pc' => {
29
+ 'in' => Rational(6),
30
+ 'cm' => Rational(6, 2.54),
31
+ 'pc' => Rational(1),
32
+ 'mm' => Rational(6, 25.4),
33
+ 'q' => Rational(6, 101.6),
34
+ 'pt' => Rational(1, 12),
35
+ 'px' => Rational(1, 16)
36
+ },
37
+ 'mm' => {
38
+ 'in' => Rational(25.4),
39
+ 'cm' => Rational(10),
40
+ 'pc' => Rational(25.4, 6),
41
+ 'mm' => Rational(1),
42
+ 'q' => Rational(1, 4),
43
+ 'pt' => Rational(25.4, 72),
44
+ 'px' => Rational(25.4, 96)
45
+ },
46
+ 'q' => {
47
+ 'in' => Rational(101.6),
48
+ 'cm' => Rational(40),
49
+ 'pc' => Rational(101.6, 6),
50
+ 'mm' => Rational(4),
51
+ 'q' => Rational(1),
52
+ 'pt' => Rational(101.6, 72),
53
+ 'px' => Rational(101.6, 96)
54
+ },
55
+ 'pt' => {
56
+ 'in' => Rational(72),
57
+ 'cm' => Rational(72, 2.54),
58
+ 'pc' => Rational(12),
59
+ 'mm' => Rational(72, 25.4),
60
+ 'q' => Rational(72, 101.6),
61
+ 'pt' => Rational(1),
62
+ 'px' => Rational(3, 4)
63
+ },
64
+ 'px' => {
65
+ 'in' => Rational(96),
66
+ 'cm' => Rational(96, 2.54),
67
+ 'pc' => Rational(16),
68
+ 'mm' => Rational(96, 25.4),
69
+ 'q' => Rational(96, 101.6),
70
+ 'pt' => Rational(4, 3),
71
+ 'px' => Rational(1)
72
+ },
73
+
74
+ # Rotation
75
+ 'deg' => {
76
+ 'deg' => Rational(1),
77
+ 'grad' => Rational(9, 10),
78
+ 'rad' => Rational(180, Math::PI),
79
+ 'turn' => Rational(360)
80
+ },
81
+ 'grad' => {
82
+ 'deg' => Rational(10, 9),
83
+ 'grad' => Rational(1),
84
+ 'rad' => Rational(200, Math::PI),
85
+ 'turn' => Rational(400)
86
+ },
87
+ 'rad' => {
88
+ 'deg' => Rational(Math::PI, 180),
89
+ 'grad' => Rational(Math::PI, 200),
90
+ 'rad' => Rational(1),
91
+ 'turn' => Rational(2 * Math::PI)
92
+ },
93
+ 'turn' => {
94
+ 'deg' => Rational(1, 360),
95
+ 'grad' => Rational(1, 400),
96
+ 'rad' => Rational(1, 2 * Math::PI),
97
+ 'turn' => Rational(1)
98
+ },
99
+
100
+ # Time
101
+ 's' => {
102
+ 's' => Rational(1),
103
+ 'ms' => Rational(1, 1000)
104
+ },
105
+ 'ms' => {
106
+ 's' => Rational(1000),
107
+ 'ms' => Rational(1)
108
+ },
109
+
110
+ # Frequency
111
+ 'Hz' => {
112
+ 'Hz' => Rational(1),
113
+ 'kHz' => Rational(1000)
114
+ },
115
+ 'kHz' => {
116
+ 'Hz' => Rational(1, 1000),
117
+ 'kHz' => Rational(1)
118
+ },
119
+
120
+ # Pixel density
121
+ 'dpi' => {
122
+ 'dpi' => Rational(1),
123
+ 'dpcm' => Rational(2.54),
124
+ 'dppx' => Rational(96)
125
+ },
126
+ 'dpcm' => {
127
+ 'dpi' => Rational(1, 2.54),
128
+ 'dpcm' => Rational(1),
129
+ 'dppx' => Rational(96, 2.54)
130
+ },
131
+ 'dppx' => {
132
+ 'dpi' => Rational(1, 96),
133
+ 'dpcm' => Rational(2.54, 96),
134
+ 'dppx' => Rational(1)
135
+ }
136
+ }.freeze
137
+
138
+ UNITS_BY_TYPE = {
139
+ time: %w[s ms],
140
+ frequency: %w[Hz kHz],
141
+ 'pixel density': %w[dpi dpcm dppx]
142
+ }.freeze
143
+
144
+ TYPES_BY_UNIT = UNITS_BY_TYPE.invert
145
+ .to_a
146
+ .flat_map { |pair| pair[0].map { |key| [key, pair[1]] } }
147
+ .to_h
148
+
149
+ module_function
150
+
151
+ def conversion_factor(unit1, unit2)
152
+ return 1 if unit1 == unit2
153
+
154
+ CONVERSIONS.dig(unit1, unit2)
155
+ end
156
+
157
+ def canonicalize_units(units)
158
+ return units if units.empty?
159
+
160
+ if units.length == 1
161
+ type = TYPES_BY_UNIT[units.first]
162
+ return type.nil? ? units : [UNITS_BY_TYPE[type].first]
163
+ end
164
+
165
+ units.map do |unit|
166
+ type = TYPES_BY_UNIT[unit]
167
+ type.nil? ? units : [UNITS_BY_TYPE[type].first]
168
+ end.sort
169
+ end
170
+
171
+ def canonical_multiplier(units)
172
+ units.reduce(1) do |multiplier, unit|
173
+ multiplier * canonical_multiplier_for_unit(unit)
174
+ end
175
+ end
176
+
177
+ def canonical_multiplier_for_unit(unit)
178
+ inner_map = CONVERSIONS[unit]
179
+ inner_map.nil? ? 1 : 1 / inner_map.values.first
180
+ end
181
+ end
182
+
183
+ private_constant :Unit
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's number type.
6
+ class Number < Sass::Value
7
+ def initialize(value, numerator_units = [], denominator_units = []) # rubocop:disable Lint/MissingSuper
8
+ numerator_units = [numerator_units] if numerator_units.is_a?(::String)
9
+ denominator_units = [denominator_units] if denominator_units.is_a?(::String)
10
+
11
+ unless denominator_units.empty? && numerator_units.empty?
12
+ value = value.dup
13
+ numerator_units = numerator_units.dup
14
+ new_denominator_units = []
15
+
16
+ denominator_units.each do |denominator_unit|
17
+ index = numerator_units.find_index do |numerator_unit|
18
+ factor = Unit.conversion_factor(denominator_unit, numerator_unit)
19
+ if factor.nil?
20
+ false
21
+ else
22
+ value *= factor
23
+ true
24
+ end
25
+ end
26
+ if index.nil?
27
+ new_denominator_units.push(denominator_unit)
28
+ else
29
+ numerator_units.delete_at(index)
30
+ end
31
+ end
32
+
33
+ denominator_units = new_denominator_units
34
+ end
35
+
36
+ @value = value.freeze
37
+ @numerator_units = numerator_units.freeze
38
+ @denominator_units = denominator_units.freeze
39
+ end
40
+
41
+ attr_reader :value, :numerator_units, :denominator_units
42
+
43
+ def unitless?
44
+ numerator_units.empty? && denominator_units.empty?
45
+ end
46
+
47
+ def assert_unitless(name = nil)
48
+ raise error "Expected #{self} to have no units", name unless unitless?
49
+ end
50
+
51
+ def units?
52
+ !unitless?
53
+ end
54
+
55
+ def unit?(unit)
56
+ single_unit? && numerator_units.first == unit
57
+ end
58
+
59
+ def assert_unit(unit, name = nil)
60
+ raise error "Expected #{self} to have no unit \"#{unit}\"", name unless unit?(unit)
61
+ end
62
+
63
+ def integer?
64
+ FuzzyMath.integer?(value)
65
+ end
66
+
67
+ def assert_integer(name = nil)
68
+ raise error "#{self} is not an integer", name unless integer?
69
+
70
+ to_i
71
+ end
72
+
73
+ def to_i
74
+ FuzzyMath.to_i(value)
75
+ end
76
+
77
+ def assert_between(min, max, name = nil)
78
+ FuzzyMath.assert_between(value, min, max, name)
79
+ end
80
+
81
+ def compatible_with_unit?(unit)
82
+ single_unit? && !Unit.conversion_factor(numerator_units.first, unit).nil?
83
+ end
84
+
85
+ def convert(new_numerator_units, new_denominator_units, name = nil)
86
+ Number.new(convert_value(new_numerator_units, new_denominator_units, name), new_numerator_units,
87
+ new_denominator_units)
88
+ end
89
+
90
+ def convert_value(new_numerator_units, new_denominator_units, name = nil)
91
+ coerce_or_convert_value(new_numerator_units, new_denominator_units,
92
+ coerce_unitless: false,
93
+ name: name)
94
+ end
95
+
96
+ def convert_to_match(other, name = nil, other_name = nil)
97
+ Number.new(convert_value_to_match(other, name, other_name), other.numerator_units, other.denominator_units)
98
+ end
99
+
100
+ def convert_value_to_match(other, name = nil, other_name = nil)
101
+ coerce_or_convert_value(other.numerator_units, other.denominator_units,
102
+ coerce_unitless: false,
103
+ name: name,
104
+ other: other,
105
+ other_name: other_name)
106
+ end
107
+
108
+ def coerce(new_numerator_units, new_denominator_units, name = nil)
109
+ Number.new(coerce_value(new_numerator_units, new_denominator_units, name), new_numerator_units,
110
+ new_denominator_units)
111
+ end
112
+
113
+ def coerce_value(new_numerator_units, new_denominator_units, name = nil)
114
+ coerce_or_convert_value(new_numerator_units, new_denominator_units,
115
+ coerce_unitless: true,
116
+ name: name)
117
+ end
118
+
119
+ def coerce_value_to_unit(unit, name = nil)
120
+ coerce_value([unit], [], name)
121
+ end
122
+
123
+ def coerce_to_match(other, name = nil, other_name = nil)
124
+ Number.new(coerce_value_to_match(other, name, other_name), other.numerator_units, other.denominator_units)
125
+ end
126
+
127
+ def coerce_value_to_match(other, name = nil, other_name = nil)
128
+ coerce_or_convert_value(other.numerator_units, other.denominator_units,
129
+ coerce_unitless: true,
130
+ name: name,
131
+ other: other,
132
+ other_name: other_name)
133
+ end
134
+
135
+ def assert_number(_name = nil)
136
+ self
137
+ end
138
+
139
+ def ==(other)
140
+ return false unless other.is_a? Sass::Value::Number
141
+
142
+ return false if numerator_units.length != other.numerator_units.length ||
143
+ denominator_units.length != other.denominator_units.length
144
+
145
+ return FuzzyMath.equals(value, other.value) if unitless?
146
+
147
+ if Unit.canonicalize_units(numerator_units) != Unit.canonicalize_units(other.numerator_units) &&
148
+ Unit.canonicalize_units(denominator_units) != Unit.canonicalize_units(other.denominator_units)
149
+ return false
150
+ end
151
+
152
+ FuzzyMath.equals(
153
+ (value *
154
+ Unit.canonical_multiplier(numerator_units) /
155
+ Unit.canonical_multiplier(denominator_units)),
156
+ (other.value *
157
+ Unit.canonical_multiplier(other.numerator_units) /
158
+ Unit.canonical_multiplier(other.denominator_units))
159
+ )
160
+ end
161
+
162
+ def hash
163
+ @hash ||= if unitless?
164
+ FuzzyMath.hash(value)
165
+ elsif single_unit?
166
+ FuzzyMath.hash(
167
+ value * Unit.canonical_multiplier_for_unit(numerator_units.first)
168
+ )
169
+ else
170
+ FuzzyMath.hash(
171
+ value * Unit.canonical_multiplier(numerator_units) / Unit.canonical_multiplier(denominator_units)
172
+ )
173
+ end
174
+ end
175
+
176
+ protected
177
+
178
+ def single_unit?
179
+ numerator_units.length == 1 && denominator_units.empty?
180
+ end
181
+
182
+ def coerce_or_convert_value(new_numerator_units, new_denominator_units,
183
+ coerce_unitless:,
184
+ name: nil,
185
+ other: nil,
186
+ other_name: nil)
187
+ if other && (other.numerator_units != new_denominator_units && other.denominator_units != new_denominator_units)
188
+ raise error "Expect #{other} to have units #{unit_string(new_numerator_units, new_denominator_units)}"
189
+ end
190
+
191
+ return value if numerator_units == new_numerator_units && denominator_units == new_denominator_units
192
+
193
+ return value if numerator_units == new_numerator_units && denominator_units == new_denominator_units
194
+
195
+ other_unitless = new_numerator_units.empty? && new_denominator_units.empty?
196
+
197
+ return value if coerce_unitless && (unitless? || other_unitless)
198
+
199
+ compatibility_error = lambda {
200
+ unless other.nil?
201
+ message = +"#{self} and"
202
+ message << " $#{other_name}:" unless other_name.nil?
203
+ message << " #{other} have incompatible units"
204
+ message << " (one has units and the other doesn't)" if unitless? || other_unitless
205
+ return error message, name
206
+ end
207
+
208
+ return error "Expected #{self} to have no units", name unless other_unitless
209
+
210
+ if new_numerator_units.length == 1 && new_denominator_units.empty?
211
+ type = Unit::TYPES_BY_UNIT[new_numerator_units.first]
212
+ return error "Expected #{self} to have a #{type} unit (#{Unit::UNITS_BY_TYPE[type].join(', ')})", name
213
+ end
214
+
215
+ unit_length = new_numerator_units.length + new_denominator_units.length
216
+ units = unit_string(new_numerator_units, new_denominator_units)
217
+ error "Expected #{self} to have unit#{unit_length > 1 ? 's' : ''} #{units}", name
218
+ }
219
+
220
+ result = value
221
+
222
+ old_numerator_units = numerator_units.dup
223
+ new_numerator_units.each do |new_numerator_unit|
224
+ index = old_numerator_units.find_index do |old_numerator_unit|
225
+ factor = Unit.conversion_factor(new_numerator_unit, old_numerator_unit)
226
+ if factor.nil?
227
+ false
228
+ else
229
+ result *= factor
230
+ true
231
+ end
232
+ end
233
+ raise compatibility_error.call if index.nil?
234
+
235
+ old_numerator_units.delete_at(index)
236
+ end
237
+
238
+ old_denominator_units = denominator_units.dup
239
+ new_denominator_units.each do |new_denominator_unit|
240
+ index = old_denominator_units.find_index do |old_denominator_unit|
241
+ factor = Unit.conversion_factor(new_denominator_unit, old_denominator_unit)
242
+ if factor.nil?
243
+ false
244
+ else
245
+ result /= factor
246
+ true
247
+ end
248
+ end
249
+ raise compatibility_error.call if index.nil?
250
+
251
+ old_denominator_units.delete_at(index)
252
+ end
253
+
254
+ raise compatibility_error.call unless old_numerator_units.empty? && old_denominator_units.empty?
255
+
256
+ result
257
+ end
258
+
259
+ def unit_string(numerator_units, denominator_units)
260
+ if numerator_units.empty?
261
+ return 'no units' if denominator_units.empty?
262
+
263
+ return denominator_units.length == 1 ? "#{denominator_units.first}^-1" : "(#{denominator_units.join('*')})^-1"
264
+ end
265
+
266
+ return numerator_units.join('*') if denominator_units.empty?
267
+
268
+ "#{numerator_units.join('*')}/#{denominator_units.join('*')}"
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Value
5
+ # Sass's string type.
6
+ class String < Sass::Value
7
+ def initialize(text = '', quoted: true) # rubocop:disable Lint/MissingSuper
8
+ @text = text.freeze
9
+ @quoted = quoted
10
+ end
11
+
12
+ attr_reader :text
13
+
14
+ def quoted?
15
+ @quoted
16
+ end
17
+
18
+ def sass_index_to_string_index(sass_index, name = nil)
19
+ index = sass_index.assert_number(name).assert_integer(name)
20
+ raise error('String index may not be 0', name) if index.zero?
21
+
22
+ if index.abs > text.length
23
+ raise error("Invalid index #{sass_index} for a string with #{text.length} characters",
24
+ name)
25
+ end
26
+
27
+ index.negative? ? text.length + index : index - 1
28
+ end
29
+
30
+ def ==(other)
31
+ other.is_a?(Sass::Value::String) && other.text == text
32
+ end
33
+
34
+ def hash
35
+ @hash ||= text.hash
36
+ end
37
+
38
+ def assert_string(_name = nil)
39
+ self
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/sass/value.rb ADDED
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ # The abstract base class of Sass's value types.
5
+ class Value
6
+ def to_a
7
+ [self]
8
+ end
9
+
10
+ def to_bool
11
+ true
12
+ end
13
+
14
+ def to_map
15
+ nil
16
+ end
17
+
18
+ def to_nil
19
+ self
20
+ end
21
+
22
+ def separator
23
+ nil
24
+ end
25
+
26
+ def bracketed?
27
+ false
28
+ end
29
+
30
+ def sass_index_to_array_index(sass_index, name = nil)
31
+ index = sass_index.assert_number(name).assert_integer(name)
32
+ raise error('List index may not be 0', name) if index.zero?
33
+
34
+ if index.abs > to_a_length
35
+ raise error("Invalid index #{sass_index} for a list with #{to_a_length} elements",
36
+ name)
37
+ end
38
+
39
+ index.negative? ? to_a_length + index : index - 1
40
+ end
41
+
42
+ def at(index)
43
+ index < 1 && index >= -1 ? self : nil
44
+ end
45
+
46
+ def [](index)
47
+ at(index)
48
+ end
49
+
50
+ def assert_boolean(name = nil)
51
+ raise error("#{self} is not a boolean", name)
52
+ end
53
+
54
+ def assert_calculation(name = nil)
55
+ raise error("#{self} is not a calculation", name)
56
+ end
57
+
58
+ def assert_color(name = nil)
59
+ raise error("#{self} is not a color", name)
60
+ end
61
+
62
+ def assert_function(name = nil)
63
+ raise error("#{self} is not a function", name)
64
+ end
65
+
66
+ def assert_map(name = nil)
67
+ raise error("#{self} is not a map", name)
68
+ end
69
+
70
+ def assert_number(name = nil)
71
+ raise error("#{self} is not a number", name)
72
+ end
73
+
74
+ def assert_string(name = nil)
75
+ raise error("#{self} is not a string", name)
76
+ end
77
+
78
+ def eql?(other)
79
+ self == other
80
+ end
81
+
82
+ private
83
+
84
+ def to_a_length
85
+ 1
86
+ end
87
+
88
+ def error(message, name = nil)
89
+ Sass::ScriptError.new name.nil? ? message : "$#{name}: #{message}"
90
+ end
91
+ end
92
+ end