virtus 0.0.10 → 0.1.0

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 (154) hide show
  1. data/.gitignore +33 -7
  2. data/.travis.yml +4 -74
  3. data/Changelog.md +11 -2
  4. data/Gemfile +10 -9
  5. data/README.md +85 -1
  6. data/TODO +18 -0
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/roodi.yml +3 -3
  10. data/config/site.reek +8 -3
  11. data/lib/virtus.rb +5 -0
  12. data/lib/virtus/attribute.rb +137 -30
  13. data/lib/virtus/attribute/array.rb +17 -1
  14. data/lib/virtus/attribute/boolean.rb +8 -13
  15. data/lib/virtus/attribute/collection.rb +96 -0
  16. data/lib/virtus/attribute/default_value.rb +11 -7
  17. data/lib/virtus/attribute/embedded_value.rb +70 -0
  18. data/lib/virtus/attribute/set.rb +25 -0
  19. data/lib/virtus/attribute_set.rb +18 -16
  20. data/lib/virtus/attributes_accessor.rb +66 -0
  21. data/lib/virtus/class_methods.rb +50 -28
  22. data/lib/virtus/coercion/array.rb +23 -0
  23. data/lib/virtus/coercion/string.rb +10 -4
  24. data/lib/virtus/instance_methods.rb +38 -31
  25. data/lib/virtus/support/options.rb +6 -8
  26. data/lib/virtus/support/type_lookup.rb +4 -11
  27. data/lib/virtus/version.rb +1 -1
  28. data/spec/integration/collection_member_coercion_spec.rb +75 -0
  29. data/spec/integration/custom_attributes_spec.rb +49 -0
  30. data/spec/integration/default_values_spec.rb +32 -0
  31. data/spec/integration/defining_attributes_spec.rb +79 -0
  32. data/spec/integration/embedded_value_spec.rb +50 -0
  33. data/spec/integration/overriding_virtus_spec.rb +46 -0
  34. data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
  35. data/spec/rcov.opts +1 -0
  36. data/spec/shared/constants_helpers.rb +9 -0
  37. data/spec/shared/options_class_method.rb +19 -0
  38. data/spec/spec_helper.rb +20 -7
  39. data/spec/unit/virtus/attribute/array/coerce_spec.rb +13 -0
  40. data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +85 -0
  41. data/spec/unit/virtus/attribute/boolean/define_reader_method_spec.rb +15 -0
  42. data/spec/unit/virtus/attribute/boolean/value_coerced_spec.rb +97 -0
  43. data/spec/unit/virtus/attribute/boolean_spec.rb +2 -81
  44. data/spec/unit/virtus/attribute/class/coerce_spec.rb +13 -0
  45. data/spec/unit/virtus/attribute/class_methods/accessor_spec.rb +12 -0
  46. data/spec/unit/virtus/attribute/class_methods/build_spec.rb +37 -0
  47. data/spec/unit/virtus/attribute/class_methods/coercion_method_spec.rb +9 -0
  48. data/spec/unit/virtus/attribute/class_methods/default_spec.rb +9 -0
  49. data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +31 -1
  50. data/spec/unit/virtus/attribute/class_methods/merge_options_spec.rb +11 -0
  51. data/spec/unit/virtus/attribute/class_methods/primitive_spec.rb +9 -0
  52. data/spec/unit/virtus/attribute/class_methods/reader_spec.rb +9 -0
  53. data/spec/unit/virtus/attribute/class_methods/writer_spec.rb +9 -0
  54. data/spec/unit/virtus/attribute/coerce_spec.rb +30 -0
  55. data/spec/unit/virtus/attribute/coercion_method_spec.rb +12 -0
  56. data/spec/unit/virtus/attribute/collection/class_methods/merge_options_spec.rb +40 -0
  57. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +26 -0
  58. data/spec/unit/virtus/attribute/date/coerce_spec.rb +47 -0
  59. data/spec/unit/virtus/attribute/date/value_coerced_spec.rb +46 -0
  60. data/spec/unit/virtus/attribute/date_time/coerce_spec.rb +68 -0
  61. data/spec/unit/virtus/attribute/decimal/coerce_spec.rb +117 -0
  62. data/spec/unit/virtus/attribute/default_spec.rb +32 -0
  63. data/spec/unit/virtus/attribute/default_value/attribute_spec.rb +11 -0
  64. data/spec/unit/virtus/attribute/default_value/class_methods/new_spec.rb +4 -2
  65. data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +51 -0
  66. data/spec/unit/virtus/attribute/default_value/instance_methods/evaluate_spec.rb +9 -6
  67. data/spec/unit/virtus/attribute/default_value/value_spec.rb +11 -0
  68. data/spec/unit/virtus/attribute/define_accessor_methods_spec.rb +26 -0
  69. data/spec/unit/virtus/attribute/define_reader_method_spec.rb +24 -0
  70. data/spec/unit/virtus/attribute/define_writer_method_spec.rb +24 -0
  71. data/spec/unit/virtus/attribute/embedded_value/class_methods/merge_options_spec.rb +17 -0
  72. data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +50 -0
  73. data/spec/unit/virtus/attribute/float/coerce_spec.rb +117 -0
  74. data/spec/unit/virtus/attribute/get_spec.rb +80 -0
  75. data/spec/unit/virtus/attribute/inspect_spec.rb +27 -0
  76. data/spec/unit/virtus/attribute/instance_variable_name_spec.rb +12 -0
  77. data/spec/unit/virtus/attribute/integer/coerce_spec.rb +105 -0
  78. data/spec/unit/virtus/attribute/name_spec.rb +12 -0
  79. data/spec/unit/virtus/attribute/numeric/class_methods/descendants_spec.rb +1 -1
  80. data/spec/unit/virtus/attribute/numeric/class_methods/max_spec.rb +9 -0
  81. data/spec/unit/virtus/attribute/numeric/class_methods/min_spec.rb +9 -0
  82. data/spec/unit/virtus/attribute/object/class_methods/descendants_spec.rb +9 -7
  83. data/spec/unit/virtus/attribute/options_spec.rb +14 -0
  84. data/spec/unit/virtus/attribute/public_reader_spec.rb +24 -0
  85. data/spec/unit/virtus/attribute/public_writer_spec.rb +24 -0
  86. data/spec/unit/virtus/attribute/reader_visibility_spec.rb +24 -0
  87. data/spec/unit/virtus/attribute/set/coerce_spec.rb +13 -0
  88. data/spec/unit/virtus/attribute/set_spec.rb +49 -0
  89. data/spec/unit/virtus/attribute/string/coerce_spec.rb +11 -0
  90. data/spec/unit/virtus/attribute/time/coerce_spec.rb +67 -0
  91. data/spec/unit/virtus/attribute/value_coerced_spec.rb +19 -0
  92. data/spec/unit/virtus/attribute/writer_visibility_spec.rb +24 -0
  93. data/spec/unit/virtus/attribute_set/append_spec.rb +12 -0
  94. data/spec/unit/virtus/attribute_set/element_reference_spec.rb +4 -0
  95. data/spec/unit/virtus/attribute_set/element_set_spec.rb +29 -9
  96. data/spec/unit/virtus/attributes_accessor/inspect_spec.rb +9 -0
  97. data/spec/unit/virtus/class_methods/attribute_spec.rb +23 -5
  98. data/spec/unit/virtus/class_methods/attributes_spec.rb +3 -5
  99. data/spec/unit/virtus/class_methods/const_missing_spec.rb +27 -0
  100. data/spec/unit/virtus/class_methods/inherited_spec.rb +21 -0
  101. data/spec/unit/virtus/coercion/array/to_set_spec.rb +12 -0
  102. data/spec/unit/virtus/coercion/date/class_methods/to_date_spec.rb +10 -0
  103. data/spec/unit/virtus/coercion/date_time/class_methods/to_datetime_spec.rb +10 -0
  104. data/spec/unit/virtus/coercion/hash/class_methods/to_date_spec.rb +10 -3
  105. data/spec/unit/virtus/coercion/hash/class_methods/to_datetime_spec.rb +10 -3
  106. data/spec/unit/virtus/coercion/hash/class_methods/to_time_spec.rb +10 -3
  107. data/spec/unit/virtus/coercion/object/class_methods/method_missing_spec.rb +8 -8
  108. data/spec/unit/virtus/coercion/string/class_methods/to_boolean_spec.rb +2 -2
  109. data/spec/unit/virtus/coercion/string/class_methods/to_constant_spec.rb +1 -1
  110. data/spec/unit/virtus/coercion/string/class_methods/to_date_spec.rb +1 -1
  111. data/spec/unit/virtus/coercion/string/class_methods/to_datetime_spec.rb +13 -13
  112. data/spec/unit/virtus/coercion/string/class_methods/to_decimal_spec.rb +25 -1
  113. data/spec/unit/virtus/coercion/string/class_methods/to_float_spec.rb +25 -1
  114. data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +30 -1
  115. data/spec/unit/virtus/coercion/string/class_methods/to_time_spec.rb +13 -13
  116. data/spec/unit/virtus/coercion/time/class_methods/to_time_spec.rb +10 -0
  117. data/spec/unit/virtus/coercion/true_class/class_methods/to_string_spec.rb +1 -1
  118. data/spec/unit/virtus/instance_methods/attributes_spec.rb +77 -20
  119. data/spec/unit/virtus/instance_methods/element_reference_spec.rb +1 -1
  120. data/spec/unit/virtus/instance_methods/element_set_spec.rb +2 -2
  121. data/spec/unit/virtus/instance_methods/to_hash_spec.rb +23 -0
  122. data/spec/unit/virtus/options/accept_options_spec.rb +10 -11
  123. data/spec/unit/virtus/options/accepted_options_spec.rb +1 -1
  124. data/spec/unit/virtus/options/options_spec.rb +27 -4
  125. data/tasks/metrics/ci.rake +2 -1
  126. data/tasks/metrics/heckle.rake +207 -0
  127. data/tasks/spec.rake +14 -7
  128. data/virtus.gemspec +1 -1
  129. metadata +111 -97
  130. data/VERSION +0 -1
  131. data/examples/custom_coercion_spec.rb +0 -50
  132. data/examples/default_values_spec.rb +0 -21
  133. data/examples/override_attribute_methods_spec.rb +0 -40
  134. data/spec/integration/virtus/attributes/attribute/set_spec.rb +0 -36
  135. data/spec/integration/virtus/class_methods/attribute_spec.rb +0 -82
  136. data/spec/integration/virtus/class_methods/attributes_spec.rb +0 -22
  137. data/spec/integration/virtus/class_methods/const_missing_spec.rb +0 -44
  138. data/spec/unit/shared/attribute.rb +0 -7
  139. data/spec/unit/shared/attribute/accept_options.rb +0 -37
  140. data/spec/unit/shared/attribute/accepted_options.rb +0 -5
  141. data/spec/unit/shared/attribute/get.rb +0 -44
  142. data/spec/unit/shared/attribute/inspect.rb +0 -7
  143. data/spec/unit/shared/attribute/set.rb +0 -37
  144. data/spec/unit/virtus/attribute/array_spec.rb +0 -24
  145. data/spec/unit/virtus/attribute/class_spec.rb +0 -24
  146. data/spec/unit/virtus/attribute/date_spec.rb +0 -59
  147. data/spec/unit/virtus/attribute/date_time_spec.rb +0 -87
  148. data/spec/unit/virtus/attribute/decimal_spec.rb +0 -109
  149. data/spec/unit/virtus/attribute/float_spec.rb +0 -109
  150. data/spec/unit/virtus/attribute/hash_spec.rb +0 -11
  151. data/spec/unit/virtus/attribute/integer_spec.rb +0 -99
  152. data/spec/unit/virtus/attribute/string_spec.rb +0 -21
  153. data/spec/unit/virtus/attribute/time_spec.rb +0 -82
  154. data/spec/unit/virtus/class_methods/new_spec.rb +0 -41
