sass 3.3.0.rc.1 → 3.3.0.rc.2

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 (78) hide show
  1. data/Rakefile +1 -1
  2. data/VERSION +1 -1
  3. data/VERSION_DATE +1 -1
  4. data/lib/sass.rb +5 -0
  5. data/lib/sass/engine.rb +3 -5
  6. data/lib/sass/plugin.rb +0 -1
  7. data/lib/sass/plugin/compiler.rb +1 -2
  8. data/lib/sass/script/functions.rb +16 -2
  9. data/lib/sass/script/lexer.rb +22 -12
  10. data/lib/sass/script/parser.rb +27 -14
  11. data/lib/sass/script/tree/variable.rb +1 -1
  12. data/lib/sass/script/value/base.rb +1 -1
  13. data/lib/sass/script/value/color.rb +29 -17
  14. data/lib/sass/script/value/list.rb +1 -1
  15. data/lib/sass/script/value/number.rb +8 -1
  16. data/lib/sass/scss/parser.rb +2 -2
  17. data/lib/sass/selector/sequence.rb +18 -19
  18. data/lib/sass/selector/simple_sequence.rb +5 -5
  19. data/lib/sass/source/map.rb +1 -1
  20. data/lib/sass/tree/node.rb +25 -0
  21. data/lib/sass/tree/variable_node.rb +5 -0
  22. data/lib/sass/tree/visitors/base.rb +4 -7
  23. data/lib/sass/tree/visitors/check_nesting.rb +2 -2
  24. data/lib/sass/tree/visitors/perform.rb +12 -7
  25. data/lib/sass/util.rb +95 -50
  26. data/lib/sass/util/normalized_map.rb +63 -14
  27. data/lib/sass/util/ordered_hash.rb +9 -5
  28. data/lib/sass/version.rb +10 -12
  29. data/test/sass/engine_test.rb +37 -0
  30. data/test/sass/functions_test.rb +9 -2
  31. data/test/sass/importer_test.rb +3 -3
  32. data/test/sass/script_test.rb +12 -10
  33. data/test/sass/source_map_test.rb +8 -8
  34. data/test/sass/util/normalized_map_test.rb +22 -1
  35. data/test/sass/util_test.rb +18 -0
  36. data/test/test_helper.rb +16 -0
  37. data/vendor/listen/CHANGELOG.md +228 -0
  38. data/vendor/listen/CONTRIBUTING.md +38 -0
  39. data/vendor/listen/Gemfile +30 -0
  40. data/vendor/listen/Guardfile +8 -0
  41. data/vendor/listen/LICENSE +20 -0
  42. data/vendor/listen/README.md +315 -0
  43. data/vendor/listen/Rakefile +47 -0
  44. data/vendor/listen/Vagrantfile +96 -0
  45. data/vendor/listen/lib/listen.rb +40 -0
  46. data/vendor/listen/lib/listen/adapter.rb +214 -0
  47. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  48. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  49. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  50. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  51. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  52. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  53. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  54. data/vendor/listen/lib/listen/listener.rb +225 -0
  55. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  56. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  57. data/vendor/listen/lib/listen/version.rb +3 -0
  58. data/vendor/listen/listen.gemspec +22 -0
  59. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  60. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  61. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  62. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  63. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  64. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  65. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  66. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  67. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  68. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  69. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  70. data/vendor/listen/spec/listen_spec.rb +73 -0
  71. data/vendor/listen/spec/spec_helper.rb +21 -0
  72. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  73. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  74. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  75. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  76. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  77. metadata +318 -300
  78. data/test/Gemfile.lock +0 -10
@@ -93,7 +93,7 @@ module Sass
93
93
  choices
94
94
  end
95
95
  weaves = Sass::Util.paths(extended_not_expanded).map {|path| weave(path)}
96
- Sass::Util.flatten(trim(weaves), 1).map {|p| Sequence.new(p)}
96
+ trim(weaves).map {|p| Sequence.new(p)}
97
97
  end
