sass 3.3.0.alpha.256 → 3.3.0.alpha.353

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/REVISION +1 -1
  2. data/Rakefile +21 -1
  3. data/VERSION +1 -1
  4. data/VERSION_DATE +1 -1
  5. data/lib/sass.rb +6 -3
  6. data/lib/sass/cache_stores/base.rb +1 -1
  7. data/lib/sass/cache_stores/chain.rb +2 -1
  8. data/lib/sass/cache_stores/filesystem.rb +2 -6
  9. data/lib/sass/cache_stores/memory.rb +1 -1
  10. data/lib/sass/cache_stores/null.rb +2 -2
  11. data/lib/sass/callbacks.rb +1 -0
  12. data/lib/sass/css.rb +6 -6
  13. data/lib/sass/engine.rb +60 -34
  14. data/lib/sass/environment.rb +3 -1
  15. data/lib/sass/error.rb +5 -5
  16. data/lib/sass/exec.rb +52 -25
  17. data/lib/sass/features.rb +0 -2
  18. data/lib/sass/importers/deprecated_path.rb +1 -1
  19. data/lib/sass/importers/filesystem.rb +8 -6
  20. data/lib/sass/logger/base.rb +3 -3
  21. data/lib/sass/logger/log_level.rb +4 -6
  22. data/lib/sass/media.rb +2 -2
  23. data/lib/sass/plugin.rb +4 -2
  24. data/lib/sass/plugin/compiler.rb +28 -15
  25. data/lib/sass/plugin/configuration.rb +15 -7
  26. data/lib/sass/plugin/merb.rb +1 -1
  27. data/lib/sass/plugin/staleness_checker.rb +24 -8
  28. data/lib/sass/repl.rb +3 -3
  29. data/lib/sass/script.rb +2 -1
  30. data/lib/sass/script/css_lexer.rb +8 -3
  31. data/lib/sass/script/css_parser.rb +6 -2
  32. data/lib/sass/script/functions.rb +164 -109
  33. data/lib/sass/script/lexer.rb +30 -20
  34. data/lib/sass/script/parser.rb +66 -37
  35. data/lib/sass/script/tree/funcall.rb +23 -14
  36. data/lib/sass/script/tree/interpolation.rb +5 -1
  37. data/lib/sass/script/tree/list_literal.rb +5 -4
  38. data/lib/sass/script/tree/map_literal.rb +1 -1
  39. data/lib/sass/script/tree/node.rb +2 -2
  40. data/lib/sass/script/tree/operation.rb +2 -1
  41. data/lib/sass/script/tree/selector.rb +3 -2
  42. data/lib/sass/script/tree/string_interpolation.rb +2 -1
  43. data/lib/sass/script/tree/variable.rb +4 -3
  44. data/lib/sass/script/value/base.rb +12 -14
  45. data/lib/sass/script/value/color.rb +35 -16
  46. data/lib/sass/script/value/helpers.rb +146 -0
  47. data/lib/sass/script/value/list.rb +24 -5
  48. data/lib/sass/script/value/map.rb +1 -1
  49. data/lib/sass/script/value/null.rb +13 -3
  50. data/lib/sass/script/value/number.rb +44 -35
  51. data/lib/sass/script/value/string.rb +2 -2
  52. data/lib/sass/scss/css_parser.rb +2 -1
  53. data/lib/sass/scss/parser.rb +143 -93
  54. data/lib/sass/scss/rx.rb +4 -4
  55. data/lib/sass/scss/script_lexer.rb +1 -0
  56. data/lib/sass/scss/script_parser.rb +1 -0
  57. data/lib/sass/scss/static_parser.rb +5 -5
  58. data/lib/sass/selector.rb +5 -2
  59. data/lib/sass/selector/abstract_sequence.rb +1 -1
  60. data/lib/sass/selector/comma_sequence.rb +16 -14
  61. data/lib/sass/selector/sequence.rb +38 -24
  62. data/lib/sass/selector/simple.rb +4 -4
  63. data/lib/sass/selector/simple_sequence.rb +21 -11
  64. data/lib/sass/source/map.rb +7 -2
  65. data/lib/sass/source/range.rb +1 -1
  66. data/lib/sass/supports.rb +3 -3
  67. data/lib/sass/tree/debug_node.rb +1 -1
  68. data/lib/sass/tree/function_node.rb +2 -1
  69. data/lib/sass/tree/if_node.rb +1 -1
  70. data/lib/sass/tree/import_node.rb +3 -4
  71. data/lib/sass/tree/prop_node.rb +4 -2
  72. data/lib/sass/tree/rule_node.rb +5 -2
  73. data/lib/sass/tree/visitors/base.rb +6 -6
  74. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  75. data/lib/sass/tree/visitors/convert.rb +34 -28
  76. data/lib/sass/tree/visitors/cssize.rb +4 -3
  77. data/lib/sass/tree/visitors/deep_copy.rb +1 -0
  78. data/lib/sass/tree/visitors/perform.rb +31 -16
  79. data/lib/sass/tree/visitors/to_css.rb +34 -16
  80. data/lib/sass/util.rb +88 -37
  81. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  82. data/lib/sass/util/ordered_hash.rb +20 -18
  83. data/lib/sass/util/subset_map.rb +3 -2
  84. data/lib/sass/util/test.rb +0 -1
  85. data/lib/sass/version.rb +9 -5
  86. data/test/rubocop_extensions.rb +70 -0
  87. data/test/sass/functions_test.rb +20 -1
  88. data/test/sass/importer_test.rb +2 -1
  89. data/test/sass/script_test.rb +4 -0
  90. data/test/sass/source_map_test.rb +1 -1
  91. data/test/sass/util_test.rb +49 -0
  92. data/test/sass/value_helpers_test.rb +181 -0
  93. metadata +13 -9