@@ -12,8 +12,10 @@ module Virtus
12
12
  # @api private
13
13
  def self.extended(descendant)
14
14
  super
15
- descendant.extend(DescendantsTracker)
16
- descendant.const_set(:AttributeMethods, Module.new)
15
+ descendant.module_eval do
16
+ extend DescendantsTracker
17
+ virtus_setup_attributes_accessor_module
18
+ end
17
19
  end
18
20
 
19
21
  private_class_method :extended
@@ -41,10 +43,12 @@ module Virtus
41
43
  #
42
44
  # @return [self]
43
45
  #
46
+ # @see Attribute.build
47
+ #
44
48
  # @api public
45
- def attribute(name, type, options = {})
46
- attribute = Attribute.determine_type(type).new(name, options)
47
- virtus_define_attribute_methods(attribute)
49
+ def attribute(*args)
50
+ attribute = Attribute.build(*args)
51
+ attribute.define_accessor_methods(virtus_attributes_accessor_module)
48
52
  virtus_add_attribute(attribute)
49
53
  self
50
54
  end
@@ -67,44 +71,62 @@ module Virtus
67
71
  #
68
72
  # @api public
69
73
  def attributes
70
- @attributes ||= begin
71
- superclass = self.superclass
72
- parent = superclass.attributes if superclass.respond_to?(:attributes)
73
- AttributeSet.new(parent)
74
- end
74
+ return @attributes if defined?(@attributes)
75
+ superclass = self.superclass
76
+ method = __method__
77
+ parent = superclass.send(method) if superclass.respond_to?(method)
78
+ @attributes = AttributeSet.new(parent)
75
79
  end
