sassc 2.1.0.pre1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +11 -0
  5. data/CHANGELOG.md +66 -0
  6. data/CODE_OF_CONDUCT.md +10 -0
  7. data/Gemfile +2 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +68 -0
  10. data/Rakefile +30 -0
  11. data/lib/sassc/2.3/libsass.so +0 -0
  12. data/lib/sassc/2.4/libsass.so +0 -0
  13. data/lib/sassc/2.5/libsass.so +0 -0
  14. data/lib/sassc/2.6/libsass.so +0 -0
  15. data/lib/sassc/dependency.rb +17 -0
  16. data/lib/sassc/engine.rb +139 -0
  17. data/lib/sassc/error.rb +37 -0
  18. data/lib/sassc/functions_handler.rb +75 -0
  19. data/lib/sassc/import_handler.rb +50 -0
  20. data/lib/sassc/importer.rb +31 -0
  21. data/lib/sassc/native/lib_c.rb +21 -0
  22. data/lib/sassc/native/native_context_api.rb +147 -0
  23. data/lib/sassc/native/native_functions_api.rb +164 -0
  24. data/lib/sassc/native/sass2scss_api.rb +10 -0
  25. data/lib/sassc/native/sass_input_style.rb +13 -0
  26. data/lib/sassc/native/sass_output_style.rb +12 -0
  27. data/lib/sassc/native/sass_value.rb +97 -0
  28. data/lib/sassc/native/string_list.rb +10 -0
  29. data/lib/sassc/native.rb +70 -0
  30. data/lib/sassc/sass_2_scss.rb +9 -0
  31. data/lib/sassc/script/functions.rb +8 -0
  32. data/lib/sassc/script/value/bool.rb +32 -0
  33. data/lib/sassc/script/value/color.rb +95 -0
  34. data/lib/sassc/script/value/list.rb +136 -0
  35. data/lib/sassc/script/value/map.rb +69 -0
  36. data/lib/sassc/script/value/number.rb +389 -0
  37. data/lib/sassc/script/value/string.rb +96 -0
  38. data/lib/sassc/script/value.rb +137 -0
  39. data/lib/sassc/script/value_conversion/base.rb +13 -0
  40. data/lib/sassc/script/value_conversion/bool.rb +13 -0
  41. data/lib/sassc/script/value_conversion/color.rb +18 -0
  42. data/lib/sassc/script/value_conversion/list.rb +25 -0
  43. data/lib/sassc/script/value_conversion/map.rb +21 -0
  44. data/lib/sassc/script/value_conversion/number.rb +13 -0
  45. data/lib/sassc/script/value_conversion/string.rb +17 -0
  46. data/lib/sassc/script/value_conversion.rb +69 -0
  47. data/lib/sassc/script.rb +19 -0
  48. data/lib/sassc/util/normalized_map.rb +117 -0
  49. data/lib/sassc/util.rb +231 -0
  50. data/lib/sassc/version.rb +5 -0
  51. data/lib/sassc.rb +57 -0
  52. data/sassc.gemspec +57 -0
  53. data/test/custom_importer_test.rb +127 -0
  54. data/test/engine_test.rb +314 -0
  55. data/test/error_test.rb +29 -0
  56. data/test/fixtures/paths.scss +10 -0
  57. data/test/functions_test.rb +303 -0
  58. data/test/native_test.rb +213 -0
  59. data/test/output_style_test.rb +107 -0
  60. data/test/sass_2_scss_test.rb +14 -0
  61. data/test/test_helper.rb +45 -0
  62. metadata +242 -0
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The abstract superclass for SassScript objects.
4
+ # Many of these methods, especially the ones that correspond to SassScript operations,
5
+ # are designed to be overridden by subclasses which may change the semantics somewhat.
6
+ # The operations listed here are just the defaults.
7
+
8
+ class SassC::Script::Value
9
+
10
+ # Returns the pure Ruby value of the value.
11
+ # The type of this value varies based on the subclass.
12
+ attr_reader :value
13
+
14
+ # The source range in the document on which this node appeared.
15
+ attr_accessor :source_range
16
+
17
+ # Creates a new value.
18
+ def initialize(value = nil)
19
+ value.freeze unless value.nil? || value == true || value == false
20
+ @value = value
21
+ @options = nil
22
+ end
23
+
24
+ # Sets the options hash for this node,
25
+ # as well as for all child nodes.
26
+ # See the official Sass reference for options.
27
+ attr_writer :options
28
+
29
+ # Returns the options hash for this node.
30
+ # Raises SassC::SyntaxError if the value was created
31
+ # outside of the parser and \{#to\_s} was called on it
32
+ def options
33
+ return @options if @options
34
+ raise SassC::SyntaxError.new("The #options attribute is not set on this #{self.class}. This error is probably occurring because #to_s was called on this value within a custom Sass function without first setting the #options attribute.")
35
+ end
36
+
37
+ # Returns the hash code of this value. Two objects' hash codes should be
38
+ # equal if the objects are equal.
39
+ def hash
40
+ value.hash
41
+ end
42
+
43
+ # True if this Value is the same as `other`
44
+ def eql?(other)
45
+ self == other
46
+ end
47
+
48
+ # Returns a system inspect value for this object
49
+ def inspect
50
+ value.inspect
51
+ end
52
+
53
+ # Returns `true` (all Values are truthy)
54
+ def to_bool
55
+ true
56
+ end
57
+
58
+ # Compares this object to `other`
59
+ def ==(other)
60
+ self.class == other.class && value == other.value
61
+ end
62
+
63
+ # Returns the integer value of this value.
64
+ # Raises SassC::SyntaxError if this value doesn’t implment integer conversion.
65
+ def to_i
66
+ raise SassC::SyntaxError.new("#{inspect} is not an integer.")
67
+ end
68
+
69
+ # @raise [SassC::SyntaxError] if this value isn't an integer
70
+ def assert_int!; to_i; end
71
+
72
+ # Returns the separator for this value. For non-list-like values or the
73
+ # empty list, this will be `nil`. For lists or maps, it will be `:space` or `:comma`.
74
+ def separator
75
+ nil
76
+ end
77
+
78
+ # Whether the value is surrounded by square brackets. For non-list values,
79
+ # this will be `false`.
80
+ def bracketed
81
+ false
82
+ end
83
+
84
+ # Returns the value of this Value as an array.
85
+ # Single Values are considered the same as single-element arrays.
86
+ def to_a
87
+ [self]
88
+ end
89
+
90
+ # Returns the value of this value as a hash. Most values don't have hash
91
+ # representations, but [Map]s and empty [List]s do.
92
+ #
93
+ # @return [Hash<Value, Value>] This value as a hash
94
+ # @raise [SassC::SyntaxError] if this value doesn't have a hash representation
95
+ def to_h
96
+ raise SassC::SyntaxError.new("#{inspect} is not a map.")
97
+ end
98
+
99
+ # Returns the string representation of this value
100
+ # as it would be output to the CSS document.
101
+ #
102
+ # @options opts :quote [String]
103
+ # The preferred quote style for quoted strings. If `:none`, strings are
104
+ # always emitted unquoted.
105
+ # @return [String]
106
+ def to_s(opts = {})
107
+ SassC::Util.abstract(self)
108
+ end
109
+ alias_method :to_sass, :to_s
110
+
111
+ # Returns `false` (all Values are truthy)
112
+ def null?
113
+ false
114
+ end
115
+
116
+ # Creates a new list containing `contents` but with the same brackets and
117
+ # separators as this object, when interpreted as a list.
118
+ #
119
+ # @param contents [Array<Value>] The contents of the new list.
120
+ # @param separator [Symbol] The separator of the new list. Defaults to \{#separator}.
121
+ # @param bracketed [Boolean] Whether the new list is bracketed. Defaults to \{#bracketed}.
122
+ # @return [Sass::Script::Value::List]
123
+ def with_contents(contents, separator: self.separator, bracketed: self.bracketed)
124
+ SassC::Script::Value::List.new(contents, separator: separator, bracketed: bracketed)
125
+ end
126
+
127
+ protected
128
+
129
+ # Evaluates the value.
130
+ #
131
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
132
+ # @return [Value] This value
133
+ def _perform(environment)
134
+ self
135
+ end
136
+
137
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ class Base
7
+ def initialize(value)
8
+ @value = value
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ class Bool < Base
7
+ def to_native
8
+ Native::make_boolean(@value.value)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ class Color < Base
7
+ def to_native
8
+ Native::make_color(
9
+ @value.red,
10
+ @value.green,
11
+ @value.blue,
12
+ @value.alpha
13
+ )
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ SEPARATORS = {
7
+ space: :sass_space,
8
+ comma: :sass_comma
9
+ }
10
+
11
+ class List < Base
12
+ def to_native
13
+ list = @value.to_a
14
+ sep = SEPARATORS.fetch(@value.separator)
15
+ native_list = Native::make_list(list.size, sep)
16
+ list.each_with_index do |item, index|
17
+ native_item = ValueConversion.to_native(item)
18
+ Native::list_set_value(native_list, index, native_item)
19
+ end
20
+ native_list
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ class Map < Base
7
+ def to_native
8
+ hash = @value.to_h
9
+ native_map = Native::make_map( hash.size )
10
+ hash.each_with_index do |(key, value), index|
11
+ key = ValueConversion.to_native key
12
+ value = ValueConversion.to_native value
13
+ Native::map_set_key( native_map, index, key )
14
+ Native::map_set_value( native_map, index, value )
15
+ end
16
+ return native_map
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ class Number < Base
7
+ def to_native
8
+ Native::make_number(@value.value, @value.numerator_units.first)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ class String < Base
7
+ def to_native(opts = {})
8
+ if opts[:quote] == :none || @value.type == :identifier
9
+ Native::make_string(@value.to_s)
10
+ else
11
+ Native::make_qstring(@value.to_s)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC::Script::ValueConversion
4
+
5
+ def self.from_native(native_value, options)
6
+ case value_tag = SassC::Native.value_get_tag(native_value)
7
+ when :sass_null
8
+ # no-op
9
+ when :sass_string
10
+ value = SassC::Native.string_get_value(native_value)
11
+ type = SassC::Native.string_get_type(native_value)
12
+ argument = SassC::Script::Value::String.new(value, type)
13
+ argument
14
+ when :sass_boolean
15
+ value = SassC::Native.boolean_get_value(native_value)
16
+ argument = SassC::Script::Value::Bool.new(value)
17
+ argument
18
+ when :sass_number
19
+ value = SassC::Native.number_get_value(native_value)
20
+ unit = SassC::Native.number_get_unit(native_value)
21
+ argument = SassC::Script::Value::Number.new(value, unit)
22
+ argument
23
+ when :sass_color
24
+ red, green, blue, alpha = SassC::Native.color_get_r(native_value), SassC::Native.color_get_g(native_value), SassC::Native.color_get_b(native_value), SassC::Native.color_get_a(native_value)
25
+ argument = SassC::Script::Value::Color.new(red:red, green:green, blue:blue, alpha:alpha)
26
+ argument.options = options
27
+ argument
28
+ when :sass_map
29
+ values = {}
30
+ length = SassC::Native::map_get_length native_value
31
+ (0..length-1).each do |index|
32
+ key = SassC::Native::map_get_key(native_value, index)
33
+ value = SassC::Native::map_get_value(native_value, index)
34
+ values[from_native(key, options)] = from_native(value, options)
35
+ end
36
+ argument = SassC::Script::Value::Map.new values
37
+ argument
38
+ when :sass_list
39
+ length = SassC::Native::list_get_length(native_value)
40
+ items = (0...length).map do |index|
41
+ native_item = SassC::Native::list_get_value(native_value, index)
42
+ from_native(native_item, options)
43
+ end
44
+ SassC::Script::Value::List.new(items, separator: :space)
45
+ else
46
+ raise UnsupportedValue.new("Sass argument of type #{value_tag} unsupported")
47
+ end
48
+ end
49
+
50
+ def self.to_native(value)
51
+ case value_name = value.class.name.split("::").last
52
+ when "String"
53
+ SassC::Script::ValueConversion::String.new(value).to_native
54
+ when "Color"
55
+ SassC::Script::ValueConversion::Color.new(value).to_native
56
+ when "Number"
57
+ SassC::Script::ValueConversion::Number.new(value).to_native
58
+ when "Map"
59
+ SassC::Script::ValueConversion::Map.new(value).to_native
60
+ when "List"
61
+ SassC::Script::ValueConversion::List.new(value).to_native
62
+ when "Bool"
63
+ SassC::Script::ValueConversion::Bool.new(value).to_native
64
+ else
65
+ raise SassC::UnsupportedValue.new("Sass return type #{value_name} unsupported")
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+
6
+ def self.custom_functions
7
+ Functions.instance_methods.select do |function|
8
+ Functions.public_method_defined?(function)
9
+ end
10
+ end
11
+
12
+ def self.formatted_function_name(function_name)
13
+ params = Functions.instance_method(function_name).parameters
14
+ params = params.map { |param_type, name| "$#{name}#{': null' if param_type == :opt}" }.join(", ")
15
+ return "#{function_name}(#{params})"
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ # A hash that normalizes its string keys while still allowing you to get back
6
+ # to the original keys that were stored. If several different values normalize
7
+ # to the same value, whichever is stored last wins.
8
+
9
+ class SassC::Util::NormalizedMap
10
+
11
+ # Create a normalized map
12
+ def initialize(map = nil)
13
+ @key_strings = {}
14
+ @map = {}
15
+ map.each {|key, value| self[key] = value} if map
16
+ end
17
+
18
+ # Specifies how to transform the key.
19
+ # This can be overridden to create other normalization behaviors.
20
+ def normalize(key)
21
+ key.tr("-", "_")
22
+ end
23
+
24
+ # Returns the version of `key` as it was stored before
25
+ # normalization. If `key` isn't in the map, returns it as it was
26
+ # passed in.
27
+ # @return [String]
28
+ def denormalize(key)
29
+ @key_strings[normalize(key)] || key
30
+ end
31
+
32
+ # @private
33
+ def []=(k, v)
34
+ normalized = normalize(k)
35
+ @map[normalized] = v
36
+ @key_strings[normalized] = k
37
+ v
38
+ end
39
+
40
+ # @private
41
+ def [](k)
42
+ @map[normalize(k)]
43
+ end
44
+
45
+ # @private
46
+ def has_key?(k)
47
+ @map.has_key?(normalize(k))
48
+ end
49
+
50
+ # @private
51
+ def delete(k)
52
+ normalized = normalize(k)
53
+ @key_strings.delete(normalized)
54
+ @map.delete(normalized)
55
+ end
56
+
57
+ # @return [Hash] Hash with the keys as they were stored (before normalization).
58
+ def as_stored
59
+ SassC::Util.map_keys(@map) {|k| @key_strings[k]}
60
+ end
61
+
62
+ def empty?
63
+ @map.empty?
64
+ end
65
+
66
+ def values
67
+ @map.values
68
+ end
69
+
70
+ def keys
71
+ @map.keys
72
+ end
73
+
74
+ def each
75
+ @map.each {|k, v| yield(k, v)}
76
+ end
77
+
78
+ def size
79
+ @map.size
80
+ end
81
+
82
+ def to_hash
83
+ @map.dup
84
+ end
85
+
86
+ def to_a
87
+ @map.to_a
88
+ end
89
+
90
+ def map
91
+ @map.map {|k, v| yield(k, v)}
92
+ end
93
+
94
+ def dup
95
+ d = super
96
+ d.send(:instance_variable_set, "@map", @map.dup)
97
+ d
98
+ end
99
+
100
+ def sort_by
101
+ @map.sort_by {|k, v| yield k, v}
102
+ end
103
+
104
+ def update(map)
105
+ map = map.as_stored if map.is_a?(NormalizedMap)
106
+ map.each {|k, v| self[k] = v}
107
+ end
108
+
109
+ def method_missing(method, *args, &block)
110
+ @map.send(method, *args, &block)
111
+ end
112
+
113
+ def respond_to_missing?(method, include_private = false)
114
+ @map.respond_to?(method, include_private)
115
+ end
116
+
117
+ end
data/lib/sassc/util.rb ADDED
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "set"
5
+ require "enumerator"
6
+ require "stringio"
7
+ require "rbconfig"
8
+ require "uri"
9
+ require "thread"
10
+ require "pathname"
11
+
12
+ # A module containing various useful functions.
13
+
14
+ module SassC::Util
15
+
16
+ extend self
17
+
18
+ # An array of ints representing the Ruby version number.
19
+ # @api public
20
+ RUBY_VERSION_COMPONENTS = RUBY_VERSION.split(".").map {|s| s.to_i}
21
+
22
+ # The Ruby engine we're running under. Defaults to `"ruby"`
23
+ # if the top-level constant is undefined.
24
+ # @api public
25
+ RUBY_ENGINE = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "ruby"
26
+
27
+ # Maps the keys in a hash according to a block.
28
+ # @example
29
+ # map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
30
+ # #=> {"foo" => "bar", "baz" => "bang"}
31
+ # @param hash [Hash] The hash to map
32
+ # @yield [key] A block in which the keys are transformed
33
+ # @yieldparam key [Object] The key that should be mapped
34
+ # @yieldreturn [Object] The new value for the key
35
+ # @return [Hash] The mapped hash
36
+ # @see #map_vals
37
+ # @see #map_hash
38
+ def map_keys(hash)
39
+ map_hash(hash) {|k, v| [yield(k), v]}
40
+ end
41
+
42
+ # Restricts the numeric `value` to be within `min` and `max`, inclusive.
43
+ # If the value is lower than `min`
44
+ def clamp(value, min, max)
45
+ return min if value < min
46
+ return max if value > max
47
+ return value
48
+ end
49
+
50
+ # Like [Fixnum.round], but leaves rooms for slight floating-point
51
+ # differences.
52
+ #
53
+ # @param value [Numeric]
54
+ # @return [Numeric]
55
+ def round(value)
56
+ # If the number is within epsilon of X.5, round up (or down for negative
57
+ # numbers).
58
+ mod = value % 1
59
+ mod_is_half = (mod - 0.5).abs < SassC::Script::Value::Number.epsilon
60
+ if value > 0
61
+ !mod_is_half && mod < 0.5 ? value.floor : value.ceil
62
+ else
63
+ mod_is_half || mod < 0.5 ? value.floor : value.ceil
64
+ end
65
+ end
66
+
67
+ # Return an array of all possible paths through the given arrays.
68
+ #
69
+ # @param arrs [Array<Array>]
70
+ # @return [Array<Arrays>]
71
+ #
72
+ # @example
73
+ # paths([[1, 2], [3, 4], [5]]) #=>
74
+ # # [[1, 3, 5],
75
+ # # [2, 3, 5],
76
+ # # [1, 4, 5],
77
+ # # [2, 4, 5]]
78
+ def paths(arrs)
79
+ arrs.inject([[]]) do |paths, arr|
80
+ arr.map {|e| paths.map {|path| path + [e]}}.flatten(1)
81
+ end
82
+ end
83
+
84
+ # Returns information about the caller of the previous method.
85
+ #
86
+ # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
87
+ # @return [[String, Integer, (String, nil)]]
88
+ # An array containing the filename, line, and method name of the caller.
89
+ # The method name may be nil
90
+ def caller_info(entry = nil)
91
+ # JRuby evaluates `caller` incorrectly when it's in an actual default argument.
92
+ entry ||= caller[1]
93
+ info = entry.scan(/^((?:[A-Za-z]:)?.*?):(-?.*?)(?::.*`(.+)')?$/).first
94
+ info[1] = info[1].to_i
95
+ # This is added by Rubinius to designate a block, but we don't care about it.
96
+ info[2].sub!(/ \{\}\Z/, '') if info[2]
97
+ info
98
+ end
99
+
100
+ # Throws a NotImplementedError for an abstract method.
101
+ #
102
+ # @param obj [Object] `self`
103
+ # @raise [NotImplementedError]
104
+ def abstract(obj)
105
+ raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
106
+ end
107
+
108
+ # Prints a deprecation warning for the caller method.
109
+ #
110
+ # @param obj [Object] `self`
111
+ # @param message [String] A message describing what to do instead.
112
+ def deprecated(obj, message = nil)
113
+ obj_class = obj.is_a?(Class) ? "#{obj}." : "#{obj.class}#"
114
+ full_message = "DEPRECATION WARNING: #{obj_class}#{caller_info[2]} " +
115
+ "will be removed in a future version of Sass.#{("\n" + message) if message}"
116
+ SassC::Util.sass_warn full_message
117
+ end
118
+
119
+ # Silences all Sass warnings within a block.
120
+ #
121
+ # @yield A block in which no Sass warnings will be printed
122
+ def silence_sass_warnings
123
+ old_level, Sass.logger.log_level = Sass.logger.log_level, :error
124
+ yield
125
+ ensure
126
+ SassC.logger.log_level = old_level
127
+ end
128
+
129
+ # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
130
+ #
131
+ # @param msg [String]
132
+ def sass_warn(msg)
133
+ Sass.logger.warn("#{msg}\n")
134
+ end
135
+
136
+ ## Cross Rails Version Compatibility
137
+
138
+ # Returns the root of the Rails application,
139
+ # if this is running in a Rails context.
140
+ # Returns `nil` if no such root is defined.
141
+ #
142
+ # @return [String, nil]
143
+ def rails_root
144
+ if defined?(::Rails.root)
145
+ return ::Rails.root.to_s if ::Rails.root
146
+ raise "ERROR: Rails.root is nil!"
147
+ end
148
+ return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
149
+ nil
150
+ end
151
+
152
+ # Returns the environment of the Rails application,
153
+ # if this is running in a Rails context.
154
+ # Returns `nil` if no such environment is defined.
155
+ #
156
+ # @return [String, nil]
157
+ def rails_env
158
+ return ::Rails.env.to_s if defined?(::Rails.env)
159
+ return RAILS_ENV.to_s if defined?(RAILS_ENV)
160
+ nil
161
+ end
162
+
163
+ ## Cross-OS Compatibility
164
+ #
165
+ # These methods are cached because some of them are called quite frequently
166
+ # and even basic checks like String#== are too costly to be called repeatedly.
167
+
168
+ # Whether or not this is running on Windows.
169
+ #
170
+ # @return [Boolean]
171
+ def windows?
172
+ return @windows if defined?(@windows)
173
+ @windows = (RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i)
174
+ end
175
+
176
+ # Whether or not this is running on IronRuby.
177
+ #
178
+ # @return [Boolean]
179
+ def ironruby?
180
+ return @ironruby if defined?(@ironruby)
181
+ @ironruby = RUBY_ENGINE == "ironruby"
182
+ end
183
+
184
+ # Whether or not this is running on Rubinius.
185
+ #
186
+ # @return [Boolean]
187
+ def rbx?
188
+ return @rbx if defined?(@rbx)
189
+ @rbx = RUBY_ENGINE == "rbx"
190
+ end
191
+
192
+ # Whether or not this is running on JRuby.
193
+ #
194
+ # @return [Boolean]
195
+ def jruby?
196
+ return @jruby if defined?(@jruby)
197
+ @jruby = RUBY_PLATFORM =~ /java/
198
+ end
199
+
200
+ # Returns an array of ints representing the JRuby version number.
201
+ #
202
+ # @return [Array<Integer>]
203
+ def jruby_version
204
+ @jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
205
+ end
206
+
207
+ # Returns `path` relative to `from`.
208
+ #
209
+ # This is like `Pathname#relative_path_from` except it accepts both strings
210
+ # and pathnames, it handles Windows path separators correctly, and it throws
211
+ # an error rather than crashing if the paths use different encodings
212
+ # (https://github.com/ruby/ruby/pull/713).
213
+ #
214
+ # @param path [String, Pathname]
215
+ # @param from [String, Pathname]
216
+ # @return [Pathname?]
217
+ def relative_path_from(path, from)
218
+ pathname(path.to_s).relative_path_from(pathname(from.to_s))
219
+ rescue NoMethodError => e
220
+ raise e unless e.name == :zero?
221
+
222
+ # Work around https://github.com/ruby/ruby/pull/713.
223
+ path = path.to_s
224
+ from = from.to_s
225
+ raise ArgumentError("Incompatible path encodings: #{path.inspect} is #{path.encoding}, " +
226
+ "#{from.inspect} is #{from.encoding}")
227
+ end
228
+
229
+ singleton_methods.each {|method| module_function method}
230
+
231
+ end