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,25 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce Symbol values
|
5
|
+
class Symbol < Object
|
6
|
+
primitive ::Symbol
|
7
|
+
|
8
|
+
# Coerce given value to String
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# coercer[Symbol].to_string(:name) # => "name"
|
12
|
+
#
|
13
|
+
# @param [Symbol] value
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def to_string(value)
|
19
|
+
value.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class Symbol
|
23
|
+
|
24
|
+
end # class Coercer
|
25
|
+
end # module Coercible
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce Time values
|
5
|
+
class Time < Object
|
6
|
+
include TimeCoercions
|
7
|
+
|
8
|
+
primitive ::Time
|
9
|
+
|
10
|
+
# Passthrough the value
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# coercer[DateTime].to_time(time) # => Time object
|
14
|
+
#
|
15
|
+
# @param [DateTime] value
|
16
|
+
#
|
17
|
+
# @return [Date]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def to_time(value)
|
21
|
+
value
|
22
|
+
end
|
23
|
+
|
24
|
+
# Creates a Fixnum instance from a Time object
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# Coercible::Coercion::Time.to_integer(time) # => Fixnum object
|
28
|
+
#
|
29
|
+
# @param [Time] value
|
30
|
+
#
|
31
|
+
# @return [Fixnum]
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def to_integer(value)
|
35
|
+
value.to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
end # class Time
|
39
|
+
|
40
|
+
end # class Coercer
|
41
|
+
end # module Coercible
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Common time coercion methods
|
5
|
+
module TimeCoercions
|
6
|
+
|
7
|
+
# Coerce given value to String
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# coercer[Time].to_string(time) # => "Wed Jul 20 10:30:41 -0700 2011"
|
11
|
+
#
|
12
|
+
# @param [Date,Time,DateTime] value
|
13
|
+
#
|
14
|
+
# @return [String]
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def to_string(value)
|
18
|
+
value.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
# Coerce given value to Time
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# coercer[DateTime].to_time(datetime) # => Time object
|
25
|
+
#
|
26
|
+
# @param [Date,DateTime] value
|
27
|
+
#
|
28
|
+
# @return [Time]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def to_time(value)
|
32
|
+
coerce_with_method(value, :to_time)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Coerce given value to DateTime
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# coercer[Time].to_datetime(time) # => DateTime object
|
39
|
+
#
|
40
|
+
# @param [Date,Time] value
|
41
|
+
#
|
42
|
+
# @return [DateTime]
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def to_datetime(value)
|
46
|
+
coerce_with_method(value, :to_datetime)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Coerce given value to Date
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# coercer[Time].to_date(time) # => Date object
|
53
|
+
#
|
54
|
+
# @param [Time,DateTime] value
|
55
|
+
#
|
56
|
+
# @return [Date]
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def to_date(value)
|
60
|
+
coerce_with_method(value, :to_date)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Try to use native coercion method on the given value
|
66
|
+
#
|
67
|
+
# Falls back to String-based parsing
|
68
|
+
#
|
69
|
+
# @param [Date,DateTime,Time] value
|
70
|
+
# @param [Symbol] method
|
71
|
+
#
|
72
|
+
# @return [Date,DateTime,Time]
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
def coerce_with_method(value, method)
|
76
|
+
coerced = super
|
77
|
+
if coerced.equal?(value)
|
78
|
+
coercers[::String].public_send(method, to_string(value))
|
79
|
+
else
|
80
|
+
coerced
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end # module TimeCoercions
|
85
|
+
|
86
|
+
end # class Coercer
|
87
|
+
end # module Coercible
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Coercible
|
2
|
+
class Coercer
|
3
|
+
|
4
|
+
# Coerce true values
|
5
|
+
class TrueClass < Object
|
6
|
+
primitive ::TrueClass
|
7
|
+
|
8
|
+
# Coerce given value to String
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# coercer[TrueClass].to_string(true) # => "true"
|
12
|
+
#
|
13
|
+
# @param [TrueClass] value
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def to_string(value)
|
19
|
+
value.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class TrueClass
|
23
|
+
|
24
|
+
end # class Coercer
|
25
|
+
end # module Coercible
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Coercible
|
2
|
+
|
3
|
+
# Configuration object for global and per coercer type settings
|
4
|
+
#
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
# Build a configuration instance
|
8
|
+
#
|
9
|
+
# @param [Array] list of accessor keys
|
10
|
+
#
|
11
|
+
# @return [Configuration]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def self.build(keys, &block)
|
15
|
+
config = new
|
16
|
+
keys.each do |key|
|
17
|
+
config.instance_eval <<-RUBY
|
18
|
+
def #{key}
|
19
|
+
@#{key}
|
20
|
+
end
|
21
|
+
|
22
|
+
def #{key}=(value)
|
23
|
+
@#{key} = value
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
end
|
27
|
+
yield(config) if block_given?
|
28
|
+
config
|
29
|
+
end
|
30
|
+
|
31
|
+
end # class Configuration
|
32
|
+
|
33
|
+
end # module Coercible
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Coercible
|
2
|
+
|
3
|
+
# A module that adds class and instance level options
|
4
|
+
module Options
|
5
|
+
Undefined = Class.new.freeze
|
6
|
+
|
7
|
+
# Returns default options hash for a given attribute class
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# Virtus::Attribute::String.options
|
11
|
+
# # => {:primitive => String}
|
12
|
+
#
|
13
|
+
# @return [Hash]
|
14
|
+
# a hash of default option values
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def options
|
18
|
+
accepted_options.each_with_object({}) do |option_name, options|
|
19
|
+
option_value = send(option_name)
|
20
|
+
options[option_name] = option_value unless option_value.nil?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns an array of valid options
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# Virtus::Attribute::String.accepted_options
|
28
|
+
# # => [:primitive, :accessor, :reader, :writer]
|
29
|
+
#
|
30
|
+
# @return [Array]
|
31
|
+
# the array of valid option names
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def accepted_options
|
35
|
+
@accepted_options ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
# Defines which options are valid for a given attribute class
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# class MyAttribute < Virtus::Attribute::Object
|
42
|
+
# accept_options :foo, :bar
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# @return [self]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def accept_options(*new_options)
|
49
|
+
add_accepted_options(new_options)
|
50
|
+
new_options.each { |option| define_option_method(option) }
|
51
|
+
descendants.each { |descendant| descendant.add_accepted_options(new_options) }
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# Adds a reader/writer method for the give option name
|
58
|
+
#
|
59
|
+
# @return [undefined]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def define_option_method(option)
|
63
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
64
|
+
def self.#{option}(value = Undefined) # def self.primitive(value = Undefined)
|
65
|
+
return @#{option} if value.equal?(Undefined) # return @primitive if value.equal?(Undefined)
|
66
|
+
@#{option} = value # @primitive = value
|
67
|
+
self # self
|
68
|
+
end # end
|
69
|
+
RUBY
|
70
|
+
end
|
71
|
+
|
72
|
+
# Sets default options
|
73
|
+
#
|
74
|
+
# @param [#each] new_options
|
75
|
+
# options to be set
|
76
|
+
#
|
77
|
+
# @return [self]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def set_options(new_options)
|
81
|
+
new_options.each { |pair| send(*pair) }
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Adds new options that an attribute class can accept
|
86
|
+
#
|
87
|
+
# @param [#to_ary] new_options
|
88
|
+
# new options to be added
|
89
|
+
#
|
90
|
+
# @return [self]
|
91
|
+
#
|
92
|
+
# @api private
|
93
|
+
def add_accepted_options(new_options)
|
94
|
+
accepted_options.concat(new_options)
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Adds descendant to descendants array and inherits default options
|
101
|
+
#
|
102
|
+
# @param [Class] descendant
|
103
|
+
#
|
104
|
+
# @return [undefined]
|
105
|
+
#
|
106
|
+
# @api private
|
107
|
+
def inherited(descendant)
|
108
|
+
super
|
109
|
+
descendant.add_accepted_options(accepted_options).set_options(options)
|
110
|
+
end
|
111
|
+
|
112
|
+
end # module Options
|
113
|
+
end # module Virtus
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Coercible
|
2
|
+
|
3
|
+
# A module that adds type lookup to a class
|
4
|
+
module TypeLookup
|
5
|
+
|
6
|
+
TYPE_FORMAT = /\A[A-Z]\w*\z/.freeze
|
7
|
+
|
8
|
+
# Set cache ivar on the model
|
9
|
+
#
|
10
|
+
# @param [Class] model
|
11
|
+
#
|
12
|
+
# @return [undefined]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.extended(model)
|
16
|
+
model.instance_variable_set('@type_lookup_cache', {})
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a descendant based on a name or class
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# MyClass.determine_type('String') # => MyClass::String
|
23
|
+
#
|
24
|
+
# @param [Class, #to_s] class_or_name
|
25
|
+
# name of a class or a class itself
|
26
|
+
#
|
27
|
+
# @return [Class]
|
28
|
+
# a descendant
|
29
|
+
#
|
30
|
+
# @return [nil]
|
31
|
+
# nil if the type cannot be determined by the class_or_name
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def determine_type(class_or_name)
|
35
|
+
@type_lookup_cache[class_or_name] ||= determine_type_and_cache(class_or_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return the default primitive supported
|
39
|
+
#
|
40
|
+
# @return [Class]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def primitive
|
44
|
+
raise NotImplementedError, "#{self}.primitive must be implemented"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Determine type and cache the class
|
50
|
+
#
|
51
|
+
# @return [Class]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
def determine_type_and_cache(class_or_name)
|
55
|
+
case class_or_name
|
56
|
+
when singleton_class
|
57
|
+
determine_type_from_descendant(class_or_name)
|
58
|
+
when Class
|
59
|
+
determine_type_from_primitive(class_or_name)
|
60
|
+
else
|
61
|
+
determine_type_from_string(class_or_name.to_s)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return the class given a descendant
|
66
|
+
#
|
67
|
+
# @param [Class] descendant
|
68
|
+
#
|
69
|
+
# @return [Class]
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
def determine_type_from_descendant(descendant)
|
73
|
+
descendant if descendant < self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return the class given a primitive
|
77
|
+
#
|
78
|
+
# @param [Class] primitive
|
79
|
+
#
|
80
|
+
# @return [Class]
|
81
|
+
#
|
82
|
+
# @return [nil]
|
83
|
+
# nil if the type cannot be determined by the primitive
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def determine_type_from_primitive(primitive)
|
87
|
+
type = nil
|
88
|
+
descendants.reverse_each do |descendant|
|
89
|
+
descendant_primitive = descendant.primitive
|
90
|
+
next unless primitive <= descendant_primitive
|
91
|
+
type = descendant if type.nil? or type.primitive > descendant_primitive
|
92
|
+
end
|
93
|
+
type
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return the class given a string
|
97
|
+
#
|
98
|
+
# @param [String] string
|
99
|
+
#
|
100
|
+
# @return [Class]
|
101
|
+
#
|
102
|
+
# @return [nil]
|
103
|
+
# nil if the type cannot be determined by the string
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
def determine_type_from_string(string)
|
107
|
+
if string =~ TYPE_FORMAT and const_defined?(string, *EXTRA_CONST_ARGS)
|
108
|
+
const_get(string, *EXTRA_CONST_ARGS)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end # module TypeLookup
|
113
|
+
end # module Virtus
|