76
80
 
77
- private
81
+ protected
78
82
 
79
- # Hooks into const missing process to determine types of attributes
80
- #
81
- # It is used when an attribute is defined and a global class like String
82
- # or Integer is provided as the type which needs to be mapped to
83
- # Virtus::Attribute::String and Virtus::Attribute::Integer
84
- #
85
- # @param [String] name
83
+ # Set up the anonymous module which will host Attribute accessor methods
86
84
  #
87
- # @return [Class]
85
+ # @return [self]
88
86
  #
89
87
  # @api private
90
- def const_missing(name)
91
- Attribute.determine_type(name) || super
88
+ def virtus_setup_attributes_accessor_module
89
+ @virtus_attributes_accessor_module = AttributesAccessor.new(inspect)
90
+ include virtus_attributes_accessor_module
91
+ self
92
92
  end
93
93
 
94
- # Define the attribute reader and writer methods in the class
94
+ private
95
+
96
+ # Setup descendants' own Attribute-accessor-method-hosting modules
95
97
  #
96
- # @param [Attribute]
98
+ # Descendants inherit Attribute accessor methods via Ruby's inheritance
99
+ # mechanism: Attribute accessor methods are defined in a module included
100
+ # in a superclass. Attributes defined on descendants add methods to the
101
+ # descendant's Attributes accessor module, leaving the superclass's method
102
+ # table unaffected.
103
+ #
104
+ # @param [Class] descendant
97
105
  #
