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,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,3 @@
1
+ module Coercible
2
+ VERSION = "0.0.1"
3
+ end
@@ -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