sass 3.3.0.alpha.93 → 3.3.0.alpha.101

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 (61) hide show
  1. data/REVISION +1 -1
  2. data/VERSION +1 -1
  3. data/VERSION_DATE +1 -1
  4. data/bin/sass +2 -1
  5. data/bin/sass-convert +2 -1
  6. data/bin/scss +2 -1
  7. data/lib/sass/cache_stores/chain.rb +1 -1
  8. data/lib/sass/cache_stores/filesystem.rb +0 -1
  9. data/lib/sass/engine.rb +7 -1
  10. data/lib/sass/exec.rb +1 -0
  11. data/lib/sass/importers/filesystem.rb +1 -1
  12. data/lib/sass/media.rb +1 -4
  13. data/lib/sass/plugin/compiler.rb +1 -1
  14. data/lib/sass/script/funcall.rb +43 -8
  15. data/lib/sass/script/functions.rb +8 -16
  16. data/lib/sass/script/lexer.rb +0 -2
  17. data/lib/sass/script/parser.rb +26 -25
  18. data/lib/sass/scss/parser.rb +13 -1
  19. data/lib/sass/selector/simple_sequence.rb +1 -1
  20. data/lib/sass/tree/comment_node.rb +2 -2
  21. data/lib/sass/tree/visitors/cssize.rb +10 -1
  22. data/lib/sass/tree/visitors/perform.rb +4 -2
  23. data/lib/sass/util.rb +54 -1
  24. data/lib/sass/util/multibyte_string_scanner.rb +29 -8
  25. data/test/sass/engine_test.rb +20 -4
  26. data/test/sass/extend_test.rb +15 -0
  27. data/test/sass/functions_test.rb +20 -1
  28. data/test/sass/script_test.rb +5 -1
  29. data/vendor/listen/CHANGELOG.md +76 -2
  30. data/vendor/listen/CONTRIBUTING.md +38 -0
  31. data/vendor/listen/Gemfile +8 -1
  32. data/vendor/listen/Guardfile +1 -1
  33. data/vendor/listen/LICENSE +1 -1
  34. data/vendor/listen/README.md +8 -5
  35. data/vendor/listen/lib/listen.rb +7 -5
  36. data/vendor/listen/lib/listen/adapter.rb +76 -29
  37. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  38. data/vendor/listen/lib/listen/adapters/darwin.rb +11 -10
  39. data/vendor/listen/lib/listen/adapters/linux.rb +33 -30
  40. data/vendor/listen/lib/listen/adapters/polling.rb +2 -1
  41. data/vendor/listen/lib/listen/adapters/windows.rb +27 -21
  42. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  43. data/vendor/listen/lib/listen/directory_record.rb +63 -10
  44. data/vendor/listen/lib/listen/listener.rb +22 -0
  45. data/vendor/listen/lib/listen/multi_listener.rb +22 -0
  46. data/vendor/listen/lib/listen/version.rb +1 -1
  47. data/vendor/listen/listen.gemspec +0 -4
  48. data/vendor/listen/spec/listen/adapter_spec.rb +45 -4
  49. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  50. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +6 -0
  51. data/vendor/listen/spec/listen/adapters/linux_spec.rb +6 -0
  52. data/vendor/listen/spec/listen/adapters/windows_spec.rb +7 -1
  53. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  54. data/vendor/listen/spec/listen/directory_record_spec.rb +91 -4
  55. data/vendor/listen/spec/listen/listener_spec.rb +14 -0
  56. data/vendor/listen/spec/listen/multi_listener_spec.rb +19 -1
  57. data/vendor/listen/spec/spec_helper.rb +6 -3
  58. data/vendor/listen/spec/support/adapter_helper.rb +125 -212
  59. data/vendor/listen/spec/support/listeners_helper.rb +13 -1
  60. data/vendor/listen/spec/support/platform_helper.rb +4 -0
  61. metadata +11 -6