98
106
  # @return [undefined]
99
107
  #
100
108
  # @api private
101
- def virtus_define_attribute_methods(attribute)
102
- module_with_methods = self::AttributeMethods
109
+ def inherited(descendant)
110
+ super
111
+ descendant.virtus_setup_attributes_accessor_module
112
+ end
103
113
 
104
- attribute.define_reader_method(module_with_methods)
105
- attribute.define_writer_method(module_with_methods)
114
+ # Holds the anonymous module which hosts this class's Attribute accessors
115
+ #
116
+ # @return [Module]
117
+ #
118
+ # @api private
119
+ attr_reader :virtus_attributes_accessor_module
106
120
 
107
- include module_with_methods
121
+ # Hooks into const missing process to determine types of attributes
122
+ #
123
+ # @param [String] name
124
+ #
125
+ # @return [Class]
126
+ #
127
+ # @api private
128
+ def const_missing(name)
129
+ Attribute.determine_type(name) || super
108
130
  end
109
131
 
110
132
  # Add the attribute to the class' and descendants' attributes
@@ -0,0 +1,23 @@
1
+ module Virtus
2
+ class Coercion
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 self.to_set(value)
18
+ value.to_set
19
+ end
20
+
21
+ end # class Array
22
+ end # class Coercion
23
+ end # module Virtus
@@ -5,11 +5,14 @@ module Virtus
5
5
  class String < Object
