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.
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