data/REVISION CHANGED
@@ -1 +1 @@
1
- 655e8ae0d3bcab12d522eac27a7f7cb876475756
1
+ d412ca4d9bd9fca447d84e1fd960268a8419b9f3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0.alpha.93
1
+ 3.3.0.alpha.101
@@ -1 +1 @@
1
- 23 February 2013 01:38:58 GMT
1
+ 27 February 2013 19:13:18 GMT
data/bin/sass CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # The command line Sass parser.
3
3
 
4
- require File.dirname(__FILE__) + '/../lib/sass'
4
+ THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
5
+ require File.dirname(THIS_FILE) + '/../lib/sass'
5
6
  require 'sass/exec'
6
7
 
7
8
  opts = Sass::Exec::Sass.new(ARGV)
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.dirname(__FILE__) + '/../lib/sass'
3
+ THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
4
+ require File.dirname(THIS_FILE) + '/../lib/sass'
4
5
  require 'sass/exec'
5
6
 
6
7
  opts = Sass::Exec::SassConvert.new(ARGV)
data/bin/scss CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # The command line Sass parser.
3
3
 
4
- require File.dirname(__FILE__) + '/../lib/sass'
4
+ THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
5
+ require File.dirname(THIS_FILE) + '/../lib/sass'
5
6
  require 'sass/exec'
6
7
 
7
8
  opts = Sass::Exec::Scss.new(ARGV)
@@ -23,7 +23,7 @@ module Sass
23
23
  def retrieve(key, sha)
24
24
  @caches.each_with_index do |c, i|
25
25
  next unless obj = c.retrieve(key, sha)
26
- @caches[0...i].each {|c| c.store(key, sha, obj)}
26
+ @caches[0...i].each {|prev| prev.store(key, sha, obj)}
27
27
  return obj
28
28
  end
29
29
  nil
@@ -17,7 +17,6 @@ module Sass
17
17
  # @see Base#\_retrieve
18
18
  def _retrieve(key, version, sha)
19
19
  return unless File.readable?(path_to(key))
20
- contents = nil
21
20
  File.open(path_to(key), "rb") do |f|
22
21
  if f.readline("\n").strip == version && f.readline("\n").strip == sha
23
22
  return f.read
@@ -314,7 +314,7 @@ module Sass
314
314
  # @return [[Sass::Engine]] The dependency documents.
315
315
  def dependencies
316
316
  _dependencies(Set.new, engines = Set.new)
317
- engines - [self]
317
+ Sass::Util.array_minus(engines, [self])
318
318
  end
319
319
 
320
320
  # Helper for \{#dependencies}.
@@ -817,6 +817,12 @@ WARNING
817
817
  @line, to_parser_offset(@offset))
818
818
  Tree::MediaNode.new(parser.parse_media_query_list.to_a)
819
819
  else
820
+ unprefixed_directive = directive.gsub(/^-[a-z0-9]+-/i, '')
821
+ if unprefixed_directive == 'supports'
822
+ parser = Sass::SCSS::Parser.new(value, @options[:filename], @line)
823
+ return Tree::SupportsNode.new(directive, parser.parse_supports_condition)
824
+ end
825
+
820
826
  Tree::DirectiveNode.new(
821
827
  value.nil? ? ["@#{directive}"] : ["@#{directive} "] + parse_interp(value, offset))
822
828
  end
@@ -317,6 +317,7 @@ END
317
317
  return watch_or_update if @options[:watch] || @options[:update]
318
318
  super
319
319
  @options[:for_engine][:filename] = @options[:filename]
320
+ @options[:for_engine][:css_filename] = @options[:output].path if @options[:output].is_a?(File)
320
321
 
321
322
  begin
322
323
  input = @options[:input]
@@ -30,7 +30,7 @@ module Sass
30
30
 
31
31
  # @see Base#mtime
32
32
  def mtime(name, options)
33
- file, s = Sass::Util.destructure(find_real_file(@root, name, options))
33
+ file, _ = Sass::Util.destructure(find_real_file(@root, name, options))
34
34
  File.mtime(file) if file
35
35
  rescue Errno::ENOENT
36
36
  nil
@@ -140,10 +140,7 @@ module Sass::Media
140
140
  type = t1
141
141
  mod = m1.empty? ? m2 : m1
142
142
  end