6
6
  primitive ::String
7
7
 
8
- TRUE_VALUES = %w[ 1 t true ].freeze
9
- FALSE_VALUES = %w[ 0 f false ].freeze
8
+ TRUE_VALUES = %w[ 1 on t true y yes ].freeze
9
+ FALSE_VALUES = %w[ 0 off f false n no ].freeze
10
10
  BOOLEAN_MAP = ::Hash[ TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
11
11
 
12
- NUMERIC_REGEXP = /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/.freeze
12
+ INTEGER_REGEXP = /[-+]?(?:0|[1-9]\d*)/.freeze
13
+ EXPONENT_REGEXP = /(?:[eE][-+]?\d+)/.freeze
14
+ FRACTIONAL_REGEXP = /(?:\.\d+#{EXPONENT_REGEXP}?)/.freeze
15
+ NUMERIC_REGEXP = /\A(#{INTEGER_REGEXP}#{FRACTIONAL_REGEXP}?|#{FRACTIONAL_REGEXP})\z/.freeze
13
16
 
14
17
  # Coerce give value to a constant
15
18
  #
@@ -96,7 +99,10 @@ module Virtus
96
99
  #
97
100
  # @api public
98
101
  def self.to_integer(value)
99
- to_numeric(value, :to_i)
102
+ # coerce to a Float first to evaluate scientific notation (if any)
103
+ # that may change the integer part, then convert to an integer
104
+ coerced = to_float(value)
105
+ ::Float === coerced ? coerced.to_i : coerced
100
106
  end
101
107
 
102
108
  # Coerce value to float
@@ -11,8 +11,8 @@ module Virtus
11
11
  # @return [undefined]
12
12
  #
13
13
  # @api private
14
- def initialize(attributes = {})
15
- self.attributes = attributes
14
+ def initialize(attribute_values = {})
15
+ self.attributes = attribute_values
16
16
  end
17
17
 
18
18
  # Returns a value of the attribute with the given name
@@ -35,7 +35,7 @@ module Virtus
35
35
  #
36
36
  # @api public
37
37
  def [](name)
38
- attribute_get(name)
38
+ __send__(name)
39
39
  end
40
40
 
41
41
  # Sets a value of the attribute with the given name
@@ -62,7 +62,7 @@ module Virtus
62
62
  #
63
63
  # @api public
64
64
  def []=(name, value)
65
- attribute_set(name, value)
65
+ __send__("#{name}=", value)
66
66
  end
67
67
 
68
68
  # Returns a hash of all publicly accessible attributes
@@ -82,17 +82,17 @@ module Virtus
82
82
  #
83
83
  # @api public
84
84
  def attributes
85
- attributes = {}
86
-
87
- self.class.attributes.each do |attribute|
85
+ self.class.attributes.each_with_object({}) do |attribute, attributes|
88
86
  name = attribute.name
89
- attributes[name] = attribute_get(name) if respond_to?(name)
87
+ attributes[name] = self[name] if attribute.public_reader?
90
88
  end
91
-
92
- attributes
93
89
  end
94
90
 
95
- # Mass-assign of attribute values
91
+ # Mass-assign attribute values
92
+ #
93
+ # Keys in the +attribute_values+ param can be symbols or strings.
94
+ # Only non-private referenced Attribute writer methods will be called.
95
+ # Non-attribute setter methods on the receiver will not be called.
96
96
  #
97
97
  # @example
98
98
  # class User
@@ -103,18 +103,20 @@ module Virtus
103
103
  # end
104
104
  #
105
105
  # user = User.new
106
- # user.attributes = { :name => 'John', :age => 28 }
106
+ # user.attributes = { :name => 'John', 'age' => 28 }
107
107
  #
108
- # @param [#to_hash] attributes
109
- # a hash of attribute values to be set on an object
108
+ # @param [#to_hash] attribute_values
109
+ # a hash of attribute names and values to set on the receiver
110
110
  #
111
111
  # @return [Hash]
112
112
  #
113
113
  # @api public
114
- def attributes=(attributes)
115
- attributes.to_hash.each do |name, value|
116
- attribute_set(name, value) if respond_to?("#{name}=")
117
- end
114
+ def attributes=(attribute_values)
115
+ attributes = self.class.attributes
116
+ set_attributes(attribute_values.select { |name,|
117
+ attribute = attributes[name]
118
+ attribute && attribute.public_writer?
119
+ })
118
120
  end
119
121
 
120
122
  # Returns a hash of all publicly accessible attributes
@@ -139,26 +141,31 @@ module Virtus
139
141
 
140
142
  private
141
143
 
142
- # Returns a value of the attribute with the given name
144
+ # Mass-assign attribute values
143
145
  #
144
- # @see Virtus::InstanceMethods#[]
146
+ # Keys in the +attribute_values+ param can be symbols or strings.
147
+ # All referenced Attribute writer methods *will* be called.
148
+ # Non-attribute setter methods on the receiver *will* be called.
145
149
  #
146
- # @return [Object]
150
+ # @example
151
+ # class User
152
+ # include Virtus
147
153
  #
148
- # @api private
149
- def attribute_get(name)
150
- __send__(name)
151
- end
152
-
153
- # Sets a value of the attribute with the given name
154
+ # attribute :name, String
155
+ # attribute :age, Integer
156
+ # end
154
157
  #
155
- # @see Virtus::InstanceMethods#[]=
158
+ # user = User.new
159
+ # user.attributes = { :name => 'John', 'age' => 28 }
156
160
  #
157
- # @return [Object]
161
+ # @param [#to_hash] attribute_values
162
+ # a hash of attribute names and values to set on the receiver
163
+ #
164
+ # @return [Hash]
158
165
  #
159
166
  # @api private
160
- def attribute_set(name, value)
161
- __send__("#{name}=", value)
167
+ def set_attributes(attribute_values)
168
+ attribute_values.each { |name, value| self[name] = value }
162
169
  end
163
170
 
164
171
  end # module InstanceMethods
@@ -14,12 +14,10 @@ module Virtus
14
14
  #
15
15
  # @api public
16
16
  def options
17
- options = {}
18
- accepted_options.each do |option_name|
17
+ accepted_options.each_with_object({}) do |option_name, options|
19
18
  option_value = send(option_name)
20
19
  options[option_name] = option_value unless option_value.nil?
21
20
  end
22
- options
23
21
  end
24
22
 
25
23
  # Returns an array of valid options
@@ -43,8 +41,7 @@ module Virtus
43
41
  # accept_options :foo, :bar
44
42
  # end
45
43
  #
46
- # @return [Array]
47
- # All accepted options
44
+ # @return [self]
48
45
  #
49
46
  # @api public
50
47
  def accept_options(*new_options)
@@ -66,20 +63,21 @@ module Virtus
66
63
  def self.#{option}(value = Undefined) # def self.primitive(value = Undefined)
67
64
  return @#{option} if value.equal?(Undefined) # return @primitive if value.equal?(Undefined)
68
65
  @#{option} = value # @primitive = value
66
+ self # self
69
67
  end # end
70
68
  RUBY
71
69
  end
72
70
 
73
71
  # Sets default options
74
72
  #
75
- # @param [#to_hash] new_options
73
+ # @param [#each] new_options
76
74
  # options to be set
77
75
  #
78
76
  # @return [self]
79
77
  #
80
78
  # @api private
81
79
  def set_options(new_options)
82
- new_options.to_hash.each { |pair| send(*pair) }
80
+ new_options.each { |pair| send(*pair) }
83
81
  self
84
82
  end
85
83
 
@@ -92,7 +90,7 @@ module Virtus
92
90
  #
93
91
  # @api private
94
92
  def add_accepted_options(new_options)
95
- accepted_options.concat(new_options.to_ary)
93
+ accepted_options.concat(new_options)
96
94
  self
97
95
  end
98
96
 
@@ -3,7 +3,8 @@ module Virtus
3
3
  # A module that adds type lookup to a class
4
4
  module TypeLookup
5
5
 
6
- TYPE_FORMAT = /\A(?:[A-Z]\w*)\z/.freeze
6
+ TYPE_FORMAT = /\A[A-Z]\w*\z/.freeze
7
+ EXTRA_CONST_ARGS = RUBY_VERSION < '1.9' || RUBY_ENGINE == 'rbx' ? [] : [ false ]
7
8
 
8
9
  # Returns a descendant based on a name or class
9
10
  #
@@ -84,16 +85,8 @@ module Virtus
84
85
  #
85
86
  # @api private
86
87
  def determine_type_from_string(string)
87
- if string =~ TYPE_FORMAT && const_defined?(string, false)
88
- const_get(string, false)
89
- end
90
- end
91
-
92
- if RUBY_VERSION < '1.9' || RUBY_ENGINE == 'rbx'
93
- def determine_type_from_string(string)
94
- if string =~ TYPE_FORMAT && const_defined?(string)
95
- const_get(string)
96
- end
88
+ if string =~ TYPE_FORMAT && const_defined?(string, *EXTRA_CONST_ARGS)
89
+ const_get(string, *EXTRA_CONST_ARGS)
97
90
  end
98
91
  end
99
92
 
@@ -1,3 +1,3 @@
1
1
  module Virtus
2
- VERSION = '0.0.10'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ # TODO: refactor to make it inline with the new style of integration specs
4
+
5
+ class Address
6
+ include Virtus
7
+
8
+ attribute :address, String
9
+ attribute :locality, String
10
+ attribute :region, String
11
+ attribute :postal_code, String
12
+ end
13
+
14
+ class PhoneNumber
15
+ include Virtus
16
+
17
+ attribute :number, String
18
+ end
19
+
20
+ class User
21
+ include Virtus
22
+
23
+ attribute :phone_numbers, Array[PhoneNumber]
24
+ attribute :addresses, Set[Address]
25
+ end
26
+
27
+ describe User do
28
+ it { should respond_to(:phone_numbers) }
29
+ it { should respond_to(:phone_numbers=) }
30
+ it { should respond_to(:addresses) }
31
+ it { should respond_to(:addresses=) }
32
+
33
+ let(:instance) do
34
+ described_class.new(:phone_numbers => phone_numbers_attributes,
35
+ :addresses => addresses_attributes)
36
+ end
37
+
38
+ let(:phone_numbers_attributes) { [
39
+ { :number => '212-555-1212' },
40
+ { :number => '919-444-3265' },
41
+ ] }
42
+
43
+ let(:addresses_attributes) { [
44
+ { :address => '1234 Any St.', :locality => 'Anytown', :region => "DC", :postal_code => "21234" },
45
+ ] }
46
+
47
+ describe '#phone_numbers' do
48
+ describe 'first entry' do
49
+ subject { instance.phone_numbers.first }
50
+
51
+ it { should be_instance_of(PhoneNumber) }
52
+
53
+ its(:number) { should eql('212-555-1212') }
54
+ end
55
+
56
+ describe 'last entry' do
57
+ subject { instance.phone_numbers.last }
58
+
59
+ it { should be_instance_of(PhoneNumber) }
60
+
61
+ its(:number) { should eql('919-444-3265') }
62
+ end
63
+ end
64
+
65
+ describe '#addresses' do
66
+ subject { instance.addresses.first }
67
+
68
+ it { should be_instance_of(Address) }
69
+
70
+ its(:address) { should eql('1234 Any St.') }
71
+ its(:locality) { should eql('Anytown') }
72
+ its(:region) { should eql('DC') }
73
+ its(:postal_code) { should eql('21234') }
74
+ end
75
+ end