sassc 2.1.0.pre1-x86-mingw32

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 (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