coercible 0.0.1
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.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +19 -0
- data/Changelog.md +4 -0
- data/Gemfile +6 -0
- data/Gemfile.devtools +44 -0
- data/Guardfile +58 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +2 -0
- data/coercible.gemspec +22 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/roodi.yml +17 -0
- data/config/site.reek +91 -0
- data/config/yardstick.yml +2 -0
- data/lib/coercible.rb +42 -0
- data/lib/coercible/coercer.rb +155 -0
- data/lib/coercible/coercer/array.rb +24 -0
- data/lib/coercible/coercer/configurable.rb +54 -0
- data/lib/coercible/coercer/date.rb +27 -0
- data/lib/coercible/coercer/date_time.rb +27 -0
- data/lib/coercible/coercer/decimal.rb +41 -0
- data/lib/coercible/coercer/false_class.rb +25 -0
- data/lib/coercible/coercer/float.rb +40 -0
- data/lib/coercible/coercer/hash.rb +72 -0
- data/lib/coercible/coercer/integer.rb +130 -0
- data/lib/coercible/coercer/numeric.rb +67 -0
- data/lib/coercible/coercer/object.rb +160 -0
- data/lib/coercible/coercer/string.rb +251 -0
- data/lib/coercible/coercer/symbol.rb +25 -0
- data/lib/coercible/coercer/time.rb +41 -0
- data/lib/coercible/coercer/time_coercions.rb +87 -0
- data/lib/coercible/coercer/true_class.rb +25 -0
- data/lib/coercible/configuration.rb +33 -0
- data/lib/coercible/version.rb +3 -0
- data/lib/support/options.rb +113 -0
- data/lib/support/type_lookup.rb +113 -0
- data/spec/integration/configuring_coercers_spec.rb +14 -0
- data/spec/shared/unit/configurable.rb +27 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/unit/coercible/coercer/array/to_set_spec.rb +12 -0
- data/spec/unit/coercible/coercer/class_methods/new_spec.rb +13 -0
- data/spec/unit/coercible/coercer/date/to_date_spec.rb +10 -0
- data/spec/unit/coercible/coercer/date/to_datetime_spec.rb +30 -0
- data/spec/unit/coercible/coercer/date/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/date/to_time_spec.rb +12 -0
- data/spec/unit/coercible/coercer/date_time/to_date_spec.rb +30 -0
- data/spec/unit/coercible/coercer/date_time/to_datetime_spec.rb +10 -0
- data/spec/unit/coercible/coercer/date_time/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/date_time/to_time_spec.rb +30 -0
- data/spec/unit/coercible/coercer/decimal/to_decimal_spec.rb +9 -0
- data/spec/unit/coercible/coercer/decimal/to_float_spec.rb +12 -0
- data/spec/unit/coercible/coercer/decimal/to_integer_spec.rb +12 -0
- data/spec/unit/coercible/coercer/decimal/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/element_reference_spec.rb +19 -0
- data/spec/unit/coercible/coercer/false_class/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/float/to_decimal_spec.rb +12 -0
- data/spec/unit/coercible/coercer/float/to_float_spec.rb +9 -0
- data/spec/unit/coercible/coercer/float/to_integer_spec.rb +12 -0
- data/spec/unit/coercible/coercer/float/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/hash/to_date_spec.rb +38 -0
- data/spec/unit/coercible/coercer/hash/to_datetime_spec.rb +38 -0
- data/spec/unit/coercible/coercer/hash/to_time_spec.rb +38 -0
- data/spec/unit/coercible/coercer/integer/to_boolean_spec.rb +27 -0
- data/spec/unit/coercible/coercer/integer/to_decimal_spec.rb +12 -0
- data/spec/unit/coercible/coercer/integer/to_float_spec.rb +12 -0
- data/spec/unit/coercible/coercer/integer/to_integer_spec.rb +9 -0
- data/spec/unit/coercible/coercer/integer/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/integer_spec.rb +11 -0
- data/spec/unit/coercible/coercer/numeric/to_decimal_spec.rb +10 -0
- data/spec/unit/coercible/coercer/numeric/to_float_spec.rb +10 -0
- data/spec/unit/coercible/coercer/numeric/to_integer_spec.rb +10 -0
- data/spec/unit/coercible/coercer/numeric/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/object/to_array_spec.rb +51 -0
- data/spec/unit/coercible/coercer/object/to_hash_spec.rb +22 -0
- data/spec/unit/coercible/coercer/object/to_integer_spec.rb +22 -0
- data/spec/unit/coercible/coercer/object/to_string_spec.rb +22 -0
- data/spec/unit/coercible/coercer/string/to_boolean_spec.rb +31 -0
- data/spec/unit/coercible/coercer/string/to_constant_spec.rb +49 -0
- data/spec/unit/coercible/coercer/string/to_date_spec.rb +25 -0
- data/spec/unit/coercible/coercer/string/to_datetime_spec.rb +52 -0
- data/spec/unit/coercible/coercer/string/to_decimal_spec.rb +47 -0
- data/spec/unit/coercible/coercer/string/to_float_spec.rb +57 -0
- data/spec/unit/coercible/coercer/string/to_integer_spec.rb +68 -0
- data/spec/unit/coercible/coercer/string/to_symbol_spec.rb +9 -0
- data/spec/unit/coercible/coercer/string/to_time_spec.rb +52 -0
- data/spec/unit/coercible/coercer/string_spec.rb +11 -0
- data/spec/unit/coercible/coercer/symbol/to_string_spec.rb +12 -0
- data/spec/unit/coercible/coercer/time/to_integer_spec.rb +10 -0
- data/spec/unit/coercible/coercer/time/to_time_spec.rb +10 -0
- data/spec/unit/coercible/coercer/time_coercions/to_date_spec.rb +30 -0
- data/spec/unit/coercible/coercer/time_coercions/to_datetime_spec.rb +34 -0
- data/spec/unit/coercible/coercer/time_coercions/to_string_spec.rb +19 -0
- data/spec/unit/coercible/coercer/time_coercions/to_time_spec.rb +34 -0
- data/spec/unit/coercible/coercer/true_class/to_string_spec.rb +12 -0
- data/spec/unit/coercible/configuration/class_methods/build_spec.rb +15 -0
- metadata +235 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce Fixnum values
|
5
|
+
class Integer < Numeric
|
6
|
+
extend Configurable
|
7
|
+
|
8
|
+
primitive ::Integer
|
9
|
+
|
10
|
+
config_keys [ :datetime_format, :datetime_proc, :boolean_map ]
|
11
|
+
|
12
|
+
# Return default config for Integer coercer type
|
13
|
+
#
|
14
|
+
# @return [Configuration]
|
15
|
+
#
|
16
|
+
# @see Configurable#config
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
def self.config
|
20
|
+
# FIXME: Remove after Rubinius 2.0 is released
|
21
|
+
is_rbx = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
22
|
+
|
23
|
+
super do |config|
|
24
|
+
config.datetime_format = is_rbx ? '%Q' : '%s'
|
25
|
+
|
26
|
+
config.datetime_proc = is_rbx ?
|
27
|
+
Proc.new { |value| "#{value * 10**3}" } : Proc.new { |value| "#{value}" }
|
28
|
+
|
29
|
+
config.boolean_map = { 0 => false, 1 => true }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return datetime format from config
|
34
|
+
#
|
35
|
+
# @return [::String]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
attr_reader :datetime_format
|
39
|
+
|
40
|
+
# Return datetime proc from config
|
41
|
+
#
|
42
|
+
# @return [Proc]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
attr_reader :datetime_proc
|
46
|
+
|
47
|
+
# Return boolean map from config
|
48
|
+
#
|
49
|
+
# @return [::Hash]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
attr_reader :boolean_map
|
53
|
+
|
54
|
+
# Initialize a new Integer coercer instance and set its configuration
|
55
|
+
#
|
56
|
+
# @return [undefined]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def initialize(coercer = Coercer.new, config = self.class.config)
|
60
|
+
super(coercer)
|
61
|
+
@boolean_map = config.boolean_map
|
62
|
+
@datetime_format = config.datetime_format
|
63
|
+
@datetime_proc = config.datetime_proc
|
64
|
+
end
|
65
|
+
|
66
|
+
# Coerce given value to String
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# coercer[Integer].to_string(1) # => "1"
|
70
|
+
#
|
71
|
+
# @param [Fixnum] value
|
72
|
+
#
|
73
|
+
# @return [String]
|
74
|
+
#
|
75
|
+
# @api public
|
76
|
+
def to_string(value)
|
77
|
+
value.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
# Passthrough the value
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# coercer[Integer].to_integer(1) # => 1
|
84
|
+
#
|
85
|
+
# @param [Fixnum] value
|
86
|
+
#
|
87
|
+
# @return [Float]
|
88
|
+
#
|
89
|
+
# @api public
|
90
|
+
def to_integer(value)
|
91
|
+
value
|
92
|
+
end
|
93
|
+
|
94
|
+
# Coerce given value to a Boolean
|
95
|
+
#
|
96
|
+
# @example with a 1
|
97
|
+
# coercer[Integer].to_boolean(1) # => true
|
98
|
+
#
|
99
|
+
# @example with a 0
|
100
|
+
# coercer[Integer].to_boolean(0) # => false
|
101
|
+
#
|
102
|
+
# @param [Fixnum] value
|
103
|
+
#
|
104
|
+
# @return [BigDecimal]
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
def to_boolean(value)
|
108
|
+
boolean_map.fetch(value) {
|
109
|
+
raise_unsupported_coercion(value, __method__)
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
# Coerce given value to a DateTime
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# coercer[Integer].to_datetime(0) # => Thu, 01 Jan 1970 00:00:00 +0000
|
117
|
+
#
|
118
|
+
# @param [Integer] value
|
119
|
+
#
|
120
|
+
# @return [DateTime]
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def to_datetime(value)
|
124
|
+
::DateTime.strptime(datetime_proc.call(value), datetime_format)
|
125
|
+
end
|
126
|
+
|
127
|
+
end # class Fixnum
|
128
|
+
|
129
|
+
end # class Coercer
|
130
|
+
end # module Coercible
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Base class for all numeric Coercion classes
|
5
|
+
class Numeric < Object
|
6
|
+
primitive ::Numeric
|
7
|
+
|
8
|
+
# Coerce given value to String
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# coercer[Numeric].to_string(Rational(2, 2)) # => "1.0"
|
12
|
+
#
|
13
|
+
# @param [Numeric] value
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def to_string(value)
|
19
|
+
value.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates an Integer instance from a numeric object
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# coercer[Numeric].to_integer(Rational(2, 2)) # => 1
|
26
|
+
#
|
27
|
+
# @param [Numeric] value
|
28
|
+
#
|
29
|
+
# @return [Integer]
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
def to_integer(value)
|
33
|
+
value.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates a Float instance from a numeric object
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# coercer[Numeric].to_float(Rational(2, 2)) # => 1.0
|
40
|
+
#
|
41
|
+
# @param [Numeric] value
|
42
|
+
#
|
43
|
+
# @return [Float]
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def to_float(value)
|
47
|
+
value.to_f
|
48
|
+
end
|
49
|
+
|
50
|
+
# Coerce a BigDecimal instance from a numeric object
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# coercer[Numeric].to_decimal(Rational(2, 2)) # => BigDecimal('1.0')
|
54
|
+
#
|
55
|
+
# @param [Numeric] value
|
56
|
+
#
|
57
|
+
# @return [BigDecimal]
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def to_decimal(value)
|
61
|
+
to_string(value).to_d
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class Numeric
|
65
|
+
|
66
|
+
end # class Coercer
|
67
|
+
end # module Coercible
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce Object values
|
5
|
+
class Object
|
6
|
+
extend DescendantsTracker, TypeLookup, Options
|
7
|
+
|
8
|
+
accept_options :primitive
|
9
|
+
|
10
|
+
primitive ::Object
|
11
|
+
|
12
|
+
COERCION_METHOD_REGEXP = /\Ato_/.freeze
|
13
|
+
|
14
|
+
# Return coercers object
|
15
|
+
#
|
16
|
+
# @return [Coercer]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
attr_reader :coercers
|
20
|
+
|
21
|
+
# Initialize a new coercer instance
|
22
|
+
#
|
23
|
+
# @param [Coercer] coercers
|
24
|
+
#
|
25
|
+
# @return [undefined]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
def initialize(coercers = Coercer.new)
|
29
|
+
@coercers = coercers
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create an Array from any Object
|
33
|
+
#
|
34
|
+
# @example with an object that does not respond to #to_a or #to_ary
|
35
|
+
# coercer[Object].to_array(value) # => [ value ]
|
36
|
+
#
|
37
|
+
# @example with an object that responds to #to_a
|
38
|
+
# coercer[Object].to_array(Set[ value ]) # => [ value ]
|
39
|
+
#
|
40
|
+
# @example with n object that responds to #to_ary
|
41
|
+
# coercer[Object].to_array([ value ]) # => [ value ]
|
42
|
+
#
|
43
|
+
# @param [#to_a,#to_ary,Object] value
|
44
|
+
# @param [#to_a,#to_ary,Object] value
|
45
|
+
#
|
46
|
+
# @return [Array]
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
def to_array(value)
|
50
|
+
Array(value)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create a Hash from the Object if possible
|
54
|
+
#
|
55
|
+
# @example with a coercible object
|
56
|
+
# coercer[Object].to_hash(key => value) # => { key => value }
|
57
|
+
#
|
58
|
+
# @example with an object that is not coercible
|
59
|
+
# coercer[Object].to_hash(value) # => value
|
60
|
+
#
|
61
|
+
# @param [#to_hash, Object] value
|
62
|
+
#
|
63
|
+
# @return [Hash]
|
64
|
+
# returns a Hash when the object can be coerced
|
65
|
+
# @return [Object]
|
66
|
+
# returns the value when the object cannot be coerced
|
67
|
+
#
|
68
|
+
# @api public
|
69
|
+
def to_hash(value)
|
70
|
+
coerce_with_method(value, :to_hash)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Create a String from the Object if possible
|
74
|
+
#
|
75
|
+
# @example with a coercible object
|
76
|
+
# coercer[Object].to_string("string") # => "string"
|
77
|
+
#
|
78
|
+
# @example with an object that is not coercible
|
79
|
+
# coercer[Object].to_string(value) # => value
|
80
|
+
#
|
81
|
+
# @param [#to_str, Object] value
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
# returns a String when the object can be coerced
|
85
|
+
# @return [Object]
|
86
|
+
# returns the value when the object cannot be coerced
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
def to_string(value)
|
90
|
+
coerce_with_method(value, :to_str)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Create an Integer from the Object if possible
|
94
|
+
#
|
95
|
+
# @example with a coercible object
|
96
|
+
# coercer[Object].to_integer(1) # => 1
|
97
|
+
#
|
98
|
+
# @example with an object that is not coercible
|
99
|
+
# coercer[Object].to_integer(value) # => value
|
100
|
+
#
|
101
|
+
# @param [#to_int, Object] value
|
102
|
+
#
|
103
|
+
# @return [Integer]
|
104
|
+
# returns an Integer when the object can be coerced
|
105
|
+
# @return [Object]
|
106
|
+
# returns the value when the object cannot be coerced
|
107
|
+
#
|
108
|
+
# @api public
|
109
|
+
def to_integer(value)
|
110
|
+
coerce_with_method(value, :to_int)
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# Raise an unsupported coercion error
|
116
|
+
#
|
117
|
+
# @raises [UnsupportedCoercion]
|
118
|
+
#
|
119
|
+
# @return [undefined]
|
120
|
+
#
|
121
|
+
# @api private
|
122
|
+
def raise_unsupported_coercion(value, method)
|
123
|
+
raise(
|
124
|
+
UnsupportedCoercion,
|
125
|
+
"#{self.class}##{method} doesn't know how to coerce #{value.inspect}"
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Passthrough given value
|
130
|
+
#
|
131
|
+
# @param [Object] value
|
132
|
+
#
|
133
|
+
# @return [Object]
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def method_missing(method, *args)
|
137
|
+
if method.to_s =~ COERCION_METHOD_REGEXP and args.size == 1
|
138
|
+
args.first
|
139
|
+
else
|
140
|
+
super
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Try to use native coercion method on the given value
|
145
|
+
#
|
146
|
+
# @param [Object] value
|
147
|
+
#
|
148
|
+
# @param [Symbol] method
|
149
|
+
#
|
150
|
+
# @return [Object]
|
151
|
+
#
|
152
|
+
# @api private
|
153
|
+
def coerce_with_method(value, method)
|
154
|
+
value.respond_to?(method) ? value.public_send(method) : value
|
155
|
+
end
|
156
|
+
|
157
|
+
end # class Object
|
158
|
+
|
159
|
+
end # class Coercer
|
160
|
+
end # module Coercible
|
@@ -0,0 +1,251 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce String values
|
5
|
+
class String < Object
|
6
|
+
extend Configurable
|
7
|
+
|
8
|
+
primitive ::String
|
9
|
+
|
10
|
+
config_keys [ :boolean_map ]
|
11
|
+
|
12
|
+
TRUE_VALUES = %w[ 1 on t true y yes ].freeze
|
13
|
+
FALSE_VALUES = %w[ 0 off f false n no ].freeze
|
14
|
+
BOOLEAN_MAP = ::Hash[ TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
|
15
|
+
|
16
|
+
INTEGER_REGEXP = /[-+]?(?:[0-9]\d*)/.freeze
|
17
|
+
EXPONENT_REGEXP = /(?:[eE][-+]?\d+)/.freeze
|
18
|
+
FRACTIONAL_REGEXP = /(?:\.\d+)/.freeze
|
19
|
+
|
20
|
+
NUMERIC_REGEXP = /\A(
|
21
|
+
#{INTEGER_REGEXP}#{FRACTIONAL_REGEXP}?#{EXPONENT_REGEXP}? |
|
22
|
+
#{FRACTIONAL_REGEXP}#{EXPONENT_REGEXP}?
|
23
|
+
)\z/x.freeze
|
24
|
+
|
25
|
+
# Return default configuration for string coercer type
|
26
|
+
#
|
27
|
+
# @return [Configuration]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
def self.config
|
31
|
+
super { |config| config.boolean_map = BOOLEAN_MAP }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return boolean map from the config
|
35
|
+
#
|
36
|
+
# @return [::Hash]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
attr_reader :boolean_map
|
40
|
+
|
41
|
+
# Initialize a new string coercer instance
|
42
|
+
#
|
43
|
+
# @param [Coercer]
|
44
|
+
#
|
45
|
+
# @param [Configuration]
|
46
|
+
#
|
47
|
+
# @return [undefined]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def initialize(coercer = Coercer.new, config = self.class.config)
|
51
|
+
super(coercer)
|
52
|
+
@boolean_map = config.boolean_map
|
53
|
+
end
|
54
|
+
|
55
|
+
# Coerce give value to a constant
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# coercer[String].to_constant('String') # => String
|
59
|
+
#
|
60
|
+
# @param [String] value
|
61
|
+
#
|
62
|
+
# @return [Object]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def to_constant(value)
|
66
|
+
names = value.split('::')
|
67
|
+
names.shift if names.first.empty?
|
68
|
+
names.inject(::Object) { |*args| constant_lookup(*args) }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Coerce give value to a symbol
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# coercer[String].to_symbol('string') # => :string
|
75
|
+
#
|
76
|
+
# @param [String] value
|
77
|
+
#
|
78
|
+
# @return [Symbol]
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
def to_symbol(value)
|
82
|
+
value.to_sym
|
83
|
+
end
|
84
|
+
|
85
|
+
# Coerce given value to Time
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# coercer[String].to_time(string) # => Time object
|
89
|
+
#
|
90
|
+
# @param [String] value
|
91
|
+
#
|
92
|
+
# @return [Time]
|
93
|
+
#
|
94
|
+
# @api public
|
95
|
+
def to_time(value)
|
96
|
+
parse_value(::Time, value, __method__)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Coerce given value to Date
|
100
|
+
#
|
101
|
+
# @example
|
102
|
+
# coercer[String].to_date(string) # => Date object
|
103
|
+
#
|
104
|
+
# @param [String] value
|
105
|
+
#
|
106
|
+
# @return [Date]
|
107
|
+
#
|
108
|
+
# @api public
|
109
|
+
def to_date(value)
|
110
|
+
parse_value(::Date, value, __method__)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Coerce given value to DateTime
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# coercer[String].to_datetime(string) # => DateTime object
|
117
|
+
#
|
118
|
+
# @param [String] value
|
119
|
+
#
|
120
|
+
# @return [DateTime]
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def to_datetime(value)
|
124
|
+
parse_value(::DateTime, value, __method__)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Coerce value to TrueClass or FalseClass
|
128
|
+
#
|
129
|
+
# @example with "T"
|
130
|
+
# coercer[String].to_boolean('T') # => true
|
131
|
+
#
|
132
|
+
# @example with "F"
|
133
|
+
# coercer[String].to_boolean('F') # => false
|
134
|
+
#
|
135
|
+
# @param [#to_s]
|
136
|
+
#
|
137
|
+
# @return [Boolean]
|
138
|
+
#
|
139
|
+
# @api public
|
140
|
+
def to_boolean(value)
|
141
|
+
boolean_map.fetch(value.downcase) {
|
142
|
+
raise_unsupported_coercion(value, __method__)
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
# Coerce value to integer
|
147
|
+
#
|
148
|
+
# @example
|
149
|
+
# coercer[String].to_integer('1') # => 1
|
150
|
+
#
|
151
|
+
# @param [Object] value
|
152
|
+
#
|
153
|
+
# @return [Integer]
|
154
|
+
#
|
155
|
+
# @api public
|
156
|
+
def to_integer(value)
|
157
|
+
if value =~ /\A#{INTEGER_REGEXP}\z/
|
158
|
+
value.to_i
|
159
|
+
else
|
160
|
+
# coerce to a Float first to evaluate scientific notation (if any)
|
161
|
+
# that may change the integer part, then convert to an integer
|
162
|
+
coerced = to_float(value)
|
163
|
+
::Float === coerced ? coerced.to_i : coerced
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Coerce value to float
|
168
|
+
#
|
169
|
+
# @example
|
170
|
+
# coercer[String].to_float('1.2') # => 1.2
|
171
|
+
#
|
172
|
+
# @param [Object] value
|
173
|
+
#
|
174
|
+
# @return [Float]
|
175
|
+
#
|
176
|
+
# @api public
|
177
|
+
def to_float(value)
|
178
|
+
to_numeric(value, :to_f)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Coerce value to decimal
|
182
|
+
#
|
183
|
+
# @example
|
184
|
+
# coercer[String].to_decimal('1.2') # => #<BigDecimal:b72157d4,'0.12E1',8(8)>
|
185
|
+
#
|
186
|
+
# @param [Object] value
|
187
|
+
#
|
188
|
+
# @return [BigDecimal]
|
189
|
+
#
|
190
|
+
# @api public
|
191
|
+
def to_decimal(value)
|
192
|
+
to_numeric(value, :to_d)
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
# Lookup a constant within a module
|
198
|
+
#
|
199
|
+
# @param [Module] mod
|
200
|
+
#
|
201
|
+
# @param [String] name
|
202
|
+
#
|
203
|
+
# @return [Object]
|
204
|
+
#
|
205
|
+
# @api private
|
206
|
+
def constant_lookup(mod, name)
|
207
|
+
if mod.const_defined?(name, *EXTRA_CONST_ARGS)
|
208
|
+
mod.const_get(name, *EXTRA_CONST_ARGS)
|
209
|
+
else
|
210
|
+
mod.const_missing(name)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Match numeric string
|
215
|
+
#
|
216
|
+
# @param [String] value
|
217
|
+
# value to typecast
|
218
|
+
# @param [Symbol] method
|
219
|
+
# method to typecast with
|
220
|
+
#
|
221
|
+
# @return [Numeric]
|
222
|
+
# number if matched, value if no match
|
223
|
+
#
|
224
|
+
# @api private
|
225
|
+
def to_numeric(value, method)
|
226
|
+
if value =~ NUMERIC_REGEXP
|
227
|
+
$1.public_send(method)
|
228
|
+
else
|
229
|
+
value
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Parse the value or return it as-is if it is invalid
|
234
|
+
#
|
235
|
+
# @param [#parse] parser
|
236
|
+
#
|
237
|
+
# @param [String] value
|
238
|
+
#
|
239
|
+
# @return [Time]
|
240
|
+
#
|
241
|
+
# @api private
|
242
|
+
def parse_value(parser, value, method)
|
243
|
+
parser.parse(value)
|
244
|
+
rescue ArgumentError
|
245
|
+
raise_unsupported_coercion(value, method)
|
246
|
+
end
|
247
|
+
|
248
|
+
end # class String
|
249
|
+
|
250
|
+
end # class Coercer
|
251
|
+
end # module Coercible
|