coercible 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +19 -0
  4. data/Changelog.md +4 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.devtools +44 -0
  7. data/Guardfile +58 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +64 -0
  10. data/Rakefile +2 -0
  11. data/coercible.gemspec +22 -0
  12. data/config/flay.yml +3 -0
  13. data/config/flog.yml +2 -0
  14. data/config/mutant.yml +3 -0
  15. data/config/roodi.yml +17 -0
  16. data/config/site.reek +91 -0
  17. data/config/yardstick.yml +2 -0
  18. data/lib/coercible.rb +42 -0
  19. data/lib/coercible/coercer.rb +155 -0
  20. data/lib/coercible/coercer/array.rb +24 -0
  21. data/lib/coercible/coercer/configurable.rb +54 -0
  22. data/lib/coercible/coercer/date.rb +27 -0
  23. data/lib/coercible/coercer/date_time.rb +27 -0
  24. data/lib/coercible/coercer/decimal.rb +41 -0
  25. data/lib/coercible/coercer/false_class.rb +25 -0
  26. data/lib/coercible/coercer/float.rb +40 -0
  27. data/lib/coercible/coercer/hash.rb +72 -0
  28. data/lib/coercible/coercer/integer.rb +130 -0
  29. data/lib/coercible/coercer/numeric.rb +67 -0
  30. data/lib/coercible/coercer/object.rb +160 -0
  31. data/lib/coercible/coercer/string.rb +251 -0
  32. data/lib/coercible/coercer/symbol.rb +25 -0
  33. data/lib/coercible/coercer/time.rb +41 -0
  34. data/lib/coercible/coercer/time_coercions.rb +87 -0
  35. data/lib/coercible/coercer/true_class.rb +25 -0
  36. data/lib/coercible/configuration.rb +33 -0
  37. data/lib/coercible/version.rb +3 -0
  38. data/lib/support/options.rb +113 -0
  39. data/lib/support/type_lookup.rb +113 -0
  40. data/spec/integration/configuring_coercers_spec.rb +14 -0
  41. data/spec/shared/unit/configurable.rb +27 -0
  42. data/spec/spec_helper.rb +30 -0
  43. data/spec/unit/coercible/coercer/array/to_set_spec.rb +12 -0
  44. data/spec/unit/coercible/coercer/class_methods/new_spec.rb +13 -0
  45. data/spec/unit/coercible/coercer/date/to_date_spec.rb +10 -0
  46. data/spec/unit/coercible/coercer/date/to_datetime_spec.rb +30 -0
  47. data/spec/unit/coercible/coercer/date/to_string_spec.rb +12 -0
  48. data/spec/unit/coercible/coercer/date/to_time_spec.rb +12 -0
  49. data/spec/unit/coercible/coercer/date_time/to_date_spec.rb +30 -0
  50. data/spec/unit/coercible/coercer/date_time/to_datetime_spec.rb +10 -0
  51. data/spec/unit/coercible/coercer/date_time/to_string_spec.rb +12 -0
  52. data/spec/unit/coercible/coercer/date_time/to_time_spec.rb +30 -0
  53. data/spec/unit/coercible/coercer/decimal/to_decimal_spec.rb +9 -0
  54. data/spec/unit/coercible/coercer/decimal/to_float_spec.rb +12 -0
  55. data/spec/unit/coercible/coercer/decimal/to_integer_spec.rb +12 -0
  56. data/spec/unit/coercible/coercer/decimal/to_string_spec.rb +12 -0
  57. data/spec/unit/coercible/coercer/element_reference_spec.rb +19 -0
  58. data/spec/unit/coercible/coercer/false_class/to_string_spec.rb +12 -0
  59. data/spec/unit/coercible/coercer/float/to_decimal_spec.rb +12 -0
  60. data/spec/unit/coercible/coercer/float/to_float_spec.rb +9 -0
  61. data/spec/unit/coercible/coercer/float/to_integer_spec.rb +12 -0
  62. data/spec/unit/coercible/coercer/float/to_string_spec.rb +12 -0
  63. data/spec/unit/coercible/coercer/hash/to_date_spec.rb +38 -0
  64. data/spec/unit/coercible/coercer/hash/to_datetime_spec.rb +38 -0
  65. data/spec/unit/coercible/coercer/hash/to_time_spec.rb +38 -0
  66. data/spec/unit/coercible/coercer/integer/to_boolean_spec.rb +27 -0
  67. data/spec/unit/coercible/coercer/integer/to_decimal_spec.rb +12 -0
  68. data/spec/unit/coercible/coercer/integer/to_float_spec.rb +12 -0
  69. data/spec/unit/coercible/coercer/integer/to_integer_spec.rb +9 -0
  70. data/spec/unit/coercible/coercer/integer/to_string_spec.rb +12 -0
  71. data/spec/unit/coercible/coercer/integer_spec.rb +11 -0
  72. data/spec/unit/coercible/coercer/numeric/to_decimal_spec.rb +10 -0
  73. data/spec/unit/coercible/coercer/numeric/to_float_spec.rb +10 -0
  74. data/spec/unit/coercible/coercer/numeric/to_integer_spec.rb +10 -0
  75. data/spec/unit/coercible/coercer/numeric/to_string_spec.rb +12 -0
  76. data/spec/unit/coercible/coercer/object/to_array_spec.rb +51 -0
  77. data/spec/unit/coercible/coercer/object/to_hash_spec.rb +22 -0
  78. data/spec/unit/coercible/coercer/object/to_integer_spec.rb +22 -0
  79. data/spec/unit/coercible/coercer/object/to_string_spec.rb +22 -0
  80. data/spec/unit/coercible/coercer/string/to_boolean_spec.rb +31 -0
  81. data/spec/unit/coercible/coercer/string/to_constant_spec.rb +49 -0
  82. data/spec/unit/coercible/coercer/string/to_date_spec.rb +25 -0
  83. data/spec/unit/coercible/coercer/string/to_datetime_spec.rb +52 -0
  84. data/spec/unit/coercible/coercer/string/to_decimal_spec.rb +47 -0
  85. data/spec/unit/coercible/coercer/string/to_float_spec.rb +57 -0
  86. data/spec/unit/coercible/coercer/string/to_integer_spec.rb +68 -0
  87. data/spec/unit/coercible/coercer/string/to_symbol_spec.rb +9 -0
  88. data/spec/unit/coercible/coercer/string/to_time_spec.rb +52 -0
  89. data/spec/unit/coercible/coercer/string_spec.rb +11 -0
  90. data/spec/unit/coercible/coercer/symbol/to_string_spec.rb +12 -0
  91. data/spec/unit/coercible/coercer/time/to_integer_spec.rb +10 -0
  92. data/spec/unit/coercible/coercer/time/to_time_spec.rb +10 -0
  93. data/spec/unit/coercible/coercer/time_coercions/to_date_spec.rb +30 -0
  94. data/spec/unit/coercible/coercer/time_coercions/to_datetime_spec.rb +34 -0
  95. data/spec/unit/coercible/coercer/time_coercions/to_string_spec.rb +19 -0
  96. data/spec/unit/coercible/coercer/time_coercions/to_time_spec.rb +34 -0
  97. data/spec/unit/coercible/coercer/true_class/to_string_spec.rb +12 -0
  98. data/spec/unit/coercible/configuration/class_methods/build_spec.rb +15 -0
  99. 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