98
98
 
99
99
  # Returns whether or not this selector matches all elements
@@ -137,32 +137,31 @@ module Sass
137
137
 
138
138
  private
139
139
 
140
- # Conceptually, this expands "parenthesized selectors".
141
- # That is, if we have `.A .B {@extend .C}` and `.D .C {...}`,
142
- # this conceptually expands into `.D .C, .D (.A .B)`,
143
- # and this function translates `.D (.A .B)` into `.D .A .B, .A.D .B, .D .A .B`.
140
+ # Conceptually, this expands "parenthesized selectors". That is, if we
141
+ # have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually expands
142
+ # into `.D .C, .D (.A .B)`, and this function translates `.D (.A .B)` into
143
+ # `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B` would also be
144
+ # required, but including merged selectors results in exponential output
145
+ # for very little gain.
144
146
  #
145
147
  # @param path [Array<Array<SimpleSequence or String>>]
146
148
  # A list of parenthesized selector groups.
147
149
  # @return [Array<Array<SimpleSequence or String>>] A list of fully-expanded selectors.
148
150
  def weave(path)
149
151
  # This function works by moving through the selector path left-to-right,
150
- # building all possible prefixes simultaneously. These prefixes are
151
- # `befores`, while the remaining parenthesized suffixes is `afters`.
152
- befores = [[]]
153
- afters = path.dup
152
+ # building all possible prefixes simultaneously.
153
+ prefixes = [[]]
154
154
 
155
- until afters.empty?
156
- current = afters.shift.dup
155
+ path.each do |current|
156
+ current = current.dup
157
157
  last_current = [current.pop]
158
- befores.map! do |before|
159
- sub = subweave(before, current)
158
+ prefixes = Sass::Util.flatten(prefixes.map do |prefix|
159
+ sub = subweave(prefix, current)
160
160
  next [] unless sub
161
161
  sub.map {|seqs| seqs + last_current}
162
- end
163
- befores = Sass::Util.flatten(befores, 1)
162
+ end, 1)
164
163
  end
165
- befores
164
+ prefixes
166
165
  end
167
166
 
168
167
  # This interweaves two lists of selectors,
@@ -455,11 +454,11 @@ module Sass
455
454
  # the other. The more specific selector is removed.
456
455
  #
457
456
  # @param seqses [Array<Array<Array<SimpleSequence or String>>>]
458
- # @return [Array<Array<Array<SimpleSequence or String>>>]
457
+ # @return [Array<Array<SimpleSequence or String>>]
459
458
  def trim(seqses)
460
459
  # Avoid truly horrific quadratic behavior. TODO: I think there
461
460
  # may be a way to get perfect trimming without going quadratic.
462
- return seqses if seqses.size > 100
461
+ return Sass::Util.flatten(seqses, 1) if seqses.size > 100
463
462
 
464
463
  # Keep the results in a separate array so we can be sure we aren't
465
464
  # comparing against an already-trimmed selector. This ensures that two
@@ -482,7 +481,7 @@ module Sass
482
481
  end
483
482
  end
484
483
  end
485
- result
484
+ Sass::Util.flatten(result, 1)
486
485
  end
487
486
 
488
487
  def _hash
@@ -107,18 +107,18 @@ module Sass
107
107
  # by extending this selector with `extends`.
108
108
  # @see CommaSequence#do_extend
109
109
  def do_extend(extends, parent_directives, seen = Set.new)
110
- groups = Sass::Util.group_by_to_a(extends.get(members.to_set)) {|ex, _| ex.extender}
110
+ groups = Sass::Util.group_by_to_a(extends[members.to_set]) {|ex| ex.extender}
111
111
  groups.map! do |seq, group|
112
- sels = group.map {|_, s| s}.flatten
112
+ sels = group.map {|e| e.target}.flatten
113
113
  # If A {@extend B} and C {...},
114
114
  # seq is A, sels is B, and self is C
115
115
 
