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,155 @@
|
|
1
|
+
module Coercible
|
2
|
+
|
3
|
+
# Coercer object
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# coercer = Coercible::Coercer.new
|
9
|
+
#
|
10
|
+
# coercer[String].to_boolean('yes') # => true
|
11
|
+
# coercer[Integer].to_string(1) # => '1'
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
class Coercer
|
15
|
+
|
16
|
+
# Return coercer instances
|
17
|
+
#
|
18
|
+
# @return [Array<Coercer::Object>]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
attr_reader :coercers
|
22
|
+
|
23
|
+
# Returns global configuration for coercers
|
24
|
+
#
|
25
|
+
# @return [Configuration]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
attr_reader :config
|
29
|
+
|
30
|
+
# Build a new coercer
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
#
|
34
|
+
# Coercible::Coercer.new { |config| # set configuration }
|
35
|
+
#
|
36
|
+
# @yieldparam [Configuration]
|
37
|
+
#
|
38
|
+
# @return [Coercer]
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def self.new(&block)
|
42
|
+
configuration = Configuration.build(config_keys)
|
43
|
+
|
44
|
+
configurable_coercers.each do |coercer|
|
45
|
+
configuration.send("#{coercer.config_name}=", coercer.config)
|
46
|
+
end
|
47
|
+
|
48
|
+
yield(configuration) if block_given?
|
49
|
+
|
50
|
+
super({}, configuration)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return configuration keys for Coercer instance
|
54
|
+
#
|
55
|
+
# @return [Array<Symbol>]
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
def self.config_keys
|
59
|
+
configurable_coercers.map(&:config_name)
|
60
|
+
end
|
61
|
+
private_class_method :config_keys
|
62
|
+
|
63
|
+
# Return coercer classes that are configurable
|
64
|
+
#
|
65
|
+
# @return [Array<Class>]
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def self.configurable_coercers(&block)
|
69
|
+
Coercer::Object.descendants.select { |descendant|
|
70
|
+
descendant.respond_to?(:config)
|
71
|
+
}
|
72
|
+
end
|
73
|
+
private_class_method :configurable_coercers
|
74
|
+
|
75
|
+
# Initialize a new coercer instance
|
76
|
+
#
|
77
|
+
# @param [Hash] coercers
|
78
|
+
#
|
79
|
+
# @param [Configuration] config
|
80
|
+
#
|
81
|
+
# @return [undefined]
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
def initialize(coercers = {}, config = nil)
|
85
|
+
@coercers = coercers
|
86
|
+
@config = config
|
87
|
+
initialize_default_coercer
|
88
|
+
end
|
89
|
+
|
90
|
+
# Access a specific coercer object for the given type
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
#
|
94
|
+
# coercer[String] # => string coercer
|
95
|
+
# coercer[Integer] # => integer coercer
|
96
|
+
#
|
97
|
+
# @param [Class] type
|
98
|
+
#
|
99
|
+
# @return [Coercer::Object]
|
100
|
+
#
|
101
|
+
# @api public
|
102
|
+
def [](klass)
|
103
|
+
fetch(klass) || coercers[::Object]
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Fetch an existing coercer or create a new instance
|
109
|
+
#
|
110
|
+
# @return [Coercer::Object]
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
def fetch(klass)
|
114
|
+
coercers[klass] || initialize_coercer(klass)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Initialize a new coercer instance for the give type
|
118
|
+
#
|
119
|
+
# If a coercer class supports configuration it will receive it from the
|
120
|
+
# global configuration object
|
121
|
+
#
|
122
|
+
# @return [Coercer::Object]
|
123
|
+
#
|
124
|
+
# @api private
|
125
|
+
def initialize_coercer(klass)
|
126
|
+
coercers[klass] =
|
127
|
+
begin
|
128
|
+
coercer = Coercer::Object.determine_type(klass) || Coercer::Object
|
129
|
+
args = [ self ]
|
130
|
+
args << config_for(coercer) if coercer.respond_to?(:config_name)
|
131
|
+
coercer.new(*args)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Initialize default coercer object
|
136
|
+
#
|
137
|
+
# @return [Coercer::Object]
|
138
|
+
#
|
139
|
+
# @api private
|
140
|
+
def initialize_default_coercer
|
141
|
+
coercers[::Object] = Coercer::Object.new(self)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Find configuration for the given coercer type
|
145
|
+
#
|
146
|
+
# @return [Configuration]
|
147
|
+
#
|
148
|
+
# @api private
|
149
|
+
def config_for(coercer)
|
150
|
+
config.send(coercer.config_name)
|
151
|
+
end
|
152
|
+
|
153
|
+
end # class Coercer
|
154
|
+
|
155
|
+
end # module Coercible
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce Array values
|
5
|
+
class Array < Object
|
6
|
+
primitive ::Array
|
7
|
+
|
8
|
+
TIME_SEGMENTS = [ :year, :month, :day, :hour, :min, :sec ].freeze
|
9
|
+
|
10
|
+
# Creates a Set instance from an Array
|
11
|
+
#
|
12
|
+
# @param [Array] value
|
13
|
+
#
|
14
|
+
# @return [Array]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
def to_set(value)
|
18
|
+
value.to_set
|
19
|
+
end
|
20
|
+
|
21
|
+
end # class Array
|
22
|
+
|
23
|
+
end # class Coercer
|
24
|
+
end # module Coercible
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
module Configurable
|
5
|
+
|
6
|
+
# Add configuration-specific option keys to the descendant
|
7
|
+
#
|
8
|
+
# @return [self]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
def self.extended(coercer)
|
12
|
+
coercer.accept_options :config_keys
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
# Build configuration object for the coercer class
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
#
|
20
|
+
# coercer_class = Class.new(Coercer::Object) do
|
21
|
+
# extend Configurable
|
22
|
+
#
|
23
|
+
# config_keys [ :foo, :bar ]
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# coercer_class.config do |config|
|
27
|
+
# config.foo = '1'
|
28
|
+
# config.bar = '2'
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @yieldparam [Configuration]
|
32
|
+
#
|
33
|
+
# @return [Configuration]
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def config(&block)
|
37
|
+
configuration = Configuration.build(config_keys)
|
38
|
+
yield configuration
|
39
|
+
configuration
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return configuration name in the global config
|
43
|
+
#
|
44
|
+
# @return [Symbol]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
def config_name
|
48
|
+
self.name.downcase.split('::').last.to_sym
|
49
|
+
end
|
50
|
+
|
51
|
+
end # module Configurable
|
52
|
+
|
53
|
+
end # class Coercer
|
54
|
+
end # module Coercible
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce Date values
|
5
|
+
class Date < Object
|
6
|
+
include TimeCoercions
|
7
|
+
|
8
|
+
primitive ::Date
|
9
|
+
|
10
|
+
# Passthrough the value
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# coercer[DateTime].to_date(date) # => Date object
|
14
|
+
#
|
15
|
+
# @param [DateTime] value
|
16
|
+
#
|
17
|
+
# @return [Date]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def to_date(value)
|
21
|
+
value
|
22
|
+
end
|
23
|
+
|
24
|
+
end # class Date
|
25
|
+
|
26
|
+
end # class Coercer
|
27
|
+
end # module Coercible
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce DateTime values
|
5
|
+
class DateTime < Object
|
6
|
+
primitive ::DateTime
|
7
|
+
|
8
|
+
include TimeCoercions
|
9
|
+
|
10
|
+
# Passthrough the value
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# coercer[DateTime].to_datetime(datetime) # => DateTime object
|
14
|
+
#
|
15
|
+
# @param [DateTime] value
|
16
|
+
#
|
17
|
+
# @return [Date]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def to_datetime(value)
|
21
|
+
value
|
22
|
+
end
|
23
|
+
|
24
|
+
end # class DateTime
|
25
|
+
|
26
|
+
end # class Coercer
|
27
|
+
end # module Coercible
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
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
|
+
# coercer[BigDecimal].to_string(BigDecimal('1.0')) # => "1.0"
|
14
|
+
#
|
15
|
+
# @param [BigDecimal] value
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def to_string(value)
|
21
|
+
value.to_s(FLOAT_FORMAT)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Passthrough the value
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# Coercible::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 to_decimal(value)
|
35
|
+
value
|
36
|
+
end
|
37
|
+
|
38
|
+
end # class BigDecimal
|
39
|
+
|
40
|
+
end # class Coercer
|
41
|
+
end # module Coercible
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce false values
|
5
|
+
class FalseClass < Object
|
6
|
+
primitive ::FalseClass
|
7
|
+
|
8
|
+
# Coerce given value to String
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# coercer[FalseClass].to_string(false) # => "false"
|
12
|
+
#
|
13
|
+
# @param [FalseClass] value
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def to_string(value)
|
19
|
+
value.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class FalseClass
|
23
|
+
|
24
|
+
end # class Coercer
|
25
|
+
end # module Coercible
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce Float values
|
5
|
+
class Float < Numeric
|
6
|
+
primitive ::Float
|
7
|
+
|
8
|
+
# Passthrough the value
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# coercer[Float].to_float(1.0) # => 1.0
|
12
|
+
#
|
13
|
+
# @param [Float] value
|
14
|
+
#
|
15
|
+
# @return [Integer]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def to_float(value)
|
19
|
+
value
|
20
|
+
end
|
21
|
+
|
22
|
+
# Coerce given value to a DateTime
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# datetime = Coercible::Coercion::Float.to_datetime(1000000000.999) # => Sun, 09 Sep 2001 01:46:40 +0000
|
26
|
+
# datetime.to_f # => 1000000000.999
|
27
|
+
#
|
28
|
+
# @param [Float] value
|
29
|
+
#
|
30
|
+
# @return [DateTime]
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def to_datetime(value)
|
34
|
+
::DateTime.strptime((value * 10**3).to_s, "%Q")
|
35
|
+
end
|
36
|
+
|
37
|
+
end # class Float
|
38
|
+
|
39
|
+
end # class Coercer
|
40
|
+
end # module Coercible
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
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 a Time instance from a Hash
|
11
|
+
#
|
12
|
+
# Valid keys are: :year, :month, :day, :hour, :min, :sec
|
13
|
+
#
|
14
|
+
# @param [Hash] value
|
15
|
+
#
|
16
|
+
# @return [Time]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
def to_time(value)
|
20
|
+
::Time.local(*extract(value))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a Date instance from a Hash
|
24
|
+
#
|
25
|
+
# Valid keys are: :year, :month, :day, :hour
|
26
|
+
#
|
27
|
+
# @param [Hash] value
|
28
|
+
#
|
29
|
+
# @return [Date]
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def to_date(value)
|
33
|
+
::Date.new(*extract(value).first(3))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates a DateTime instance from a Hash
|
37
|
+
#
|
38
|
+
# Valid keys are: :year, :month, :day, :hour, :min, :sec
|
39
|
+
#
|
40
|
+
# @param [Hash] value
|
41
|
+
#
|
42
|
+
# @return [DateTime]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def to_datetime(value)
|
46
|
+
::DateTime.new(*extract(value))
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Extracts the given args from a Hash
|
52
|
+
#
|
53
|
+
# If a value does not exist, it uses the value of Time.now
|
54
|
+
#
|
55
|
+
# @param [Hash] value
|
56
|
+
#
|
57
|
+
# @return [Array]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
def extract(value)
|
61
|
+
now = ::Time.now
|
62
|
+
|
63
|
+
TIME_SEGMENTS.map do |segment|
|
64
|
+
val = value.fetch(segment, now.public_send(segment))
|
65
|
+
coercers[val.class].to_integer(val)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end # class Hash
|
70
|
+
|
71
|
+
end # class Coercer
|
72
|
+
end # module Coercible
|