143
- q = Query.new([], [], other.expressions + expressions)
144
- q.type = [type]
145
- q.modifier = [mod]
146
- return q
143
+ return Query.new([mod], [type], other.expressions + expressions)
147
144
  end
148
145
 
149
146
  # Returns the CSS for the media query.
@@ -171,7 +171,7 @@ module Sass::Plugin
171
171
  # Get the relative path to the file
172
172
  name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
173
173
  css = css_filename(name, css_location)
174
- sourcemap = Util::sourcemap_name(css) if engine_options[:sourcemap]
174
+ sourcemap = Sass::Util.sourcemap_name(css) if engine_options[:sourcemap]
175
175
  individual_files << [file, css, sourcemap]
176
176
  end
177
177
  end
@@ -107,18 +107,53 @@ module Sass
107
107
  opts(Functions::EvaluationContext.new(local_environment).send(ruby_name, *args))
108
108
  end
109
109
  rescue ArgumentError => e
110
+ message = e.message
111
+
110
112
  # If this is a legitimate Ruby-raised argument error, re-raise it.
111
113
  # Otherwise, it's an error in the user's stylesheet, so wrap it.
112
- if e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
113
- e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/ &&
114
- # JRuby (as of 1.6.7.2) doesn't put the actual method for
115
- # which the argument error was thrown in the backtrace, so
116
- # we detect whether our send threw an argument error.
117
- (RUBY_PLATFORM !~ /java/ || e.backtrace[0] !~ /:in `send'$/ ||
118
- e.backtrace[1] !~ /:in `_perform'$/)
114
+ if Sass::Util.rbx?
115
+ # Rubinius has a different error report string than vanilla Ruby. It
116
+ # also doesn't put the actual method for which the argument error was
117
+ # thrown in the backtrace, nor does it include `send`, so we look for
118
+ # `_perform`.
119
+ if e.message =~ /^method '([^']+)': given (\d+), expected (\d+)/
120
+ error_name, given, expected = $1, $2, $3
121
+ raise e if error_name != ruby_name || e.backtrace[0] !~ /:in `_perform'$/
122
+ message = "wrong number of arguments (#{given} for #{expected})"
123
+ end
124
+ elsif Sass::Util.jruby?
125
+ if Sass::Util.jruby1_6?
126
+ should_maybe_raise = e.message =~ /^wrong number of arguments \((\d+) for (\d+)\)/ &&
127
+ # The one case where JRuby does include the Ruby name of the function
128
+ # is manually-thrown ArgumentErrors, which are indistinguishable from
129
+ # legitimate ArgumentErrors. We treat both of these as
130
+ # Sass::SyntaxErrors even though it can hide Ruby errors.
131
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
132
+ else
133
+ should_maybe_raise = e.message =~ /^wrong number of arguments calling `[^`]+` \((\d+) for (\d+)\)/
134
+ given, expected = $1, $2
135
+ end
136
+
137
+ if should_maybe_raise
138
+ # JRuby 1.7 includes __send__ before send and _perform.
139
+ trace = e.backtrace.dup
140
+ raise e if !Sass::Util.jruby1_6? && trace.shift !~ /:in `__send__'$/
141
+
142
+ # JRuby (as of 1.7.2) doesn't put the actual method
143
+ # for which the argument error was thrown in the backtrace, so we
144
+ # detect whether our send threw an argument error.
145
+ if !(trace[0] =~ /:in `send'$/ && trace[1] =~ /:in `_perform'$/)
146
+ raise e
147
+ elsif !Sass::Util.jruby1_6?
148
+ # JRuby 1.7 doesn't use standard formatting for its ArgumentErrors.
149
+ message = "wrong number of arguments (#{given} for #{expected})"
150
+ end
151
+ end
152
+ elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
153
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
119
154
  raise e
120
155
  end
121
- raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
156
+ raise Sass::SyntaxError.new("#{message} for `#{name}'")
122
157
  end
123
158
 
124
159
  # This method is factored out from `_perform` so that compass can override
@@ -379,18 +379,11 @@ module Sass::Script
379
379
  def assert_unit(number, unit, name = nil)
380
380
  assert_type number, :Number, name
