virtus 0.0.5 → 0.0.6

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 (113) hide show
  1. data/.travis.yml +9 -2
  2. data/.yardopts +1 -0
  3. data/History.md +51 -0
  4. data/{README.markdown → README.md} +63 -7
  5. data/TODO +2 -4
  6. data/VERSION +1 -1
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/roodi.yml +5 -6
  10. data/config/site.reek +3 -3
  11. data/examples/custom_coercion_spec.rb +50 -0
  12. data/examples/default_values_spec.rb +21 -0
  13. data/lib/virtus.rb +21 -6
  14. data/lib/virtus/attribute.rb +113 -253
  15. data/lib/virtus/attribute/array.rb +6 -3
  16. data/lib/virtus/attribute/boolean.rb +9 -28
  17. data/lib/virtus/attribute/date.rb +9 -12
  18. data/lib/virtus/attribute/date_time.rb +10 -12
  19. data/lib/virtus/attribute/decimal.rb +4 -11
  20. data/lib/virtus/attribute/float.rb +4 -11
  21. data/lib/virtus/attribute/hash.rb +5 -3
  22. data/lib/virtus/attribute/integer.rb +4 -11
  23. data/lib/virtus/attribute/numeric.rb +1 -0
  24. data/lib/virtus/attribute/object.rb +1 -0
  25. data/lib/virtus/attribute/string.rb +4 -11
  26. data/lib/virtus/attribute/time.rb +9 -16
  27. data/lib/virtus/class_methods.rb +42 -7
  28. data/lib/virtus/coercion.rb +32 -0
  29. data/lib/virtus/coercion/date.rb +26 -0
  30. data/lib/virtus/coercion/date_time.rb +26 -0
  31. data/lib/virtus/coercion/decimal.rb +40 -0
  32. data/lib/virtus/coercion/false_class.rb +24 -0
  33. data/lib/virtus/coercion/float.rb +24 -0
  34. data/lib/virtus/coercion/hash.rb +82 -0
  35. data/lib/virtus/coercion/integer.rb +60 -0
  36. data/lib/virtus/coercion/numeric.rb +66 -0
  37. data/lib/virtus/coercion/object.rb +25 -0
  38. data/lib/virtus/coercion/string.rb +155 -0
  39. data/lib/virtus/coercion/symbol.rb +24 -0
  40. data/lib/virtus/coercion/time.rb +26 -0
  41. data/lib/virtus/coercion/time_coercions.rb +85 -0
  42. data/lib/virtus/coercion/true_class.rb +24 -0
  43. data/lib/virtus/instance_methods.rb +7 -0
  44. data/lib/virtus/support/descendants_tracker.rb +1 -1
  45. data/lib/virtus/support/options.rb +114 -0
  46. data/lib/virtus/support/type_lookup.rb +95 -0
  47. data/spec/integration/virtus/attributes/attribute/{typecast_spec.rb → set_spec.rb} +7 -7
  48. data/spec/unit/shared/attribute.rb +3 -3
  49. data/spec/unit/shared/attribute/accept_options.rb +0 -18
  50. data/spec/unit/shared/attribute/accepted_options.rb +0 -6
  51. data/spec/unit/shared/attribute/get.rb +32 -17
  52. data/spec/unit/shared/attribute/inspect.rb +7 -0
  53. data/spec/unit/shared/attribute/primitive.rb +15 -0
  54. data/spec/unit/shared/attribute/set.rb +16 -21
  55. data/spec/unit/virtus/attribute/array_spec.rb +18 -3
  56. data/spec/unit/virtus/attribute/boolean_spec.rb +8 -6
  57. data/spec/unit/virtus/attribute/date_spec.rb +8 -6
  58. data/spec/unit/virtus/attribute/date_time_spec.rb +8 -6
  59. data/spec/unit/virtus/attribute/decimal_spec.rb +18 -6
  60. data/spec/unit/virtus/attribute/float_spec.rb +19 -7
  61. data/spec/unit/virtus/attribute/hash_spec.rb +5 -3
  62. data/spec/unit/virtus/attribute/integer_spec.rb +10 -8
  63. data/spec/unit/virtus/attribute/string_spec.rb +10 -8
  64. data/spec/unit/virtus/attribute/time_spec.rb +8 -6
  65. data/spec/unit/virtus/class_methods/attributes_spec.rb +11 -0
  66. data/spec/unit/virtus/coercion/class_name_reference_spec.rb +17 -0
  67. data/spec/unit/virtus/coercion/date/class_methods/to_datetime_spec.rb +30 -0
  68. data/spec/unit/virtus/coercion/date/class_methods/to_string_spec.rb +12 -0
  69. data/spec/unit/virtus/coercion/date/class_methods/to_time_spec.rb +12 -0
  70. data/spec/unit/virtus/coercion/date_time/class_methods/to_date_spec.rb +30 -0
  71. data/spec/unit/virtus/coercion/date_time/class_methods/to_string_spec.rb +12 -0
  72. data/spec/unit/virtus/coercion/date_time/class_methods/to_time_spec.rb +30 -0
  73. data/spec/unit/virtus/coercion/decimal/class_methods/to_float_spec.rb +12 -0
  74. data/spec/unit/virtus/coercion/decimal/class_methods/to_integer_spec.rb +12 -0
  75. data/spec/unit/virtus/coercion/decimal/class_methods/to_string_spec.rb +12 -0
  76. data/spec/unit/virtus/coercion/false_class/class_methods/to_string_spec.rb +12 -0
  77. data/spec/unit/virtus/coercion/float/class_methods/to_decimal_spec.rb +12 -0
  78. data/spec/unit/virtus/coercion/float/class_methods/to_integer_spec.rb +12 -0
  79. data/spec/unit/virtus/coercion/float/class_methods/to_string_spec.rb +12 -0
  80. data/spec/unit/virtus/coercion/hash/class_methods/to_array_spec.rb +12 -0
  81. data/spec/unit/virtus/coercion/hash/class_methods/to_date_spec.rb +31 -0
  82. data/spec/unit/virtus/coercion/hash/class_methods/to_datetime_spec.rb +31 -0
  83. data/spec/unit/virtus/coercion/hash/class_methods/to_time_spec.rb +31 -0
  84. data/spec/unit/virtus/coercion/integer/class_methods/to_boolean_spec.rb +25 -0
  85. data/spec/unit/virtus/coercion/integer/class_methods/to_decimal_spec.rb +12 -0
  86. data/spec/unit/virtus/coercion/integer/class_methods/to_float_spec.rb +12 -0
  87. data/spec/unit/virtus/coercion/integer/class_methods/to_string_spec.rb +12 -0
  88. data/spec/unit/virtus/coercion/object/class_methods/method_missing_spec.rb +33 -0
  89. data/spec/unit/virtus/coercion/string/class_methods/to_boolean_spec.rb +29 -0
  90. data/spec/unit/virtus/coercion/string/class_methods/to_date_spec.rb +23 -0
  91. data/spec/unit/virtus/coercion/string/class_methods/to_datetime_spec.rb +50 -0
  92. data/spec/unit/virtus/coercion/string/class_methods/to_decimal_spec.rb +23 -0
  93. data/spec/unit/virtus/coercion/string/class_methods/to_float_spec.rb +21 -0
  94. data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +21 -0
  95. data/spec/unit/virtus/coercion/string/class_methods/to_time_spec.rb +50 -0
  96. data/spec/unit/virtus/coercion/symbol/class_methods/to_string_spec.rb +12 -0
  97. data/spec/unit/virtus/coercion/true_class/class_methods/to_string_spec.rb +12 -0
  98. data/spec/unit/virtus/instance_methods/attributes_spec.rb +7 -0
  99. data/spec/unit/virtus/options/accept_options_spec.rb +38 -0
  100. data/spec/unit/virtus/options/accepted_options_spec.rb +21 -0
  101. data/spec/unit/virtus/options/options_spec.rb +11 -0
  102. data/spec/unit/virtus/type_lookup/determine_type_spec.rb +68 -0
  103. data/spec/unit/virtus/type_lookup/primitive_spec.rb +9 -0
  104. data/virtus.gemspec +70 -17
  105. metadata +78 -27
  106. data/History.txt +0 -38
  107. data/lib/virtus/typecast/boolean.rb +0 -29
  108. data/lib/virtus/typecast/numeric.rb +0 -87
  109. data/lib/virtus/typecast/string.rb +0 -24
  110. data/lib/virtus/typecast/time.rb +0 -192
  111. data/spec/unit/shared/attribute/complex.rb +0 -15
  112. data/spec/unit/shared/attribute/options.rb +0 -7
  113. data/spec/unit/virtus/attribute/attribute_spec.rb +0 -12
