virtus 0.0.5 → 0.0.6
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/.travis.yml +9 -2
- data/.yardopts +1 -0
- data/History.md +51 -0
- data/{README.markdown → README.md} +63 -7
- data/TODO +2 -4
- data/VERSION +1 -1
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/roodi.yml +5 -6
- data/config/site.reek +3 -3
- data/examples/custom_coercion_spec.rb +50 -0
- data/examples/default_values_spec.rb +21 -0
- data/lib/virtus.rb +21 -6
- data/lib/virtus/attribute.rb +113 -253
- data/lib/virtus/attribute/array.rb +6 -3
- data/lib/virtus/attribute/boolean.rb +9 -28
- data/lib/virtus/attribute/date.rb +9 -12
- data/lib/virtus/attribute/date_time.rb +10 -12
- data/lib/virtus/attribute/decimal.rb +4 -11
- data/lib/virtus/attribute/float.rb +4 -11
- data/lib/virtus/attribute/hash.rb +5 -3
- data/lib/virtus/attribute/integer.rb +4 -11
- data/lib/virtus/attribute/numeric.rb +1 -0
- data/lib/virtus/attribute/object.rb +1 -0
- data/lib/virtus/attribute/string.rb +4 -11
- data/lib/virtus/attribute/time.rb +9 -16
- data/lib/virtus/class_methods.rb +42 -7
- data/lib/virtus/coercion.rb +32 -0
- data/lib/virtus/coercion/date.rb +26 -0
- data/lib/virtus/coercion/date_time.rb +26 -0
- data/lib/virtus/coercion/decimal.rb +40 -0
- data/lib/virtus/coercion/false_class.rb +24 -0
- data/lib/virtus/coercion/float.rb +24 -0
- data/lib/virtus/coercion/hash.rb +82 -0
- data/lib/virtus/coercion/integer.rb +60 -0
- data/lib/virtus/coercion/numeric.rb +66 -0
- data/lib/virtus/coercion/object.rb +25 -0
- data/lib/virtus/coercion/string.rb +155 -0
- data/lib/virtus/coercion/symbol.rb +24 -0
- data/lib/virtus/coercion/time.rb +26 -0
- data/lib/virtus/coercion/time_coercions.rb +85 -0
- data/lib/virtus/coercion/true_class.rb +24 -0
- data/lib/virtus/instance_methods.rb +7 -0
- data/lib/virtus/support/descendants_tracker.rb +1 -1
- data/lib/virtus/support/options.rb +114 -0
- data/lib/virtus/support/type_lookup.rb +95 -0
- data/spec/integration/virtus/attributes/attribute/{typecast_spec.rb → set_spec.rb} +7 -7
- data/spec/unit/shared/attribute.rb +3 -3
- data/spec/unit/shared/attribute/accept_options.rb +0 -18
- data/spec/unit/shared/attribute/accepted_options.rb +0 -6
- data/spec/unit/shared/attribute/get.rb +32 -17
- data/spec/unit/shared/attribute/inspect.rb +7 -0
- data/spec/unit/shared/attribute/primitive.rb +15 -0
- data/spec/unit/shared/attribute/set.rb +16 -21
- data/spec/unit/virtus/attribute/array_spec.rb +18 -3
- data/spec/unit/virtus/attribute/boolean_spec.rb +8 -6
- data/spec/unit/virtus/attribute/date_spec.rb +8 -6
- data/spec/unit/virtus/attribute/date_time_spec.rb +8 -6
- data/spec/unit/virtus/attribute/decimal_spec.rb +18 -6
- data/spec/unit/virtus/attribute/float_spec.rb +19 -7
- data/spec/unit/virtus/attribute/hash_spec.rb +5 -3
- data/spec/unit/virtus/attribute/integer_spec.rb +10 -8
- data/spec/unit/virtus/attribute/string_spec.rb +10 -8
- data/spec/unit/virtus/attribute/time_spec.rb +8 -6
- data/spec/unit/virtus/class_methods/attributes_spec.rb +11 -0
- data/spec/unit/virtus/coercion/class_name_reference_spec.rb +17 -0
- data/spec/unit/virtus/coercion/date/class_methods/to_datetime_spec.rb +30 -0
- data/spec/unit/virtus/coercion/date/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/date/class_methods/to_time_spec.rb +12 -0
- data/spec/unit/virtus/coercion/date_time/class_methods/to_date_spec.rb +30 -0
- data/spec/unit/virtus/coercion/date_time/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/date_time/class_methods/to_time_spec.rb +30 -0
- data/spec/unit/virtus/coercion/decimal/class_methods/to_float_spec.rb +12 -0
- data/spec/unit/virtus/coercion/decimal/class_methods/to_integer_spec.rb +12 -0
- data/spec/unit/virtus/coercion/decimal/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/false_class/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/float/class_methods/to_decimal_spec.rb +12 -0
- data/spec/unit/virtus/coercion/float/class_methods/to_integer_spec.rb +12 -0
- data/spec/unit/virtus/coercion/float/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/hash/class_methods/to_array_spec.rb +12 -0
- data/spec/unit/virtus/coercion/hash/class_methods/to_date_spec.rb +31 -0
- data/spec/unit/virtus/coercion/hash/class_methods/to_datetime_spec.rb +31 -0
- data/spec/unit/virtus/coercion/hash/class_methods/to_time_spec.rb +31 -0
- data/spec/unit/virtus/coercion/integer/class_methods/to_boolean_spec.rb +25 -0
- data/spec/unit/virtus/coercion/integer/class_methods/to_decimal_spec.rb +12 -0
- data/spec/unit/virtus/coercion/integer/class_methods/to_float_spec.rb +12 -0
- data/spec/unit/virtus/coercion/integer/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/object/class_methods/method_missing_spec.rb +33 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_boolean_spec.rb +29 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_date_spec.rb +23 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_datetime_spec.rb +50 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_decimal_spec.rb +23 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_float_spec.rb +21 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +21 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_time_spec.rb +50 -0
- data/spec/unit/virtus/coercion/symbol/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/true_class/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/instance_methods/attributes_spec.rb +7 -0
- data/spec/unit/virtus/options/accept_options_spec.rb +38 -0
- data/spec/unit/virtus/options/accepted_options_spec.rb +21 -0
- data/spec/unit/virtus/options/options_spec.rb +11 -0
- data/spec/unit/virtus/type_lookup/determine_type_spec.rb +68 -0
- data/spec/unit/virtus/type_lookup/primitive_spec.rb +9 -0
- data/virtus.gemspec +70 -17
- metadata +78 -27
- data/History.txt +0 -38
- data/lib/virtus/typecast/boolean.rb +0 -29
- data/lib/virtus/typecast/numeric.rb +0 -87
- data/lib/virtus/typecast/string.rb +0 -24
- data/lib/virtus/typecast/time.rb +0 -192
- data/spec/unit/shared/attribute/complex.rb +0 -15
- data/spec/unit/shared/attribute/options.rb +0 -7
- data/spec/unit/virtus/attribute/attribute_spec.rb +0 -12
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
|
|
3
|
+
# Coerce abstract class
|
|
4
|
+
#
|
|
5
|
+
# @abstract
|
|
6
|
+
#
|
|
7
|
+
class Coercion
|
|
8
|
+
extend DescendantsTracker
|
|
9
|
+
extend TypeLookup
|
|
10
|
+
extend Options
|
|
11
|
+
|
|
12
|
+
accept_options :primitive
|
|
13
|
+
|
|
14
|
+
# Return a class that matches given name
|
|
15
|
+
#
|
|
16
|
+
# Defaults to Virtus::Coercion::Object
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# Virtus::Coercion['String'] # => Virtus::Coercion::String
|
|
20
|
+
# Virtus::Coercion[String] # => Virtus::Coercion::String
|
|
21
|
+
#
|
|
22
|
+
# @param [String]
|
|
23
|
+
#
|
|
24
|
+
# @return [Class]
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
def self.[](name)
|
|
28
|
+
determine_type(name) || Coercion::Object
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end # Coerce
|
|
32
|
+
end # Virtus
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce Date values
|
|
5
|
+
class Date < Object
|
|
6
|
+
extend TimeCoercions
|
|
7
|
+
|
|
8
|
+
primitive ::Date
|
|
9
|
+
|
|
10
|
+
# Passthrough the value
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# Virtus::Coercion::DateTime.to_date(date) # => Date object
|
|
14
|
+
#
|
|
15
|
+
# @param [DateTime] value
|
|
16
|
+
#
|
|
17
|
+
# @return [Date]
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
def self.to_date(value)
|
|
21
|
+
value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end # class Date
|
|
25
|
+
end # class Coercion
|
|
26
|
+
end # module Virtus
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce DateTime values
|
|
5
|
+
class DateTime < Object
|
|
6
|
+
primitive ::DateTime
|
|
7
|
+
|
|
8
|
+
extend TimeCoercions
|
|
9
|
+
|
|
10
|
+
# Passthrough the value
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# Virtus::Coercion::DateTime.to_datetime(datetime) # => DateTime object
|
|
14
|
+
#
|
|
15
|
+
# @param [DateTime] value
|
|
16
|
+
#
|
|
17
|
+
# @return [Date]
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
def self.to_datetime(value)
|
|
21
|
+
value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end # class DateTime
|
|
25
|
+
end # class Coercion
|
|
26
|
+
end # module Virtus
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce BigDecimal values
|
|
5
|
+
class Decimal < Numeric
|
|
6
|
+
primitive ::BigDecimal
|
|
7
|
+
|
|
8
|
+
FLOAT_FORMAT = 'F'.freeze
|
|
9
|
+
|
|
10
|
+
# Coerce given value to String
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# Virtus::Coercion::BigDecimal.to_string(BigDecimal('1.0')) # => "1.0"
|
|
14
|
+
#
|
|
15
|
+
# @param [BigDecimal] value
|
|
16
|
+
#
|
|
17
|
+
# @return [String]
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
def self.to_string(value)
|
|
21
|
+
value.to_s(FLOAT_FORMAT)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Passthrough the value
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# Virtus::Coercion::BigDecimal.to_decimal(BigDecimal('1.0')) # => BigDecimal('1.0')
|
|
28
|
+
#
|
|
29
|
+
# @param [BigDecimal] value
|
|
30
|
+
#
|
|
31
|
+
# @return [Fixnum]
|
|
32
|
+
#
|
|
33
|
+
# @api public
|
|
34
|
+
def self.to_decimal(value)
|
|
35
|
+
value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end # class BigDecimal
|
|
39
|
+
end # class Coercion
|
|
40
|
+
end # module Virtus
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce false values
|
|
5
|
+
class FalseClass < Object
|
|
6
|
+
primitive ::FalseClass
|
|
7
|
+
|
|
8
|
+
# Coerce given value to String
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# Virtus::Coercion::FalseClass.to_string(false) # => "false"
|
|
12
|
+
#
|
|
13
|
+
# @param [FalseClass] value
|
|
14
|
+
#
|
|
15
|
+
# @return [String]
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
def self.to_string(value)
|
|
19
|
+
value.to_s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end # class FalseClass
|
|
23
|
+
end # class Coercion
|
|
24
|
+
end # module Virtus
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce Float values
|
|
5
|
+
class Float < Numeric
|
|
6
|
+
primitive ::Float
|
|
7
|
+
|
|
8
|
+
# Passthrough the value
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# Virtus::Coercion::Float.to_float(1.0) # => 1.0
|
|
12
|
+
#
|
|
13
|
+
# @param [Float] value
|
|
14
|
+
#
|
|
15
|
+
# @return [Integer]
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
def self.to_float(value)
|
|
19
|
+
value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end # class Float
|
|
23
|
+
end # class Coercion
|
|
24
|
+
end # module Virtus
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce Hash values
|
|
5
|
+
class Hash < Object
|
|
6
|
+
primitive ::Hash
|
|
7
|
+
|
|
8
|
+
TIME_SEGMENTS = [ :year, :month, :day, :hour, :min, :sec ].freeze
|
|
9
|
+
|
|
10
|
+
# Creates an Array instance from a Hash
|
|
11
|
+
#
|
|
12
|
+
# @param [Hash] value
|
|
13
|
+
#
|
|
14
|
+
# @return [Array]
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
def self.to_array(value)
|
|
18
|
+
value.to_a
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Creates a Time instance from a Hash
|
|
22
|
+
#
|
|
23
|
+
# Valid keys are: :year, :month, :day, :hour, :min, :sec
|
|
24
|
+
#
|
|
25
|
+
# @param [Hash] value
|
|
26
|
+
#
|
|
27
|
+
# @return [Time]
|
|
28
|
+
#
|
|
29
|
+
# @api private
|
|
30
|
+
def self.to_time(value)
|
|
31
|
+
::Time.local(*extract(value))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Creates a Date instance from a Hash
|
|
35
|
+
#
|
|
36
|
+
# Valid keys are: :year, :month, :day, :hour
|
|
37
|
+
#
|
|
38
|
+
# @param [Hash] value
|
|
39
|
+
#
|
|
40
|
+
# @return [Date]
|
|
41
|
+
#
|
|
42
|
+
# @api private
|
|
43
|
+
def self.to_date(value)
|
|
44
|
+
::Date.new(*extract(value).first(3))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Creates a DateTime instance from a Hash
|
|
48
|
+
#
|
|
49
|
+
# Valid keys are: :year, :month, :day, :hour, :min, :sec
|
|
50
|
+
#
|
|
51
|
+
# @param [Hash] value
|
|
52
|
+
#
|
|
53
|
+
# @return [DateTime]
|
|
54
|
+
#
|
|
55
|
+
# @api private
|
|
56
|
+
def self.to_datetime(value)
|
|
57
|
+
::DateTime.new(*extract(value))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Extracts the given args from a Hash
|
|
61
|
+
#
|
|
62
|
+
# If a value does not exist, it uses the value of Time.now
|
|
63
|
+
#
|
|
64
|
+
# @param [Hash] value
|
|
65
|
+
#
|
|
66
|
+
# @return [Array]
|
|
67
|
+
#
|
|
68
|
+
# @api private
|
|
69
|
+
def self.extract(value)
|
|
70
|
+
now = ::Time.now
|
|
71
|
+
|
|
72
|
+
TIME_SEGMENTS.map do |segment|
|
|
73
|
+
val = value.fetch(segment, now.send(segment))
|
|
74
|
+
Coercion[val.class.name].to_integer(val)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private_class_method :extract
|
|
79
|
+
|
|
80
|
+
end # class Hash
|
|
81
|
+
end # class Coercion
|
|
82
|
+
end # module Virtus
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce Fixnum values
|
|
5
|
+
class Integer < Numeric
|
|
6
|
+
primitive ::Integer
|
|
7
|
+
|
|
8
|
+
# Coerce given value to String
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# Virtus::Coercion::Fixnum.to_string(1) # => "1"
|
|
12
|
+
#
|
|
13
|
+
# @param [Fixnum] value
|
|
14
|
+
#
|
|
15
|
+
# @return [String]
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
def self.to_string(value)
|
|
19
|
+
value.to_s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Passthrough the value
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
# Virtus::Coercion::Fixnum.to_integer(1) # => 1
|
|
26
|
+
#
|
|
27
|
+
# @param [Fixnum] value
|
|
28
|
+
#
|
|
29
|
+
# @return [Float]
|
|
30
|
+
#
|
|
31
|
+
# @api public
|
|
32
|
+
def self.to_integer(value)
|
|
33
|
+
value
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Coerce given value to a Boolean
|
|
37
|
+
#
|
|
38
|
+
# @example with a 1
|
|
39
|
+
# Virtus::Coercion::Fixnum.to_boolean(1) # => true
|
|
40
|
+
#
|
|
41
|
+
# @example with a 0
|
|
42
|
+
# Virtus::Coercion::Fixnum.to_boolean(0) # => false
|
|
43
|
+
#
|
|
44
|
+
# @param [Fixnum] value
|
|
45
|
+
#
|
|
46
|
+
# @return [BigDecimal]
|
|
47
|
+
#
|
|
48
|
+
# @api public
|
|
49
|
+
def self.to_boolean(value)
|
|
50
|
+
case value
|
|
51
|
+
when 1 then true
|
|
52
|
+
when 0 then false
|
|
53
|
+
else
|
|
54
|
+
value
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end # class Fixnum
|
|
59
|
+
end # class Coercion
|
|
60
|
+
end # module Virtus
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
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
|
+
# Virtus::Coercion::Float.to_string(1.0) # => "1.0"
|
|
12
|
+
#
|
|
13
|
+
# @param [Float] value
|
|
14
|
+
#
|
|
15
|
+
# @return [String]
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
def self.to_string(value)
|
|
19
|
+
value.to_s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Creates a Fixnum instance from a numeric object
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
# Virtus::Coercion::BigDecimal.to_integer(BigDecimal('1.0')) # => 1
|
|
26
|
+
#
|
|
27
|
+
# @param [BigDecimal] value
|
|
28
|
+
#
|
|
29
|
+
# @return [Fixnum]
|
|
30
|
+
#
|
|
31
|
+
# @api public
|
|
32
|
+
def self.to_integer(value)
|
|
33
|
+
value.to_i
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Creates a Float instance from a numeric object
|
|
37
|
+
#
|
|
38
|
+
# @example
|
|
39
|
+
# Virtus::Coercion::BigDecimal.to_float(BigDecimal('1.0')) # => 1.0
|
|
40
|
+
#
|
|
41
|
+
# @param [BigDecimal] value
|
|
42
|
+
#
|
|
43
|
+
# @return [Fixnum]
|
|
44
|
+
#
|
|
45
|
+
# @api public
|
|
46
|
+
def self.to_float(value)
|
|
47
|
+
value.to_f
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Coerce given value to BigDecimal
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# Virtus::Coercion::Float.to_decimal(1.0) # => BigDecimal('1.0')
|
|
54
|
+
#
|
|
55
|
+
# @param [Float] value
|
|
56
|
+
#
|
|
57
|
+
# @return [BigDecimal]
|
|
58
|
+
#
|
|
59
|
+
# @api public
|
|
60
|
+
def self.to_decimal(value)
|
|
61
|
+
to_string(value).to_d
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end # class Coercion
|
|
66
|
+
end # module Virtus
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce Object values
|
|
5
|
+
class Object < Coercion
|
|
6
|
+
primitive ::Object
|
|
7
|
+
|
|
8
|
+
# Passthrough given value
|
|
9
|
+
#
|
|
10
|
+
# @param [Object] value
|
|
11
|
+
#
|
|
12
|
+
# @return [Object]
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
15
|
+
def self.method_missing(method, *args)
|
|
16
|
+
if method.to_s[0, 3] == 'to_' && args.size == 1
|
|
17
|
+
args.first
|
|
18
|
+
else
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end # class Object
|
|
24
|
+
end # class Coercion
|
|
25
|
+
end # module Virtus
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Coercion
|
|
3
|
+
|
|
4
|
+
# Coerce String values
|
|
5
|
+
class String < Object
|
|
6
|
+
primitive ::String
|
|
7
|
+
|
|
8
|
+
TRUE_VALUES = %w[ 1 t true ].freeze
|
|
9
|
+
FALSE_VALUES = %w[ 0 f false ].freeze
|
|
10
|
+
BOOLEAN_MAP = ::Hash[ TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
|
|
11
|
+
|
|
12
|
+
NUMERIC_REGEXP = /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/.freeze
|
|
13
|
+
|
|
14
|
+
# Coerce given value to Time
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# Virtus::Coercion::String.to_time(string) # => Time object
|
|
18
|
+
#
|
|
19
|
+
# @param [String] value
|
|
20
|
+
#
|
|
21
|
+
# @return [Time]
|
|
22
|
+
#
|
|
23
|
+
# @api public
|
|
24
|
+
def self.to_time(value)
|
|
25
|
+
parse_value(::Time, value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Coerce given value to Date
|
|
29
|
+
#
|
|
30
|
+
# @example
|
|
31
|
+
# Virtus::Coercion::String.to_date(string) # => Date object
|
|
32
|
+
#
|
|
33
|
+
# @param [String] value
|
|
34
|
+
#
|
|
35
|
+
# @return [Date]
|
|
36
|
+
#
|
|
37
|
+
# @api public
|
|
38
|
+
def self.to_date(value)
|
|
39
|
+
parse_value(::Date, value)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Coerce given value to DateTime
|
|
43
|
+
#
|
|
44
|
+
# @example
|
|
45
|
+
# Virtus::Coercion::String.to_datetime(string) # => DateTime object
|
|
46
|
+
#
|
|
47
|
+
# @param [String] value
|
|
48
|
+
#
|
|
49
|
+
# @return [DateTime]
|
|
50
|
+
#
|
|
51
|
+
# @api public
|
|
52
|
+
def self.to_datetime(value)
|
|
53
|
+
parse_value(::DateTime, value)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Coerce value to TrueClass or FalseClass
|
|
57
|
+
#
|
|
58
|
+
# @example with "T"
|
|
59
|
+
# Virtus::Coercion::String.to_boolean('T') # => true
|
|
60
|
+
#
|
|
61
|
+
# @example with "F"
|
|
62
|
+
# Virtus::Coercion::String.to_boolean('F') # => false
|
|
63
|
+
#
|
|
64
|
+
# @param [#to_s]
|
|
65
|
+
#
|
|
66
|
+
# @return [Boolean]
|
|
67
|
+
#
|
|
68
|
+
# @api public
|
|
69
|
+
def self.to_boolean(value)
|
|
70
|
+
BOOLEAN_MAP.fetch(value.downcase, value)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Coerce value to integer
|
|
74
|
+
#
|
|
75
|
+
# @example
|
|
76
|
+
# Virtus::Coercion::String.to_integer('1') # => 1
|
|
77
|
+
#
|
|
78
|
+
# @param [Object] value
|
|
79
|
+
#
|
|
80
|
+
# @return [Integer]
|
|
81
|
+
#
|
|
82
|
+
# @api public
|
|
83
|
+
def self.to_integer(value)
|
|
84
|
+
to_numeric(value, :to_i)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Coerce value to float
|
|
88
|
+
#
|
|
89
|
+
# @example
|
|
90
|
+
# Virtus::Coercion::String.to_float('1.2') # => 1.2
|
|
91
|
+
#
|
|
92
|
+
# @param [Object] value
|
|
93
|
+
#
|
|
94
|
+
# @return [Float]
|
|
95
|
+
#
|
|
96
|
+
# @api public
|
|
97
|
+
def self.to_float(value)
|
|
98
|
+
to_numeric(value, :to_f)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Coerce value to decimal
|
|
102
|
+
#
|
|
103
|
+
# @example
|
|
104
|
+
# Virtus::Coercion::String.to_decimal('1.2') # => #<BigDecimal:b72157d4,'0.12E1',8(8)>
|
|
105
|
+
#
|
|
106
|
+
# @param [Object] value
|
|
107
|
+
#
|
|
108
|
+
# @return [BigDecimal]
|
|
109
|
+
#
|
|
110
|
+
# @api public
|
|
111
|
+
def self.to_decimal(value)
|
|
112
|
+
to_numeric(value, :to_d)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Match numeric string
|
|
116
|
+
#
|
|
117
|
+
# @param [String] value
|
|
118
|
+
# value to typecast
|
|
119
|
+
# @param [Symbol] method
|
|
120
|
+
# method to typecast with
|
|
121
|
+
#
|
|
122
|
+
# @return [Numeric]
|
|
123
|
+
# number if matched, value if no match
|
|
124
|
+
#
|
|
125
|
+
# @api private
|
|
126
|
+
def self.to_numeric(value, method)
|
|
127
|
+
if value =~ NUMERIC_REGEXP
|
|
128
|
+
$1.send(method)
|
|
129
|
+
else
|
|
130
|
+
value
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private_class_method :to_numeric
|
|
135
|
+
|
|
136
|
+
# Parse the value or return it as-is if it is invalid
|
|
137
|
+
#
|
|
138
|
+
# @param [#parse] parser
|
|
139
|
+
#
|
|
140
|
+
# @param [String] value
|
|
141
|
+
#
|
|
142
|
+
# @return [Time]
|
|
143
|
+
#
|
|
144
|
+
# @api private
|
|
145
|
+
def self.parse_value(parser, value)
|
|
146
|
+
parser.parse(value)
|
|
147
|
+
rescue ArgumentError
|
|
148
|
+
return value
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private_class_method :parse_value
|
|
152
|
+
|
|
153
|
+
end # class String
|
|
154
|
+
end # class Coercion
|
|
155
|
+
end # module Virtus
|