116
116
  self_without_sel = Sass::Util.array_minus(members, sels)
117
- group.each {|e, _| e.result = :failed_to_unify unless e.result == :succeeded}
117
+ group.each {|e| e.result = :failed_to_unify unless e.result == :succeeded}
118
118
  unified = seq.members.last.unify(self_without_sel, subject?)
119
119
  next unless unified
120
- group.each {|e, _| e.result = :succeeded}
121
- group.each {|e, _| check_directives_match!(e, parent_directives)}
120
+ group.each {|e| e.result = :succeeded}
121
+ group.each {|e| check_directives_match!(e, parent_directives)}
122
122
  new_seq = Sequence.new(seq.members[0...-1] + [unified])
123
123
  new_seq.add_sources!(sources + [seq])
124
124
  [sels, new_seq]
@@ -104,7 +104,7 @@ module Sass::Source
104
104
  css_uri ||= css_path.relative_path_from(sourcemap_path.dirname).to_s
105
105
 
106
106
  result = "{\n"
107
- write_json_field(result, "version", "3", true)
107
+ write_json_field(result, "version", 3, true)
108
108
 
109
109
  source_uri_to_id = {}
110
110
  id_to_source_uri = {}
@@ -30,6 +30,31 @@ module Sass
30
30
  class Node
31
31
  include Enumerable
32
32
 
33
+ def self.inherited(base)
34
+ node_name = base.name.gsub(/.*::(.*?)Node$/, '\\1').downcase
35
+ base.instance_eval <<-METHODS
36
+ # @return [Symbol] The name that is used for this node when visiting.
37
+ def node_name
38
+ :#{node_name}
39
+ end
40
+
41
+ # @return [Symbol] The method that is used on the visitor to visit nodes of this type.
42
+ def visit_method
43
+ :visit_#{node_name}
44
+ end
45
+
46
+ # @return [Symbol] The method name that determines if the parent is invalid.
47
+ def invalid_child_method_name
48
+ :"invalid_#{node_name}_child?"
49
+ end
50
+
51
+ # @return [Symbol] The method name that determines if the node is an invalid parent.
52
+ def invalid_parent_method_name
53
+ :"invalid_#{node_name}_parent?"
54
+ end
55
+ METHODS
56
+ end
57
+
33
58
  # The child nodes of this node.
34
59
  #
35
60
  # @return [Array<Tree::Node>]
@@ -20,6 +20,11 @@ module Sass
20
20
  # @return [Boolean]
21
21
  attr_reader :global
22
22
 
23
+ # Whether we've warned about deprecated global variable
24
+ # assignment yet for this node.
25
+ # @return [Boolean]
26
+ attr_accessor :global_warning_given
27
+
23
28
  # @param name [String] The name of the variable
24
29
  # @param expr [Script::Tree::Node] See \{#expr}
25
30
  # @param guarded [Boolean] See \{#guarded}
@@ -32,9 +32,8 @@ module Sass::Tree::Visitors
32
32
  # @param node [Tree::Node] The node to visit.
33
33
  # @return [Object] The return value of the `visit_*` method for this node.
34
34
  def visit(node)
35
- method = "visit_#{self.class.node_name node}"
36
- if respond_to?(method, true)
37
- send(method, node) {visit_children(node)}
35
+ if respond_to?(node.class.visit_method, true)
36
+ send(node.class.visit_method, node) {visit_children(node)}
38
37
  else
39
38
  visit_children(node)
40
39
  end
@@ -53,15 +52,13 @@ module Sass::Tree::Visitors
53
52
  parent.children.map {|c| visit(c)}
54
53
  end
55
54
 
56
- NODE_NAME_RE = /.*::(.*?)Node$/
57
-
58
55
  # Returns the name of a node as used in the `visit_*` method.
59
56
  #
60
57
  # @param [Tree::Node] node The node.
61
58
  # @return [String] The name.
62
59
  def self.node_name(node)