@@ -0,0 +1,24 @@
1
+ module Virtus
2
+ class Coercion
3
+
4
+ # Coerce Symbol values
5
+ class Symbol < Object
6
+ primitive ::Symbol
7
+
8
+ # Coerce given value to String
9
+ #
10
+ # @example
11
+ # Virtus::Coercion::Symbol.to_string(:name) # => "name"
12
+ #
13
+ # @param [Symbol] value
14
+ #
15
+ # @return [String]
16
+ #
17
+ # @api public
18
+ def self.to_string(value)
19
+ value.to_s
20
+ end
21
+
22
+ end # class Symbol
23
+ end # class Coercion
24
+ end # module Virtus
@@ -0,0 +1,26 @@
1
+ module Virtus
2
+ class Coercion
3
+
4
+ # Coerce Time values
5
+ class Time < Object
6
+ extend TimeCoercions
7
+
8
+ primitive ::Time
9
+
10
+ # Passthrough the value
11
+ #
12
+ # @example
13
+ # Virtus::Coercion::DateTime.to_time(time) # => Time object
14
+ #
15
+ # @param [DateTime] value
16
+ #
17
+ # @return [Date]
18
+ #
19
+ # @api public
20
+ def self.to_time(value)
21
+ value
22
+ end
23
+
24
+ end # class Time
25
+ end # class Coercion
26
+ end # module Virtus
@@ -0,0 +1,85 @@
1
+ module Virtus
2
+ class Coercion
3
+
4
+ # Common time coercion methods
5
+ module TimeCoercions
6
+
7
+ # Coerce given value to String
8
+ #
9
+ # @example
10
+ # Virtus::Coercion::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
+ # Virtus::Coercion::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
+ # Virtus::Coercion::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
+ # Virtus::Coercion::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
+ if value.respond_to?(method)
77
+ value.send(method)
78
+ else
79
+ String.send(method, to_string(value))
80
+ end
81
+ end
82
+
83
+ end # module TimeCoercions
84
+ end # class Coercion
85
+ end # module Virtus
@@ -0,0 +1,24 @@
1
+ module Virtus
2
+ class Coercion
3
+
4
+ # Coerce true values
5
+ class TrueClass < Object
6
+ primitive ::TrueClass
7
+
8
+ # Coerce given value to String
9
+ #
10
+ # @example
11
+ # Virtus::Coercion::TrueClass.to_string(true) # => "true"
12
+ #
13
+ # @param [TrueClass] value
14
+ #
15
+ # @return [String]
16
+ #
17
+ # @api public
18
+ def self.to_string(value)
19
+ value.to_s
20
+ end
21
+
22
+ end # class TrueClass
23
+ end # class Coercion
24
+ end # module Virtus
@@ -117,6 +117,13 @@ module Virtus
117
117
  end