@@ -31,7 +31,10 @@ module Sass::Script::Tree
31
31
  # @param wb [Boolean] See {Interpolation#whitespace_before}
32
32
  # @param wa [Boolean] See {Interpolation#whitespace_after}
33
33
  # @param originally_text [Boolean] See {Interpolation#originally_text}
34
+ # @comment
35
+ # rubocop:disable ParameterLists
34
36
  def initialize(before, mid, after, wb, wa, originally_text = false)
37
+ # rubocop:enable ParameterLists
35
38
  @before = before
36
39
  @mid = mid
37
40
  @after = after
@@ -81,7 +84,8 @@ module Sass::Script::Tree
81
84
  # Evaluates the interpolation.
82
85
  #
83
86
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
84
- # @return [Sass::Script::Value::String] The SassScript string that is the value of the interpolation
87
+ # @return [Sass::Script::Value::String]
88
+ # The SassScript string that is the value of the interpolation
85
89
  def _perform(environment)
86
90
  res = ""
87
91
  res << @before.perform(environment).to_s if @before
@@ -31,7 +31,8 @@ module Sass::Script::Tree
31
31
  precedence = Sass::Script::Parser.precedence_of(separator)
32
32
  elements.reject {|e| e.is_a?(Sass::Script::Value::Null)}.map do |v|
33
33
  if v.is_a?(ListLiteral) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
34
- separator == :space && v.is_a?(UnaryOperation) && (v.operator == :minus || v.operator == :plus)
34
+ separator == :space && v.is_a?(UnaryOperation) &&
35
+ (v.operator == :minus || v.operator == :plus)
35
36
  "(#{v.to_sass(opts)})"
36
37
  else
37
38
  v.to_sass(opts)
@@ -60,16 +61,16 @@ module Sass::Script::Tree
60
61
  list.line = line
61
62
  list.source_range = source_range
62
63
  list.filename = filename
63
- list.options = self.options
64
+ list.options = options
64
65
  list
65
66
  end
66
67
 
67
68
  private
68
69
 
69
- def sep_str(opts = self.options)
70
+ def sep_str(opts = options)
70
71
  return ' ' if separator == :space
71
72
  return ',' if opts && opts[:style] == :compressed
72
- return ', '
73
+ ', '
73
74
  end
74
75
  end
75
76
  end
@@ -57,7 +57,7 @@ module Sass::Script::Tree
57
57
  keys << k
