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