63
- @node_names ||= {}
64
- @node_names[node.class.name] ||= node.class.name.gsub(NODE_NAME_RE, '\\1').downcase
60
+ Sass::Util.deprecated(self, "Call node.class.node_name instead.")
61
+ node.class.node_name
65
62
  end
66
63
 
67
64
  # `yield`s, then runs the visitor on the `@else` clause if the node has one.
@@ -9,8 +9,8 @@ class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
9
9
 
10
10
  def visit(node)
11
11
  if (error = @parent && (
12
- try_send("invalid_#{self.class.node_name @parent}_child?", @parent, node) ||
13
- try_send("invalid_#{self.class.node_name node}_parent?", @parent, node)))
12
+ try_send(@parent.class.invalid_child_method_name, @parent, node) ||
13
+ try_send(node.class.invalid_parent_method_name, @parent, node)))
14
14
  raise Sass::SyntaxError.new(error)
15
15
  end
16
16
  super
@@ -99,7 +99,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
99
99
  # @api private
100
100
  # @return [Sass::Script::Value::ArgList]
101
101
  def perform_splat(splat, performed_keywords, kwarg_splat, environment)
102
- args, kwargs, separator = [], Sass::Util.ordered_hash, :comma
102
+ args, kwargs, separator = [], nil, :comma
103
103
 
104
104
  if splat
105
105
  splat = splat.perform(environment)
@@ -113,8 +113,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
113
113
  args = splat.to_a
114
114
  end
115
115
  end
116
-
117
- kwargs = kwargs.merge(performed_keywords)
116
+ kwargs ||= Sass::Util.ordered_hash
117
+ kwargs.update(performed_keywords)
118
118
 
119
119
  if kwarg_splat
120
120
  kwarg_splat = kwarg_splat.perform(environment)
@@ -122,7 +122,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
122
122
  raise Sass::SyntaxError.new("Variable keyword arguments must be a map " +
123
123
  "(was #{kwarg_splat.inspect}).")
124
124
  end
125
- kwargs = kwargs.merge(arg_hash(kwarg_splat))
125
+ kwargs.update(arg_hash(kwarg_splat))
126
126
  end
127
127
 
128
128
  Sass::Script::Value::ArgList.new(args, kwargs, separator)
@@ -195,7 +195,11 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
195
195
  # Prints the expression to STDERR.
196
196
  def visit_debug(node)
197
197
  res = node.expr.perform(@environment)
198
- res = res.value if res.is_a?(Sass::Script::Value::String)
198
+ if res.is_a?(Sass::Script::Value::String)
199
+ res = res.value
200
+ else
201
+ res = res.to_sass
202
+ end
199
203
  if node.filename
200
204
  Sass::Util.sass_warn "#{node.filename}:#{node.line} DEBUG: #{res}"
201
205
  else
@@ -325,7 +329,7 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
325
329
  end
326
330
 
327
331
  args = node.args.map {|a| a.perform(@environment)}
328
- keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
332
+ keywords = Sass::Util.map_vals(node.keywords) {|v| v.perform(@environment)}
329
333
  splat = self.class.perform_splat(node.splat, keywords, node.kwarg_splat, @environment)
330
334
 
331
335
  self.class.perform_arguments(mixin, args, splat) do |env|
@@ -419,7 +423,8 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
419
423
  env = @environment
420
424
  if node.global
421
425
  env = env.global_env
422
- elsif env.parent && env.is_var_global?(node.name)
426
+ elsif env.parent && env.is_var_global?(node.name) && !node.global_warning_given
427
+ node.global_warning_given = true
423
428
  var_expr = "$#{node.name}: #{node.expr.to_sass(env.options)} !global"
424
429
  var_expr << " !default" if node.guarded
425
430
  location = "on line #{node.line}"
@@ -31,36 +31,6 @@ module Sass
31
31
  File.join(Sass::ROOT_DIR, file)
32
32
  end
33
33
 
