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

Sign up to get free protection for your applications and to get access to all the features.
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