381
381
  return if number.is_unit?(unit)
382
- if unit
383
- if name
384
- raise ArgumentError.new("Expected $#{name} to have a unit of #{unit} but got #{number}")
385
- else
386
- raise ArgumentError.new("Expected #{number} to have a unit of #{unit}")
387
- end
382
+ expectation = unit ? "have a unit of #{unit}" : "be unitless"
383
+ if name
384
+ raise ArgumentError.new("Expected $#{name} to #{expectation} but got #{number}")
388
385
  else
389
- if name
390
- raise ArgumentError.new("Expected $#{name} to be unitless but got #{number}")
391
- else
392
- raise ArgumentError.new("Expected #{number} to be unitless")
393
- end
386
+ raise ArgumentError.new("Expected #{number} to #{expectation}")
394
387
  end
395
388
  end
396
389
 
@@ -1575,9 +1568,9 @@ module Sass::Script
1575
1568
  length = nil
1576
1569
  values = []
1577
1570
  lists.each do |list|
1578
- assert_type list, :List
1579
- values << list.value.dup
1580
- length = length.nil? ? list.value.length : [length, list.value.length].min
1571
+ array = list.to_a
1572
+ values << array.dup
1573
+ length = length.nil? ? array.length : [length, array.length].min
1581
1574
  end
1582
1575
  values.each do |value|
1583
1576
  value.slice!(length)
@@ -1595,8 +1588,7 @@ module Sass::Script
1595
1588
  # index(1px solid red, solid) => 2
1596
1589
  # index(1px solid red, dashed) => false
1597
1590
  def index(list, value)
1598
- assert_type list, :List
1599
- index = list.value.index {|e| e.eq(value).to_bool }
1591
+ index = list.to_a.index {|e| e.eq(value).to_bool }
1600
1592
  if index
1601
1593
  Number.new(index + 1)
1602
1594
  else
@@ -248,8 +248,6 @@ module Sass
248
248
  end
249
249
 
250
250
  def _variable(rx)
251
- line = @line
252
- offset = @offset
253
251
  return unless scan(rx)
254
252
 
255
253
  [:const, @scanner[2]]
@@ -372,8 +372,6 @@ RUBY
372
372
  splat = nil
373
373
  must_have_default = false
374
374
  loop do
375
- line = @lexer.line
376
- offset = @lexer.offset
377
375
  c = assert_tok(:const)
378
376
  var = Script::Variable.new(c.value)
379
377
  var.source_range = range(c.offset)
@@ -394,38 +392,41 @@ RUBY
394
392
  end
395
393
 
396
394
  def fn_arglist
397
- arglist(:fn_arglist, :equals)
395
+ arglist(:equals, "function argument")
398
396
  end
399
397
 
400
398
  def mixin_arglist
401
- arglist(:mixin_arglist, :interpolation)
399
+ arglist(:interpolation, "mixin argument")
402
400
  end
403
401
 
404
- def arglist(type, subexpr)
402
+ def arglist(subexpr, description)
405
403
  return unless e = send(subexpr)
406
- if @lexer.peek && @lexer.peek.type == :colon
407
- name = e
408
- @lexer.expected!("comma") unless name.is_a?(Variable)
409
- assert_tok(:colon)
410
- keywords = {name.underscored_name => assert_expr(subexpr, EXPR_NAMES[type])}
411
- end
412
404
 
413
- unless try_tok(:comma)
414
- return [], keywords if keywords
415
- return [], {}, e if try_tok(:splat)
416
- return [e], {}
417
- end
405
+ args = []
406
+ keywords = {}
407
+ loop do
408
+ if @lexer.peek && @lexer.peek.type == :colon
409
+ name = e
410
+ @lexer.expected!("comma") unless name.is_a?(Variable)
411
+ assert_tok(:colon)
412
+ value = assert_expr(subexpr, description)
413
+
414
+ if keywords[name.underscored_name]
415
+ raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
416
+ end
418
417
 