34
- # Converts a hash or a list of pairs into an order-preserving hash.
35
- #
36
- # On Ruby 1.8.7, this uses the orderedhash gem to simulate an
37
- # order-preserving hash. On Ruby 1.9 and up, it just uses the native Hash
38
- # class, since that preserves the order itself.
39
- #
40
- # @overload ordered_hash(hash)
41
- # @param hash [Hash] a normal hash to convert to an ordered hash
42
- # @return [Hash]
43
- # @overload ordered_hash(*pairs)
44
- # @example
45
- # ordered_hash([:foo, "bar"], [:baz, "bang"])
46
- # #=> {:foo => "bar", :baz => "bang"}
47
- # ordered_hash #=> {}
48
- # @param pairs [Array<(Object, Object)>] the list of key/value pairs for
49
- # the hash.
50
- # @return [Hash]
51
- def ordered_hash(*pairs_or_hash)
52
- require 'sass/util/ordered_hash' if ruby1_8?
53
-
54
- if pairs_or_hash.length == 1 && pairs_or_hash.first.is_a?(Hash)
55
- hash = pairs_or_hash.first
56
- return hash unless ruby1_8?
57
- return OrderedHash.new.merge hash
58
- end
59
-
60
- return Hash[pairs_or_hash] unless ruby1_8?
61
- OrderedHash[*flatten(pairs_or_hash, 1)]
62
- end
63
-
64
34
  # Converts an array of `[key, value]` pairs to a hash.
65
35
  #
66
36
  # @example
@@ -85,7 +55,7 @@ module Sass
85
55
  # @see #map_vals
86
56
  # @see #map_hash
87
57
  def map_keys(hash)
88
- to_hash(hash.map {|k, v| [yield(k), v]})
58
+ map_hash(hash) {|k, v| [yield(k), v]}
89
59
  end
90
60
 
91
61
  # Maps the values in a hash according to a block.
@@ -101,7 +71,13 @@ module Sass
101
71
  # @see #map_keys
102
72
  # @see #map_hash
103
73
  def map_vals(hash)
104
- to_hash(hash.map {|k, v| [k, yield(v)]})
74
+ # We don't delegate to map_hash for performance here
75
+ # because map_hash does more than is necessary.
76
+ rv = hash.class.new
77
+ hash.each do |k, v|
78
+ rv[k] = yield(v)
79
+ end
80
+ rv
105
81
  end
106
82
 
107
83
  # Maps the key-value pairs of a hash according to a block.
@@ -118,8 +94,15 @@ module Sass
118
94
  # @see #map_keys
119
95
  # @see #map_vals
120
96
  def map_hash(hash)
121
- # Using &block here completely hoses performance on 1.8.
122
- to_hash(hash.map {|k, v| yield k, v})
97
+ # Copy and modify is more performant than mapping to an array and using
98
+ # to_hash on the result.
99
+ rv = hash.class.new
100
+ hash.each do |k, v|
101
+ new_key, new_value = yield(k, v)
102
+ rv.delete(k)
103
+ rv[new_key] = new_value
104
+ end
105
+ rv
123
106
  end
124
107
 
125
108
  # Computes the powerset of the given array.
@@ -272,12 +255,12 @@ module Sass
272
255
  #
273
256
  # @param enum [Enumerable]
274
257
  # @return [Array<[Object, Array]>] An array of pairs.
275
- def group_by_to_a(enum, &block)
276
- return enum.group_by(&block).to_a unless ruby1_8?
258
+ def group_by_to_a(enum)
259
+ return enum.group_by {|e| yield(e)}.to_a unless ruby1_8?
277
260
  order = {}
278
261
  arr = []
279
262
  groups = enum.group_by do |e|
280
- res = block[e]
263
+ res = yield(e)
281
264
  unless order.include?(res)
282
265
  order[res] = order.size
283
266
  end
@@ -415,6 +398,17 @@ module Sass
415
398
  raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
416
399
  end
417
400
 