118
118
  end
119
119
 
120
+ # @see Virtus::InstanceMethods#attributes
121
+ #
122
+ # @api public
123
+ def to_hash
124
+ attributes
125
+ end
126
+
120
127
  private
121
128
 
122
129
  # Returns a value of the attribute with the given name
@@ -32,7 +32,7 @@ module Virtus
32
32
  #
33
33
  # @param [Class] descendant
34
34
  #
35
- # @return [self]
35
+ # @return [undefined]
36
36
  #
37
37
  # @api private
38
38
  def inherited(descendant)
@@ -0,0 +1,114 @@
1
+ module Virtus
2
+
3
+ # A module that adds class and instance level options
4
+ module Options
5
+
6
+ # Returns default options hash for a given attribute class
7
+ #
8
+ # @example
9
+ # Virtus::Attribute::String.options
10
+ # # => {:primitive => String}
11
+ #
12
+ # @return [Hash]
13
+ # a hash of default option values
14
+ #
15
+ # @api public
16
+ def options
17
+ options = {}
18
+ accepted_options.each do |option_name|
19
+ option_value = send(option_name)
20
+ options[option_name] = option_value unless option_value.nil?
21
+ end
22
+ options
23
+ end
24
+
25
+ # Returns an array of valid options
26
+ #
27
+ # @example
28
+ # Virtus::Attribute::String.accepted_options
29
+ # # => [:primitive, :accessor, :reader, :writer]
30
+ #
31
+ # @return [Array]
32
+ # the array of valid option names
33
+ #
34
+ # @api public
35
+ def accepted_options
36
+ @accepted_options ||= []
37
+ end
38
+
39
+ # Defines which options are valid for a given attribute class
40
+ #
41
+ # @example
42
+ # class MyAttribute < Virtus::Attribute::Object
43
+ # accept_options :foo, :bar
44
+ # end
45
+ #
46
+ # @return [Array]
47
+ # All accepted options
48
+ #
49
+ # @api public
50
+ def accept_options(*new_options)
51
+ add_accepted_options(new_options)
52
+ new_options.each { |option| define_option_method(option) }
53
+ descendants.each { |descendant| descendant.add_accepted_options(new_options) }
54
+ self
55
+ end
56
+
57
+ protected
58
+
59
+ # Adds a reader/writer method for the give option name
60
+ #
61
+ # @return [undefined]
62
+ #
63
+ # @api private
64
+ def define_option_method(option)
65
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
66
+ def self.#{option}(value = Undefined) # def self.primitive(value = Undefined)
67
+ return @#{option} if value.equal?(Undefined) # return @primitive if value.equal?(Undefined)
68
+ @#{option} = value # @primitive = value
69
+ end # end
70
+ RUBY
71
+ end
72
+
73
+ # Sets default options
74
+ #
75
+ # @param [#to_hash] new_options
76
+ # options to be set
77
+ #
78
+ # @return [self]
79
+ #
80
+ # @api private
81
+ def set_options(new_options)
82
+ new_options.to_hash.each { |pair| send(*pair) }
83
+ self
84
+ end
85
+
86
+ # Adds new options that an attribute class can accept
87
+ #
88
+ # @param [#to_ary] new_options
89
+ # new options to be added
90
+ #
91
+ # @return [self]
92
+ #
93
+ # @api private
94
+ def add_accepted_options(new_options)
95
+ accepted_options.concat(new_options.to_ary)
96
+ self
97
+ end
98
+
99
+ private
100
+
101
+ # Adds descendant to descendants array and inherits default options
102
+ #
103
+ # @param [Class] descendant
104
+ #
105
+ # @return [undefined]
106
+ #
107
+ # @api private
108
+ def inherited(descendant)
109
+ super
110
+ descendant.add_accepted_options(accepted_options).set_options(options)
111
+ end
112
+
113
+ end # module Options
114
+ end # module Virtus
@@ -0,0 +1,95 @@
1
+ module Virtus
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
+ # Returns a descendant based on a name or class
9
+ #
10
+ # @example
11
+ # MyClass.determine_type('String') # => MyClass::String
12
+ #
13
+ # @param [Class, #to_s] class_or_name
14
+ # name of a class or a class itself
15
+ #
16
+ # @return [Class]
17
+ # a descendant
18
+ #
19
+ # @return [nil]
20
+ # nil if the type cannot be determined by the class_or_name
21
+ #
22
+ # @api public
23
+ def determine_type(class_or_name)
24
+ case class_or_name
25
+ when singleton_class
26
+ determine_type_from_descendant(class_or_name)
27
+ when Class
28
+ determine_type_from_primitive(class_or_name)
29
+ else
30
+ determine_type_from_string(class_or_name.to_s)
31
+ end
32
+ end
33
+
34
+ # Return the default primitive supported
35
+ #
36
+ # @return [Class]
37
+ #
38
+ # @api private
39
+ def primitive
40
+ raise NotImplementedError, "#{self}.primitive must be implemented"
41
+ end
42
+
43
+ private
44
+
45
+ # Return the class given a descendant
46
+ #
47
+ # @param [Class] descendant
48
+ #
49
+ # @return [Class]
50
+ #
51
+ # @api private
52
+ def determine_type_from_descendant(descendant)
53
+ descendant if descendant < self
54
+ end
55
+
56
+ # Return the class given a primitive
57
+ #
58
+ # @param [Class] primitive
59
+ #
60
+ # @return [Class]
61
+ #
62
+ # @return [nil]
63
+ # nil if the type cannot be determined by the primitive
64
+ #
65
+ # @api private
66
+ def determine_type_from_primitive(primitive)
67
+ descendants.detect { |descendant| primitive <= descendant.primitive }
68
+ end
69
+
70
+ # Return the class given a string
71
+ #
72
+ # @param [String] string
73
+ #
74
+ # @return [Class]
75
+ #
76
+ # @return [nil]
77
+ # nil if the type cannot be determined by the string
78
+ #
79
+ # @api private
80
+ def determine_type_from_string(string)
81
+ if string =~ TYPE_FORMAT && const_defined?(string, false)
82
+ const_get(string, false)
83
+ end
84
+ end
85
+
86
+ if RUBY_VERSION < '1.9'
87
+ def determine_type_from_string(string)
88
+ if string =~ TYPE_FORMAT && const_defined?(string)
89
+ const_get(string)
90
+ end
91
+ end
92
+ end
93
+
94
+ end # module TypeLookup
95
+ end # module Virtus
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Virtus::Attribute, '#typecast' do
3
+ describe Virtus::Attribute, '#set' do
4
4
  let(:attribute_class) do