419
- other_args, other_keywords, splat = assert_expr(type)
420
- if keywords
421
- if !other_args.empty? || splat
422
- raise SyntaxError.new("Positional arguments must come before keyword arguments.")
423
- elsif other_keywords[name.underscored_name]
424
- raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
418
+ keywords[name.underscored_name] = value
419
+ else
420
+ if !keywords.empty?
421
+ raise SyntaxError.new("Positional arguments must come before keyword arguments.")
422
+ end
423
+
424
+ return args, keywords, e if try_tok(:splat)
425
+ args << e
425
426
  end
426
- return other_args, keywords.merge(other_keywords), splat
427
- else
428
- return [e, *other_args], other_keywords, splat
427
+
428
+ return args, keywords unless try_tok(:comma)
429
+ e = assert_expr(subexpr, description)
429
430
  end
430
431
  end
431
432
 
@@ -64,6 +64,18 @@ module Sass
64
64
  ql
65
65
  end
66
66
 
67
+ # Parses a supports query condition.
68
+ #
69
+ # @return [Sass::Supports::Condition] The parsed condition
70
+ # @raise [Sass::SyntaxError] if there's a syntax error in the condition,
71
+ # or if it doesn't take up the entire input string.
72
+ def parse_supports_condition
73
+ init_scanner!
74
+ condition = supports_condition
75
+ expected("supports condition") unless @scanner.eos?
76
+ condition
77
+ end
78
+
67
79
  private
68
80
 
69
81
  include Sass::SCSS::RX
@@ -1021,7 +1033,7 @@ MESSAGE
1021
1033
  offset = @offset
1022
1034
  @strs.push ""
1023
1035
  throw_error {yield} && @strs.last
1024
- rescue Sass::SyntaxError => e
1036
+ rescue Sass::SyntaxError
1025
1037
  @scanner.pos = pos
1026
1038
  @line = line
1027
1039
  @offset = offset
@@ -107,7 +107,7 @@ module Sass
107
107
  # If A {@extend B} and C {...},
108
108
  # seq is A, sels is B, and self is C
109
109
 
110
- self_without_sel = self.members - sels
110
+ self_without_sel = Sass::Util.array_minus(self.members, sels)
111
111
  group.each {|e, _| e.result = :failed_to_unify unless e.result == :succeeded}
112
112
  next unless unified = seq.members.last.unify(self_without_sel, subject?)
113
113
  group.each {|e, _| e.result = :succeeded}
@@ -70,13 +70,13 @@ module Sass::Tree
70
70
  private
71
71
 
72
72
  def normalize_indentation(str)
73
- pre = str.split("\n").inject(str[/^[ \t]*/].split("")) do |pre, line|
73
+ ind = str.split("\n").inject(str[/^[ \t]*/].split("")) do |pre, line|
74
74
  line[/^[ \t]*/].split("").zip(pre).inject([]) do |arr, (a, b)|
75
75
  break arr if a != b
76
76
  arr << a
77
77
  end
78
78
  end.join
79
- str.gsub(/^#{pre}/, '')
79
+ str.gsub(/^#{ind}/, '')
80
80
  end
81
81
  end
82
82
  end
@@ -32,6 +32,8 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
32
32
  end
33
33
  end
34
34
 
35
+ MERGEABLE_DIRECTIVES = [Sass::Tree::MediaNode]
36
+
35
37
  # Runs a block of code with the current parent node
36
38
  # replaced with the given node.
37
39
  #
@@ -39,11 +41,18 @@ class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
39
41
  # @yield A block in which the parent is set to `parent`.
40
42
  # @return [Object] The return value of the block.
41
43
  def with_parent(parent)
42
- @parent_directives.push parent if parent.is_a?(Sass::Tree::DirectiveNode)
44
+ if parent.is_a?(Sass::Tree::DirectiveNode)
45
+ if MERGEABLE_DIRECTIVES.any? {|klass| parent.is_a?(klass)}
46
+ old_parent_directive = @parent_directives.pop
47
+ end
48
+ @parent_directives.push parent
49
+ end
50
+
43
51
  old_parent, @parent = @parent, parent
44
52
  yield
45
53
  ensure
46
54
  @parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode)
55
+ @parent_directives.push old_parent_directive if old_parent_directive
47
56
  @parent = old_parent
48
57
  end
49
58
 
@@ -14,11 +14,13 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
14
14
 
15
15
  begin
16
16
  unless keywords.empty?
17
- unknown_args = keywords.keys - callable.args.map {|var| var.first.underscored_name}
17
+ unknown_args = Sass::Util.array_minus(keywords.keys,
18
+ callable.args.map {|var| var.first.underscored_name})
18
19
  if callable.splat && unknown_args.include?(callable.splat.underscored_name)
19
20
  raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc} cannot be used as a named argument.")