401
+ # Prints a deprecation warning for the caller method.
402
+ #
403
+ # @param obj [Object] `self`
404
+ # @param message [String] A message describing what to do instead.
405
+ def deprecated(obj, message = nil)
406
+ obj_class = obj.is_a?(Class) ? "#{obj}." : "#{obj.class}#"
407
+ full_message = "DEPRECATION WARNING: #{obj_class}#{caller_info[2]} " +
408
+ "will be removed in a future version of Sass.#{("\n" + message) if message}"
409
+ Sass::Util.sass_warn full_message
410
+ end
411
+
418
412
  # Silence all output to STDERR within a block.
419
413
  #
420
414
  # @yield A block in which no output will be printed to STDERR
@@ -507,33 +501,40 @@ module Sass
507
501
  end
508
502
 
509
503
  ## Cross-OS Compatibility
504
+ #
505
+ # These methods are cached because some of them are called quite frequently
506
+ # and even basic checks like String#== are too costly to be called repeatedly.
510
507
 
511
508
  # Whether or not this is running on Windows.
512
509
  #
513
510
  # @return [Boolean]
514
511
  def windows?
515
- RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
512
+ return @windows if defined?(@windows)
513
+ @windows = (RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i)
516
514
  end
517
515
 
518
516
  # Whether or not this is running on IronRuby.
519
517
  #
520
518
  # @return [Boolean]
521
519
  def ironruby?
522
- RUBY_ENGINE == "ironruby"
520
+ return @ironruby if defined?(@ironruby)
521
+ @ironruby = RUBY_ENGINE == "ironruby"
523
522
  end
524
523
 
525
524
  # Whether or not this is running on Rubinius.
526
525
  #
527
526
  # @return [Boolean]
528
527
  def rbx?
529
- RUBY_ENGINE == "rbx"
528
+ return @rbx if defined?(@rbx)
529
+ @rbx = RUBY_ENGINE == "rbx"
530
530
  end
531
531
 
532
532
  # Whether or not this is running on JRuby.
533
533
  #
534
534
  # @return [Boolean]
535
535
  def jruby?
536
- RUBY_PLATFORM =~ /java/
536
+ return @jruby if defined?(@jruby)
537
+ @jruby = RUBY_PLATFORM =~ /java/
537
538
  end
538
539
 
539
540
  # @see #jruby_version-class_method
@@ -551,9 +552,13 @@ module Sass
551
552
  # Like `Dir.glob`, but works with backslash-separated paths on Windows.
552
553
  #
553
554
  # @param path [String]
554
- def glob(path, &block)
555
+ def glob(path)
555
556
  path = path.gsub('\\', '/') if windows?
556
- Dir.glob(path, &block)
557
+ if block_given?
558
+ Dir.glob(path) {|f| yield(f)}
559
+ else
560
+ Dir.glob(path)
561
+ end
557
562
  end
558
563
 
559
564
  # Prepare a value for a destructuring assignment (e.g. `a, b =
@@ -575,7 +580,8 @@ module Sass
575
580
  #
576
581
  # @return [Boolean]
577
582
  def ruby1?
578
- Sass::Util::RUBY_VERSION[0] <= 1
583
+ return @ruby1 if defined?(@ruby1)
584
+ @ruby1 = Sass::Util::RUBY_VERSION[0] <= 1
579
585
  end
580
586
 
581
587
  # Whether or not this is running under Ruby 1.8 or lower.
@@ -587,7 +593,9 @@ module Sass
587
593
  def ruby1_8?
588
594
  # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
589
595
  # We have to fall back to 1.8 behavior.