58
58
  [k, v]
59
59
  end))
60
- map.options = self.options
60
+ map.options = options
61
61
  map
62
62
  end
63
63
  end
@@ -32,7 +32,7 @@ module Sass::Script::Tree
32
32
  @options = options
33
33
  children.each do |c|
34
34
  if c.is_a? Hash
35
- c.values.each {|v| v.options = options }
35
+ c.values.each {|v| v.options = options}
36
36
  else
37
37
  c.options = options
38
38
  end
@@ -80,7 +80,7 @@ module Sass::Script::Tree
80
80
  # Converts underscores to dashes if the :dasherize option is set.
81
81
  def dasherize(s, opts)
82
82
  if opts[:dasherize]
83
- s.gsub(/_/,'-')
83
+ s.gsub(/_/, '-')
84
84
  else
85
85
  s
86
86
  end
@@ -74,7 +74,8 @@ module Sass::Script::Tree
74
74
 
75
75
  if (value1.is_a?(Sass::Script::Value::Null) || value2.is_a?(Sass::Script::Value::Null)) &&
76
76
  @operator != :eq && @operator != :neq
77
- raise Sass::SyntaxError.new("Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
77
+ raise Sass::SyntaxError.new(
78
+ "Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
78
79
  end
79
80
 
80
81
  begin
@@ -12,13 +12,14 @@ module Sass::Script::Tree
12
12
  end
13
13
 
14
14
  def deep_copy
15
- self.dup
15
+ dup
16
16
  end
17
17
 
18
18
  protected
19
19
 
20
20
  def _perform(environment)
21
- return opts(Sass::Script::Value::Null.new) unless selector = environment.selector
21
+ selector = environment.selector
22
+ return opts(Sass::Script::Value::Null.new) unless selector
22
23
  opts(Sass::Script::Value::List.new(selector.members.map do |seq|
23
24
  Sass::Script::Value::List.new(seq.members.map do |component|
24
25
  Sass::Script::Value::String.new(component.to_s)
@@ -74,7 +74,8 @@ module Sass::Script::Tree
74
74
  # Evaluates the interpolation.
75
75
  #
76
76
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
77
- # @return [Sass::Script::Value::String] The SassScript string that is the value of the interpolation
77
+ # @return [Sass::Script::Value::String]
78
+ # The SassScript string that is the value of the interpolation
78
79
  def _perform(environment)
79
80
  res = ""
80
81
  before = @before.perform(environment)
@@ -14,7 +14,7 @@ module Sass::Script::Tree
14
14
  # @param name [String] See \{#name}
15
15
  def initialize(name)
16
16
  @name = name
17
- @underscored_name = name.gsub(/-/,"_")
17
+ @underscored_name = name.gsub(/-/, "_")
18
18
  super()
19
19
  end
20
20
 
@@ -45,12 +45,13 @@ module Sass::Script::Tree
45
45
  # @return [Sass::Script::Value] The SassScript object that is the value of the variable
46
46
  # @raise [Sass::SyntaxError] if the variable is undefined
47
47
  def _perform(environment)
48
- raise Sass::SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
48
+ val = environment.var(name)
49
+ raise Sass::SyntaxError.new("Undefined variable: \"$#{name}\".") unless val
49
50
  if val.is_a?(Sass::Script::Value::Number)
50
51
  val = val.dup
51
52
  val.original = nil
52
53
  end
53
- return val
54
+ val
54
55
  end
55
56
  end
56
57
  end
@@ -38,9 +38,7 @@ module Sass::Script::Value
38
38
  # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
39
39
  #
40
40
  # @param options [{Symbol => Object}] The options
41
- def options=(options)
42
- @options = options
43
- end
41
+ attr_writer :options
44
42
 
45
43
  # Returns the options hash for this node.
46
44
  #
@@ -66,7 +64,7 @@ MSG
66
64
  # @return [Sass::Script::Value::Bool] True if this value is the same as the other,
67
65
  # false otherwise
68
66
  def eq(other)
69
- Sass::Script::Value::Bool.new(self.class == other.class && self.value == other.value)
67
+ Sass::Script::Value::Bool.new(self.class == other.class && value == other.value)
70
68
  end
71
69
 
72
70
  # The SassScript `!=` operation.
@@ -98,7 +96,7 @@ MSG
98
96
  # @return [Script::Value::String] A string containing both values
99
97
  # separated by `"="`
100
98
  def single_eq(other)
101
- Sass::Script::Value::String.new("#{self.to_s}=#{other.to_s}")
99
+ Sass::Script::Value::String.new("#{to_s}=#{other.to_s}")
102
100
  end
103
101
 
104
102
  # The SassScript `+` operation.
@@ -108,9 +106,9 @@ MSG
108
106
  # without any separation
109
107
  def plus(other)
110
108
  if other.is_a?(Sass::Script::Value::String)
111
- return Sass::Script::Value::String.new(self.to_s + other.value, other.type)
109
+ return Sass::Script::Value::String.new(to_s + other.value, other.type)
112
110
  end
113
- Sass::Script::Value::String.new(self.to_s + other.to_s)
111
+ Sass::Script::Value::String.new(to_s + other.to_s)
114
112
  end
115
113
 
116
114
  # The SassScript `-` operation.
@@ -119,7 +117,7 @@ MSG
119
117
  # @return [Script::Value::String] A string containing both values
120
118
  # separated by `"-"`
121
119
  def minus(other)
122
- Sass::Script::Value::String.new("#{self.to_s}-#{other.to_s}")
120
+ Sass::Script::Value::String.new("#{to_s}-#{other.to_s}")
123
121
  end
124
122
 
125
123
  # The SassScript `/` operation.
@@ -128,7 +126,7 @@ MSG
128
126
  # @return [Script::Value::String] A string containing both values
129
127
  # separated by `"/"`
130
128
  def div(other)
131
- Sass::Script::Value::String.new("#{self.to_s}/#{other.to_s}")
129
+ Sass::Script::Value::String.new("#{to_s}/#{other.to_s}")
132
130
  end
133
131
 
134
132
  # The SassScript unary `+` operation (e.g. `+$a`).
@@ -137,7 +135,7 @@ MSG
137
135
  # @return [Script::Value::String] A string containing the value
138
136
  # preceded by `"+"`
139
137
  def unary_plus
140
- Sass::Script::Value::String.new("+#{self.to_s}")
138
+ Sass::Script::Value::String.new("+#{to_s}")
141
139
  end
142
140
 
143
141
  # The SassScript unary `-` operation (e.g. `-$a`).
@@ -146,7 +144,7 @@ MSG
146
144
  # @return [Script::Value::String] A string containing the value
147
145
  # preceded by `"-"`
148
146
  def unary_minus
149
- Sass::Script::Value::String.new("-#{self.to_s}")
147
+ Sass::Script::Value::String.new("-#{to_s}")
150
148
  end
151
149
 
152
150
  # The SassScript unary `/` operation (e.g. `/$a`).
@@ -155,7 +153,7 @@ MSG
155
153
  # @return [Script::Value::String] A string containing the value
156
154
  # preceded by `"/"`
157
155
  def unary_div
158
- Sass::Script::Value::String.new("/#{self.to_s}")
156
+ Sass::Script::Value::String.new("/#{to_s}")
159
157
  end
160
158
 
161
159
  # Returns the hash code of this value. Two objects' hash codes should be
@@ -191,7 +189,7 @@ MSG
191
189
  # @return [Fixnum] The integer value of this value
192
190
  # @raise [Sass::SyntaxError] if this value isn't an integer
193
191
  def to_i
194
- raise Sass::SyntaxError.new("#{self.inspect} is not an integer.")
192
+ raise Sass::SyntaxError.new("#{inspect} is not an integer.")
195
193
  end
196
194
 
197
195
  # @raise [Sass::SyntaxError] if this value isn't an integer
@@ -218,7 +216,7 @@ MSG
218
216
  # @return [Hash<Value, Value>] This value as a hash
219
217
  # @raise [Sass::SyntaxError] if this value doesn't have a hash representation
220
218
  def to_h
221
- raise Sass::SyntaxError.new("#{self.inspect} is not a map.")
219
+ raise Sass::SyntaxError.new("#{inspect} is not a map.")
222
220
  end
223
221
 
224
222
  # Returns the string representation of this value
@@ -248,6 +248,24 @@ module Sass::Script::Value
248
248
  @attrs[:alpha] = Sass::Util.check_range("Alpha channel", 0..1, @attrs[:alpha])
249
249
  end
250
250
 
251
+ # Create a new color from a valid CSS hex string.
252
+ #
253
+ # The leading hash is optional.
254
+ #
255
+ # @return [Color]
256
+ def self.from_hex(hex_string, alpha = nil)
257
+ unless hex_string =~ /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i ||
258
+ hex_string =~ /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i
259
+ raise ArgumentError.new("#{hex_string.inspect} is not a valid hex color.")
260
+ end
261
+ red = $1.ljust(2, $1).to_i(16)
262
+ green = $2.ljust(2, $2).to_i(16)
263
+ blue = $3.ljust(2, $3).to_i(16)
264
+ attrs = {:red => red, :green => green, :blue => blue}
265
+ attrs[:alpha] = alpha if alpha
266
+ new(attrs)
267
+ end
268
+
251
269
  # The red component of the color.
252
270
  #
253
271
  # @return [Fixnum]
@@ -520,7 +538,7 @@ module Sass::Script::Value
520
538
  def to_s(opts = {})
521
539
  return smallest if options[:style] == :compressed
522
540
  return COLOR_NAMES_REVERSE[rgba] if COLOR_NAMES_REVERSE[rgba]
523
- return alpha? ? rgba_str : hex_str
541
+ alpha? ? rgba_str : hex_str
524
542
  end
525
543
  alias_method :to_sass, :to_s
526
544
 
@@ -537,7 +555,7 @@ module Sass::Script::Value
537
555
  small_explicit_str = alpha? ? rgba_str : hex_str.gsub(/^#(.)\1(.)\2(.)\3$/, '#\1\2\3')
538
556
  return small_explicit_str unless (color = COLOR_NAMES_REVERSE[rgba]) &&
539
557
  color.size <= small_explicit_str.size
540
- return color
558
+ color
541
559
  end
542
560
 
543
561
  def rgba_str
@@ -546,20 +564,21 @@ module Sass::Script::Value
546
564
  end
547
565
 
548
566
  def hex_str
549
- red, green, blue = rgb.map { |num| num.to_s(16).rjust(2, '0') }
567
+ red, green, blue = rgb.map {|num| num.to_s(16).rjust(2, '0')}
550
568
  "##{red}#{green}#{blue}"
551
569
  end
552
570
 
553
571
  def piecewise(other, operation)
554
572
  other_num = other.is_a? Number
555
573
  if other_num && !other.unitless?
556
- raise Sass::SyntaxError.new("Cannot add a number with units (#{other}) to a color (#{self}).")
574
+ raise Sass::SyntaxError.new(
575
+ "Cannot add a number with units (#{other}) to a color (#{self}).")
557
576
  end
558
577
 
559
578
  result = []
560
- for i in (0...3)
579
+ (0...3).each do |i|
561
580
  res = rgb[i].send(operation, other_num ? other.value : other.rgb[i])
562
- result[i] = [ [res, 255].min, 0 ].max
581
+ result[i] = [[res, 255].min, 0].max
563
582
  end
564
583
 
565
584
  if !other_num && other.alpha != alpha
@@ -580,9 +599,9 @@ module Sass::Script::Value
580
599
  m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
581
600
  m1 = l * 2 - m2
582
601
  @attrs[:red], @attrs[:green], @attrs[:blue] = [
583
- hue_to_rgb(m1, m2, h + 1.0/3),
602
+ hue_to_rgb(m1, m2, h + 1.0 / 3),
584
603
  hue_to_rgb(m1, m2, h),
585
- hue_to_rgb(m1, m2, h - 1.0/3)
604
+ hue_to_rgb(m1, m2, h - 1.0 / 3)
586
605
  ].map {|c| (c * 0xff).round}
587
606
  end
588
607
 
@@ -591,8 +610,8 @@ module Sass::Script::Value
591
610
  h -= 1 if h > 1
592
611
  return m1 + (m2 - m1) * h * 6 if h * 6 < 1
593
612
  return m2 if h * 2 < 1
594
- return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
595
- return m1
613
+ return m1 + (m2 - m1) * (2.0 / 3 - h) * 6 if h * 3 < 2
614
+ m1
596
615
  end
597
616
 
598
617
  def rgb_to_hsl!
@@ -607,20 +626,20 @@ module Sass::Script::Value
607
626
  h =
608
627
  case max
609
628
  when min; 0
610
- when r; 60 * (g-b)/d
611
- when g; 60 * (b-r)/d + 120
612
- when b; 60 * (r-g)/d + 240
629
+ when r; 60 * (g - b) / d
630
+ when g; 60 * (b - r) / d + 120
631
+ when b; 60 * (r - g) / d + 240
613
632
  end
614
633
 
615
- l = (max + min)/2.0
634
+ l = (max + min) / 2.0
616
635
 
617
636
  s =
618
637
  if max == min
619
638
  0
620
639
  elsif l < 0.5
621
- d/(2*l)
640
+ d / (2 * l)
622
641
  else
623
- d/(2 - 2*l)
642
+ d / (2 - 2 * l)
624
643
  end
625
644
 
626
645
  @attrs[:hue] = h % 360
@@ -0,0 +1,146 @@
1
+ module Sass::Script::Value
2
+ # Provides helper functions for creating sass values from within ruby methods.
3
+ # @since `3.3.0`
4
+ module Helpers
5
+ # Construct a Sass Boolean.
6
+ #
7
+ # @param value [Object] A ruby object that will be tested for truthiness.
8
+ # @return [Sass::Script::Value::Bool] whether the ruby value is truthy.
9
+ def bool(value)
10
+ Bool.new(value)
11
+ end
12
+
13
+ # Construct a Sass Color from a hex color string.
14
+ #
15
+ # @param value [::String] A string representing a hex color.
16
+ # The leading hash ("#") is optional.
17
+ # @param alpha [::Number] The alpha channel. A number between 0 and 1.
18
+ # @return [Sass::Script::Value::Color] the color object
19
+ def hex_color(value, alpha = nil)
20
+ Color.from_hex(value, alpha)
21
+ end
22
+
23
+ # Construct a Sass Color from hsl values.
24
+ #
25
+ # @param hue [::Number] The hue of the color in degrees.
26
+ # A non-negative number, usually less than 360.
27
+ # @param saturation [::Number] The saturation of the color.
28
+ # Must be between 0 and 100 inclusive.
29
+ # @param lightness [::Number] The lightness of the color.
30
+ # Must be between 0 and 100 inclusive.
31
+ # @param alpha [::Number] The alpha channel. A number between 0 and 1.
32
+ #
33
+ # @return [Sass::Script::Value::Color] the color object
34
+ def hsl_color(hue, saturation, lightness, alpha = nil)
35
+ attrs = {:hue => hue, :saturation => saturation, :lightness => lightness}
36
+ attrs[:alpha] = alpha if alpha
37
+ Color.new(attrs)
38
+ end
39
+
40
+ # Construct a Sass Color from rgb values.
41
+ #
42
+ # @param red [::Number] The red component. Must be between 0 and 255 inclusive.
43
+ # @param green [::Number] The green component. Must be between 0 and 255 inclusive.
44
+ # @param blue [::Number] The blue component. Must be between 0 and 255 inclusive.
45
+ # @param alpha [::Number] The alpha channel. A number between 0 and 1.
46
+ #
47
+ # @return [Sass::Script::Value::Color] the color object
48
+ def rgb_color(red, green, blue, alpha = nil)
49
+ attrs = {:red => red, :green => green, :blue => blue}
50
+ attrs[:alpha] = alpha if alpha
51
+ Color.new(attrs)
52
+ end
53
+
54
+ # Construct a Sass Number from a ruby number.
55
+ #
56
+ # @param number [::Number] A numeric value.
57
+ # @param unit_string [::String] A unit string of the form
58
+ # `numeral_unit1 * numeral_unit2 ... / denominator_unit1 * denominator_unit2 ...`
59
+ # this is the same format that is returned by
60
+ # {Sass::Script::Value::Number#unit_str the `unit_str` method}
61
+ #
62
+ # @see Sass::Script::Value::Number#unit_str
63
+ #
64
+ # @return [Sass::Script::Value::Number] The sass number representing the given ruby number.
65
+ def number(number, unit_string = nil)
66
+ Number.new(number, *parse_unit_string(unit_string))
67
+ end
68
+
69
+ # @overload list(*elements, separator)
70
+ # Create a space-separated list from the arguments given.
71
+ # @param elements [Array<Sass::Script::Value::Base>] Each argument will be a list element.
72
+ # @param separator [Symbol] Either :space or :comma.
73
+ # @return [Sass::Script::Value::List] The space separated list.
74
+ #
75
+ # @overload list(array, separator)
76
+ # Create a space-separated list from the array given.
77
+ # @param array [Array<Sass::Script::Value::Base>] A ruby array of Sass values
78
+ # to make into a list.
79
+ # @return [Sass::Script::Value::List] The space separated list.
80
+ def list(*elements)
81
+ unless elements.last.is_a?(Symbol)
82
+ raise ArgumentError.new("A list type of :space or :comma must be specified.")
83
+ end
84
+ separator = elements.pop
85
+ if elements.size == 1 && elements.first.is_a?(Array)
86
+ elements = elements.first
87
+ end
88
+ Sass::Script::Value::List.new(elements, separator)
89
+ end
90
+
91
+ # Create a sass null value.
92
+ #
93
+ # @return [Sass::Script::Value::Null]
94
+ def null
95
+ Sass::Script::Value::Null.new
96
+ end
97
+
98
+ # Create a quoted string.
99
+ #
100
+ # @param str [::String] A ruby string.
101
+ # @return [Sass::Script::Value::String] A quoted string.
102
+ def quoted_string(str)
103
+ Sass::Script::String.new(str, :string)
104
+ end
105
+
106
+ # Create an unquoted string.
107
+ #
108
+ # @param str [::String] A ruby string.
109
+ # @return [Sass::Script::Value::String] An unquoted string.
110
+ def unquoted_string(str)
111
+ Sass::Script::String.new(str, :identifier)
112
+ end
113
+ alias_method :identifier, :unquoted_string
114
+
115
+ private
116
+
117
+ # @private
118
+ VALID_UNIT = /#{Sass::SCSS::RX::NMSTART}#{Sass::SCSS::RX::NMCHAR}|%*/
119
+
120
+ # @example
121
+ # parse_unit_string("em*px/in*%") # => [["em", "px], ["in", "%"]]
122
+ #
123
+ # @param unit_string [String] A string adhering to the output of a number with complex
124
+ # units. E.g. "em*px/in*%"
125
+ # @return [Array<Array<String>>] A list of numerator units and a list of denominator units.
126
+ def parse_unit_string(unit_string)
127
+ denominator_units = numerator_units = Sass::Script::Value::Number::NO_UNITS
128
+ return numerator_units, denominator_units unless unit_string && unit_string.length > 0
129
+ num_over_denominator = unit_string.split(/ *\/ */)
130
+ unless (1..2).include?(num_over_denominator.size)
131
+ raise ArgumentError.new("Malformed unit string: #{unit_string}")
132
+ end
133
+ numerator_units = num_over_denominator[0].split(/ *\* */)
134
+ denominator_units = (num_over_denominator[1] || "").split(/ *\* */)
135
+ [[numerator_units, "numerator"], [denominator_units, "denominator"]].each do |units, name|
136
+ if unit_string =~ /\// && units.size == 0
137
+ raise ArgumentError.new("Malformed unit string: #{unit_string}")
138
+ end
139
+ if units.any? {|unit| unit !~ VALID_UNIT}
140
+ raise ArgumentError.new("Malformed #{name} in unit string: #{unit_string}")
141
+ end
142
+ end
143
+ [numerator_units, denominator_units]
144
+ end
145
+ end
146
+ end