5
5
  Class.new(Virtus::Attribute::Integer) do
6
- def typecast(value)
7
- super + 1
6
+ def set(instance, value)
7
+ super(instance, coerce(value) + 1) unless value.nil?
8
8
  end
9
9
  end
10
10
  end
@@ -23,14 +23,14 @@ describe Virtus::Attribute, '#typecast' do
23
23
 
24
24
  context 'when overridden' do
25
25
  let(:input_value) { 1 }
26
- let(:output_value) { 2 }
26
+ let(:output_value) { 2 }
27
27
 
28
28
  before do
29
29
  object.count = input_value
30
30
  end
31
31
 
32
- it "peforms custom typecasting" do
33
- object.count.should eql(output_value)
34
- end
32
+ subject { object }
33
+
34
+ its(:count) { should eql(output_value) }
35
35
  end
36
36
  end
@@ -1,8 +1,8 @@
1
- shared_examples_for "Attribute" do
2
- it_behaves_like 'Attribute.options'
1
+ shared_examples_for 'Attribute' do
3
2
  it_behaves_like 'Attribute.accept_options'
4
3
  it_behaves_like 'Attribute.accepted_options'
4
+ it_behaves_like 'Attribute.primitive?'
5
+ it_behaves_like 'Attribute#inspect'
5
6
  it_behaves_like 'Attribute#set'
6
7
  it_behaves_like 'Attribute#get'
7
- it_behaves_like 'Attribute#complex?'
8
8
  end