20
21
  elsif unknown_args.any?
21
- raise Sass::SyntaxError.new("#{desc} doesn't have #{unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'} #{unknown_args.map{|name| "$#{name}"}.join ', '}.")
22
+ description = unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'
23
+ raise Sass::SyntaxError.new("#{desc} doesn't have #{description} #{unknown_args.map {|name| "$#{name}"}.join ', '}.")
22
24
  end
23
25
  end
24
26
  rescue Sass::SyntaxError => keyword_exception
@@ -256,6 +256,33 @@ module Sass
256
256
  arr
257
257
  end
258
258
 
259
+ # Returns a sub-array of `minuend` containing only elements that are also in
260
+ # `subtrahend`. Ensures that the return value has the same order as
261
+ # `minuend`, even on Rubinius where that's not guaranteed by {Array#-}.
262
+ #
263
+ # @param minuend [Array]
264
+ # @param subtrahend [Array]
265
+ # @return [Array]
266
+ def array_minus(minuend, subtrahend)
267
+ return minuend - subtrahend unless rbx?
268
+ set = Set.new(minuend) - subtrahend
269
+ minuend.select {|e| set.include?(e)}
270
+ end
271
+
272
+ # Returns a string description of the character that caused an
273
+ # `Encoding::UndefinedConversionError`.
274
+ #
275
+ # @param [Encoding::UndefinedConversionError]
276
+ # @return [String]
277
+ def undefined_conversion_error_char(e)
278
+ # Rubinius (as of 2.0.0.rc1) pre-quotes the error character.
279
+ return e.error_char if rbx?
280
+ # JRuby (as of 1.7.2) doesn't have an error_char field on
281
+ # Encoding::UndefinedConversionError.
282
+ return e.error_char.dump unless jruby?
283
+ e.message[/^"[^"]+"/] #"
284
+ end
285
+
259
286
  # Asserts that `value` falls within `range` (inclusive), leaving
260
287
  # room for slight floating-point errors.
261
288
  #
@@ -462,6 +489,27 @@ module Sass
462
489
  RUBY_ENGINE == "ironruby"
463
490
  end
464
491
 
492
+ # Whether or not this is running on Rubinius.
493
+ #
494
+ # @return [Boolean]
495
+ def rbx?
496
+ RUBY_ENGINE == "rbx"
497
+ end
498
+
499
+ # Whether or not this is running on JRuby.
500
+ #
501
+ # @return [Boolean]
502
+ def jruby?
503
+ RUBY_PLATFORM =~ /java/
504
+ end
505
+
506
+ # Returns an array of ints representing the JRuby version number.
507
+ #
508
+ # @return [Array<Fixnum>]
509
+ def jruby_version
510
+ $jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
511
+ end
512
+
465
513
  # Like `Dir.glob`, but works with backslash-separated paths on Windows.
466
514
  #
467
515
  # @param path [String]
@@ -512,6 +560,11 @@ module Sass
512
560
  ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
513
561
  end
514
562
 
563
+ # Wehter or not this is running under JRuby 1.6 or lower.
564
+ def jruby1_6?
565
+ jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
566
+ end
567
+
515
568
  # Whether or not this is running under MacRuby.
516
569
  #
517
570
  # @return [Boolean]
@@ -548,7 +601,7 @@ module Sass
548
601
  line.encode(encoding)
549
602
  rescue Encoding::UndefinedConversionError => e
550
603
  yield <<MSG.rstrip, i + 1
551
- Invalid #{encoding.name} character #{e.error_char.dump}
604
+ Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}
552
605
  MSG
553
606
  end
554
607
  end