590
- ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
596
+ return @ruby1_8 if defined?(@ruby1_8)
597
+ @ruby1_8 = ironruby? ||
598
+ (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
591
599
  end
592
600
 
593
601
  # Whether or not this is running under Ruby 1.8.6 or lower.
@@ -595,21 +603,55 @@ module Sass
595
603
  #
596
604
  # @return [Boolean]
597
605
  def ruby1_8_6?
598
- ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
606
+ return @ruby1_8_6 if defined?(@ruby1_8_6)
607
+ @ruby1_8_6 = ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
599
608
  end
600
609
 
601
610
  # Wehter or not this is running under JRuby 1.6 or lower.
602
611
  def jruby1_6?
603
- jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
612
+ return @jruby1_6 if defined?(@jruby1_6)
613
+ @jruby1_6 = jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
604
614
  end
605
615
 
606
616
  # Whether or not this is running under MacRuby.
607
617
  #
608
618
  # @return [Boolean]
609
619
  def macruby?
610
- RUBY_ENGINE == 'macruby'
620
+ return @macruby if defined?(@macruby)
621
+ @macruby = RUBY_ENGINE == 'macruby'
611
622
  end
612
623
 
624
+ require 'sass/util/ordered_hash' if ruby1_8?
625
+
626
+ # Converts a hash or a list of pairs into an order-preserving hash.
627
+ #
628
+ # On Ruby 1.8.7, this uses the orderedhash gem to simulate an
629
+ # order-preserving hash. On Ruby 1.9 and up, it just uses the native Hash
630
+ # class, since that preserves the order itself.
631
+ #
632
+ # @overload ordered_hash(hash)
633
+ # @param hash [Hash] a normal hash to convert to an ordered hash
634
+ # @return [Hash]
635
+ # @overload ordered_hash(*pairs)
636
+ # @example
637
+ # ordered_hash([:foo, "bar"], [:baz, "bang"])
638
+ # #=> {:foo => "bar", :baz => "bang"}
639
+ # ordered_hash #=> {}
640
+ # @param pairs [Array<(Object, Object)>] the list of key/value pairs for
641
+ # the hash.
642
+ # @return [Hash]
643
+ def ordered_hash(*pairs_or_hash)
644
+ if pairs_or_hash.length == 1 && pairs_or_hash.first.is_a?(Hash)
645
+ hash = pairs_or_hash.first
646
+ return hash unless ruby1_8?
647
+ return OrderedHash.new.merge hash
648
+ end
649
+
650
+ return Hash[pairs_or_hash] unless ruby1_8?
651
+ (pairs_or_hash.is_a?(NormalizedMap) ? NormalizedMap : OrderedHash)[*flatten(pairs_or_hash, 1)]
652
+ end
653
+
654
+
613
655
  # Checks that the encoding of a string is valid in Ruby 1.9
614
656
  # and cleans up potential encoding gotchas like the UTF-8 BOM.
615
657
  # If it's not, yields an error string describing the invalid character
@@ -1011,8 +1053,8 @@ MSG
1011
1053
  #
1012
1054
  # @param name [Symbol] The name of the variable
1013
1055
  # @return [Boolean]
1014
- def method_missing(name, *args, &block)
1015
- super unless args.empty? && block.nil?
1056
+ def method_missing(name, *args)
1057
+ super unless args.empty? && !block_given?
1016
1058
  @set.include?(name)
1017
1059
  end
1018
1060
  end
@@ -1053,6 +1095,7 @@ MSG
1053
1095
  # Calculates the memoization table for the Least Common Subsequence algorithm.
1054
1096
  # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
1055
1097
  def lcs_table(x, y)
1098
+ # This method does not take a block as an explicit parameter for performance reasons.
1056
1099
  # rubocop:enable LineLength
1057
1100
  c = Array.new(x.size) {[]}
1058
1101
  x.size.times {|i| c[i][0] = 0}
@@ -1085,6 +1128,8 @@ MSG
1085
1128
  return lcs_backtrace(c, x, y, i, j - 1, &block) if c[i][j - 1] > c[i - 1][j]
1086
1129
  lcs_backtrace(c, x, y, i - 1, j, &block)
1087
1130
  end
1131
+
1132
+ (Sass::Util.methods - Module.methods).each {|method| module_function method}
1088
1133
  end
1089
1134
  end
1090
1135