virtus 0.5.5 → 1.0.0.beta3

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 (268) hide show
  1. data/.rspec +1 -1
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +6 -8
  5. data/Gemfile +8 -2
  6. data/Gemfile.devtools +24 -30
  7. data/Guardfile +17 -6
  8. data/README.md +215 -77
  9. data/TODO.md +6 -0
  10. data/config/flay.yml +2 -2
  11. data/config/flog.yml +1 -1
  12. data/config/mutant.yml +12 -1
  13. data/config/reek.yml +57 -66
  14. data/lib/virtus.rb +126 -42
  15. data/lib/virtus/attribute.rb +52 -240
  16. data/lib/virtus/attribute/accessor.rb +91 -0
  17. data/lib/virtus/attribute/boolean.rb +11 -8
  18. data/lib/virtus/attribute/builder.rb +120 -0
  19. data/lib/virtus/attribute/coercer.rb +51 -0
  20. data/lib/virtus/attribute/coercible.rb +14 -0
  21. data/lib/virtus/attribute/collection.rb +44 -90
  22. data/lib/virtus/attribute/default_value/from_callable.rb +1 -1
  23. data/lib/virtus/attribute/default_value/from_clonable.rb +1 -6
  24. data/lib/virtus/attribute/default_value/from_symbol.rb +3 -3
  25. data/lib/virtus/attribute/embedded_value.rb +37 -28
  26. data/lib/virtus/attribute/hash.rb +69 -71
  27. data/lib/virtus/attribute/lazy_default.rb +18 -0
  28. data/lib/virtus/attribute/strict.rb +16 -0
  29. data/lib/virtus/attribute_set.rb +81 -9
  30. data/lib/virtus/class_inclusions.rb +24 -17
  31. data/lib/virtus/class_methods.rb +13 -31
  32. data/lib/virtus/configuration.rb +77 -0
  33. data/lib/virtus/const_missing_extensions.rb +16 -0
  34. data/lib/virtus/extensions.rb +86 -61
  35. data/lib/virtus/instance_methods.rb +107 -128
  36. data/lib/virtus/model.rb +68 -0
  37. data/lib/virtus/module_builder.rb +184 -0
  38. data/lib/virtus/module_extensions.rb +27 -15
  39. data/lib/virtus/support/equalizer.rb +2 -3
  40. data/lib/virtus/support/type_lookup.rb +1 -1
  41. data/lib/virtus/value_object.rb +24 -31
  42. data/lib/virtus/version.rb +1 -1
  43. data/spec/integration/building_module_spec.rb +68 -0
  44. data/spec/integration/custom_attributes_spec.rb +8 -15
  45. data/spec/integration/custom_collection_attributes_spec.rb +101 -0
  46. data/spec/integration/default_values_spec.rb +9 -2
  47. data/spec/integration/defining_attributes_spec.rb +2 -2
  48. data/spec/integration/using_modules_spec.rb +5 -3
  49. data/spec/shared/freeze_method_behavior.rb +2 -2
  50. data/spec/shared/options_class_method.rb +1 -1
  51. data/spec/spec_helper.rb +25 -5
  52. data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +25 -0
  53. data/spec/unit/virtus/attribute/class_methods/build_spec.rb +115 -22
  54. data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +32 -0
  55. data/spec/unit/virtus/attribute/coerce_spec.rb +38 -21
  56. data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +20 -0
  57. data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +84 -0
  58. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +37 -24
  59. data/spec/unit/virtus/attribute/custom_collection_spec.rb +23 -0
  60. data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +37 -0
  61. data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +46 -10
  62. data/spec/unit/virtus/attribute/get_spec.rb +15 -63
  63. data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +80 -0
  64. data/spec/unit/virtus/attribute/hash/coerce_spec.rb +75 -13
  65. data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +20 -0
  66. data/spec/unit/virtus/attribute/set_default_value_spec.rb +74 -0
  67. data/spec/unit/virtus/attribute/set_spec.rb +16 -36
  68. data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +19 -0
  69. data/spec/unit/virtus/attribute_set/append_spec.rb +3 -5
  70. data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +1 -1
  71. data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +1 -1
  72. data/spec/unit/virtus/attribute_set/each_spec.rb +3 -3
  73. data/spec/unit/virtus/attribute_set/element_reference_spec.rb +1 -1
  74. data/spec/unit/virtus/attribute_set/element_set_spec.rb +4 -4
  75. data/spec/unit/virtus/attribute_set/merge_spec.rb +10 -16
  76. data/spec/unit/virtus/attribute_set/reset_spec.rb +6 -10
  77. data/spec/unit/virtus/attribute_spec.rb +222 -0
  78. data/spec/unit/virtus/attributes_reader_spec.rb +41 -0
  79. data/spec/unit/virtus/attributes_writer_spec.rb +51 -0
  80. data/spec/unit/virtus/class_methods/new_spec.rb +39 -0
  81. data/spec/unit/virtus/config_spec.rb +13 -0
  82. data/spec/unit/virtus/element_reader_spec.rb +21 -0
  83. data/spec/unit/virtus/element_writer_spec.rb +19 -0
  84. data/spec/unit/virtus/freeze_spec.rb +21 -0
  85. data/spec/unit/virtus/model_spec.rb +134 -0
  86. data/spec/unit/virtus/module_spec.rb +76 -0
  87. data/spec/unit/virtus/set_default_attributes_spec.rb +25 -0
  88. data/spec/unit/virtus/value_object_spec.rb +105 -0
  89. data/virtus.gemspec +4 -2
  90. metadata +82 -191
  91. data/.rvmrc +0 -1
  92. data/TODO +0 -1
  93. data/config/roodi.yml +0 -17
  94. data/lib/virtus/attribute/array.rb +0 -24
  95. data/lib/virtus/attribute/class.rb +0 -21
  96. data/lib/virtus/attribute/date.rb +0 -34
  97. data/lib/virtus/attribute/date_time.rb +0 -36
  98. data/lib/virtus/attribute/decimal.rb +0 -21
  99. data/lib/virtus/attribute/embedded_value/from_open_struct.rb +0 -19
  100. data/lib/virtus/attribute/embedded_value/from_struct.rb +0 -19
  101. data/lib/virtus/attribute/float.rb +0 -30
  102. data/lib/virtus/attribute/integer.rb +0 -27
  103. data/lib/virtus/attribute/numeric.rb +0 -11
  104. data/lib/virtus/attribute/object.rb +0 -11
  105. data/lib/virtus/attribute/set.rb +0 -24
  106. data/lib/virtus/attribute/string.rb +0 -24
  107. data/lib/virtus/attribute/symbol.rb +0 -21
  108. data/lib/virtus/attribute/time.rb +0 -36
  109. data/lib/virtus/coercion.rb +0 -32
  110. data/lib/virtus/coercion/array.rb +0 -23
  111. data/lib/virtus/coercion/date.rb +0 -26
  112. data/lib/virtus/coercion/date_time.rb +0 -26
  113. data/lib/virtus/coercion/decimal.rb +0 -40
  114. data/lib/virtus/coercion/false_class.rb +0 -24
  115. data/lib/virtus/coercion/float.rb +0 -39
  116. data/lib/virtus/coercion/hash.rb +0 -71
  117. data/lib/virtus/coercion/integer.rb +0 -83
  118. data/lib/virtus/coercion/numeric.rb +0 -66
  119. data/lib/virtus/coercion/object.rb +0 -125
  120. data/lib/virtus/coercion/string.rb +0 -218
  121. data/lib/virtus/coercion/symbol.rb +0 -24
  122. data/lib/virtus/coercion/time.rb +0 -40
  123. data/lib/virtus/coercion/time_coercions.rb +0 -86
  124. data/lib/virtus/coercion/true_class.rb +0 -24
  125. data/spec/unit/virtus/attribute/array/coerce_spec.rb +0 -13
  126. data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +0 -85
  127. data/spec/unit/virtus/attribute/boolean/define_reader_method_spec.rb +0 -15
  128. data/spec/unit/virtus/attribute/boolean/value_coerced_spec.rb +0 -97
  129. data/spec/unit/virtus/attribute/boolean_spec.rb +0 -19
  130. data/spec/unit/virtus/attribute/class/coerce_spec.rb +0 -13
  131. data/spec/unit/virtus/attribute/class_methods/accessor_spec.rb +0 -12
  132. data/spec/unit/virtus/attribute/class_methods/coercion_method_spec.rb +0 -9
  133. data/spec/unit/virtus/attribute/class_methods/default_spec.rb +0 -9
  134. data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +0 -117
  135. data/spec/unit/virtus/attribute/class_methods/merge_options_spec.rb +0 -11
  136. data/spec/unit/virtus/attribute/class_methods/primitive_spec.rb +0 -9
  137. data/spec/unit/virtus/attribute/class_methods/reader_spec.rb +0 -9
  138. data/spec/unit/virtus/attribute/class_methods/writer_spec.rb +0 -9
  139. data/spec/unit/virtus/attribute/coercion_method_spec.rb +0 -12
  140. data/spec/unit/virtus/attribute/collection/class_methods/merge_options_spec.rb +0 -40
  141. data/spec/unit/virtus/attribute/collection/coerce_and_append_member_spec.rb +0 -16
  142. data/spec/unit/virtus/attribute/collection/member_coercion/coerce_and_append_member_spec.rb +0 -18
  143. data/spec/unit/virtus/attribute/collection/member_type_spec.rb +0 -17
  144. data/spec/unit/virtus/attribute/collection/new_collection_spec.rb +0 -9
  145. data/spec/unit/virtus/attribute/date/coerce_spec.rb +0 -47
  146. data/spec/unit/virtus/attribute/date/value_coerced_spec.rb +0 -46
  147. data/spec/unit/virtus/attribute/date_time/coerce_spec.rb +0 -80
  148. data/spec/unit/virtus/attribute/decimal/coerce_spec.rb +0 -123
  149. data/spec/unit/virtus/attribute/default_spec.rb +0 -28
  150. data/spec/unit/virtus/attribute/default_value/call_spec.rb +0 -21
  151. data/spec/unit/virtus/attribute/default_value/class_methods/build_spec.rb +0 -23
  152. data/spec/unit/virtus/attribute/default_value/class_methods/new_spec.rb +0 -21
  153. data/spec/unit/virtus/attribute/default_value/from_callable/call_spec.rb +0 -19
  154. data/spec/unit/virtus/attribute/default_value/from_callable/class_methods/handle_spec.rb +0 -17
  155. data/spec/unit/virtus/attribute/default_value/from_clonable/call_spec.rb +0 -20
  156. data/spec/unit/virtus/attribute/default_value/from_clonable/class_methods/handle_spec.rb +0 -19
  157. data/spec/unit/virtus/attribute/default_value/from_symbol/call_spec.rb +0 -17
  158. data/spec/unit/virtus/attribute/default_value/from_symbol/class_methods/handle_spec.rb +0 -17
  159. data/spec/unit/virtus/attribute/default_value/value_spec.rb +0 -10
  160. data/spec/unit/virtus/attribute/define_accessor_methods_spec.rb +0 -26
  161. data/spec/unit/virtus/attribute/define_reader_method_spec.rb +0 -24
  162. data/spec/unit/virtus/attribute/define_writer_method_spec.rb +0 -24
  163. data/spec/unit/virtus/attribute/embedded_value/class_methods/determine_type_spec.rb +0 -23
  164. data/spec/unit/virtus/attribute/embedded_value/class_methods/merge_options_spec.rb +0 -17
  165. data/spec/unit/virtus/attribute/embedded_value/from_open_struct/coerce_spec.rb +0 -34
  166. data/spec/unit/virtus/attribute/embedded_value/from_struct/coerce_spec.rb +0 -34
  167. data/spec/unit/virtus/attribute/float/coerce_spec.rb +0 -123
  168. data/spec/unit/virtus/attribute/hash/class_methods/merge_options_spec.rb +0 -33
  169. data/spec/unit/virtus/attribute/hash/key_type_spec.rb +0 -10
  170. data/spec/unit/virtus/attribute/hash/value_type_spec.rb +0 -10
  171. data/spec/unit/virtus/attribute/inspect_spec.rb +0 -27
  172. data/spec/unit/virtus/attribute/integer/coerce_spec.rb +0 -117
  173. data/spec/unit/virtus/attribute/name_spec.rb +0 -12
  174. data/spec/unit/virtus/attribute/numeric/class_methods/descendants_spec.rb +0 -15
  175. data/spec/unit/virtus/attribute/numeric/class_methods/max_spec.rb +0 -9
  176. data/spec/unit/virtus/attribute/numeric/class_methods/min_spec.rb +0 -9
  177. data/spec/unit/virtus/attribute/options_spec.rb +0 -14
  178. data/spec/unit/virtus/attribute/public_reader_spec.rb +0 -24
  179. data/spec/unit/virtus/attribute/public_writer_spec.rb +0 -24
  180. data/spec/unit/virtus/attribute/set/coerce_spec.rb +0 -13
  181. data/spec/unit/virtus/attribute/string/coerce_spec.rb +0 -11
  182. data/spec/unit/virtus/attribute/symbol/coerce_spec.rb +0 -13
  183. data/spec/unit/virtus/attribute/time/coerce_spec.rb +0 -67
  184. data/spec/unit/virtus/attribute/value_coerced_spec.rb +0 -19
  185. data/spec/unit/virtus/class_methods/attribute_set_spec.rb +0 -9
  186. data/spec/unit/virtus/class_methods/attribute_spec.rb +0 -30
  187. data/spec/unit/virtus/class_methods/attributes_spec.rb +0 -22
  188. data/spec/unit/virtus/class_methods/const_missing_spec.rb +0 -27
  189. data/spec/unit/virtus/class_methods/inherited_spec.rb +0 -21
  190. data/spec/unit/virtus/coercion/array/class_methods/to_set_spec.rb +0 -12
  191. data/spec/unit/virtus/coercion/class_methods/element_reference_spec.rb +0 -17
  192. data/spec/unit/virtus/coercion/class_methods/primitive_spec.rb +0 -13
  193. data/spec/unit/virtus/coercion/date/class_methods/to_date_spec.rb +0 -10
  194. data/spec/unit/virtus/coercion/date/class_methods/to_datetime_spec.rb +0 -30
  195. data/spec/unit/virtus/coercion/date/class_methods/to_string_spec.rb +0 -12
  196. data/spec/unit/virtus/coercion/date/class_methods/to_time_spec.rb +0 -12
  197. data/spec/unit/virtus/coercion/date_time/class_methods/to_date_spec.rb +0 -30
  198. data/spec/unit/virtus/coercion/date_time/class_methods/to_datetime_spec.rb +0 -10
  199. data/spec/unit/virtus/coercion/date_time/class_methods/to_string_spec.rb +0 -12
  200. data/spec/unit/virtus/coercion/date_time/class_methods/to_time_spec.rb +0 -30
  201. data/spec/unit/virtus/coercion/decimal/class_methods/to_decimal_spec.rb +0 -9
  202. data/spec/unit/virtus/coercion/decimal/class_methods/to_float_spec.rb +0 -12
  203. data/spec/unit/virtus/coercion/decimal/class_methods/to_integer_spec.rb +0 -12
  204. data/spec/unit/virtus/coercion/decimal/class_methods/to_string_spec.rb +0 -12
  205. data/spec/unit/virtus/coercion/false_class/class_methods/to_string_spec.rb +0 -12
  206. data/spec/unit/virtus/coercion/float/class_methods/to_decimal_spec.rb +0 -12
  207. data/spec/unit/virtus/coercion/float/class_methods/to_float_spec.rb +0 -9
  208. data/spec/unit/virtus/coercion/float/class_methods/to_integer_spec.rb +0 -12
  209. data/spec/unit/virtus/coercion/float/class_methods/to_string_spec.rb +0 -12
  210. data/spec/unit/virtus/coercion/hash/class_methods/to_date_spec.rb +0 -38
  211. data/spec/unit/virtus/coercion/hash/class_methods/to_datetime_spec.rb +0 -38
  212. data/spec/unit/virtus/coercion/hash/class_methods/to_time_spec.rb +0 -38
  213. data/spec/unit/virtus/coercion/integer/class_methods/to_boolean_spec.rb +0 -25
  214. data/spec/unit/virtus/coercion/integer/class_methods/to_decimal_spec.rb +0 -12
  215. data/spec/unit/virtus/coercion/integer/class_methods/to_float_spec.rb +0 -12
  216. data/spec/unit/virtus/coercion/integer/class_methods/to_integer_spec.rb +0 -9
  217. data/spec/unit/virtus/coercion/integer/class_methods/to_string_spec.rb +0 -12
  218. data/spec/unit/virtus/coercion/numeric/class_methods/to_decimal_spec.rb +0 -10
  219. data/spec/unit/virtus/coercion/numeric/class_methods/to_float_spec.rb +0 -10
  220. data/spec/unit/virtus/coercion/numeric/class_methods/to_integer_spec.rb +0 -10
  221. data/spec/unit/virtus/coercion/numeric/class_methods/to_string_spec.rb +0 -12
  222. data/spec/unit/virtus/coercion/object/class_methods/method_missing_spec.rb +0 -33
  223. data/spec/unit/virtus/coercion/object/class_methods/to_array_spec.rb +0 -51
  224. data/spec/unit/virtus/coercion/object/class_methods/to_hash_spec.rb +0 -22
  225. data/spec/unit/virtus/coercion/object/class_methods/to_integer_spec.rb +0 -22
  226. data/spec/unit/virtus/coercion/object/class_methods/to_string_spec.rb +0 -22
  227. data/spec/unit/virtus/coercion/string/class_methods/to_boolean_spec.rb +0 -29
  228. data/spec/unit/virtus/coercion/string/class_methods/to_constant_spec.rb +0 -49
  229. data/spec/unit/virtus/coercion/string/class_methods/to_date_spec.rb +0 -23
  230. data/spec/unit/virtus/coercion/string/class_methods/to_datetime_spec.rb +0 -50
  231. data/spec/unit/virtus/coercion/string/class_methods/to_decimal_spec.rb +0 -47
  232. data/spec/unit/virtus/coercion/string/class_methods/to_float_spec.rb +0 -57
  233. data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +0 -68
  234. data/spec/unit/virtus/coercion/string/class_methods/to_symbol_spec.rb +0 -9
  235. data/spec/unit/virtus/coercion/string/class_methods/to_time_spec.rb +0 -50
  236. data/spec/unit/virtus/coercion/symbol/class_methods/to_string_spec.rb +0 -12
  237. data/spec/unit/virtus/coercion/time/class_methods/to_integer_spec.rb +0 -10
  238. data/spec/unit/virtus/coercion/time/class_methods/to_time_spec.rb +0 -10
  239. data/spec/unit/virtus/coercion/time_coercions/to_date_spec.rb +0 -33
  240. data/spec/unit/virtus/coercion/time_coercions/to_datetime_spec.rb +0 -33
  241. data/spec/unit/virtus/coercion/time_coercions/to_string_spec.rb +0 -18
  242. data/spec/unit/virtus/coercion/time_coercions/to_time_spec.rb +0 -33
  243. data/spec/unit/virtus/coercion/true_class/class_methods/to_string_spec.rb +0 -12
  244. data/spec/unit/virtus/equalizer/append_spec.rb +0 -82
  245. data/spec/unit/virtus/equalizer/class_method/new_spec.rb +0 -111
  246. data/spec/unit/virtus/equalizer/methods/eql_spec.rb +0 -47
  247. data/spec/unit/virtus/equalizer/methods/equal_value_spec.rb +0 -57
  248. data/spec/unit/virtus/extensions/allowed_writer_methods_spec.rb +0 -25
  249. data/spec/unit/virtus/extensions/attribute_spec.rb +0 -26
  250. data/spec/unit/virtus/instance_methods/attributes_spec.rb +0 -140
  251. data/spec/unit/virtus/instance_methods/element_reference_spec.rb +0 -24
  252. data/spec/unit/virtus/instance_methods/element_set_spec.rb +0 -28
  253. data/spec/unit/virtus/instance_methods/freeze_spec.rb +0 -64
  254. data/spec/unit/virtus/instance_methods/initialize_spec.rb +0 -48
  255. data/spec/unit/virtus/instance_methods/to_hash_spec.rb +0 -23
  256. data/spec/unit/virtus/module_extensions/attribute_spec.rb +0 -31
  257. data/spec/unit/virtus/options/accept_options_spec.rb +0 -37
  258. data/spec/unit/virtus/options/accepted_options_spec.rb +0 -21
  259. data/spec/unit/virtus/options/options_spec.rb +0 -34
  260. data/spec/unit/virtus/type_lookup/class_methods/extended_spec.rb +0 -9
  261. data/spec/unit/virtus/type_lookup/determine_type_spec.rb +0 -81
  262. data/spec/unit/virtus/type_lookup/primitive_spec.rb +0 -9
  263. data/spec/unit/virtus/value_object/class_methods/allowed_writer_methods_spec.rb +0 -15
  264. data/spec/unit/virtus/value_object/class_methods/attribute_spec.rb +0 -50
  265. data/spec/unit/virtus/value_object/class_methods/equalizer_spec.rb +0 -24
  266. data/spec/unit/virtus/value_object/initialize_spec.rb +0 -19
  267. data/spec/unit/virtus/value_object/instance_methods/clone_spec.rb +0 -21
  268. data/spec/unit/virtus/value_object/instance_methods/with_spec.rb +0 -31
@@ -20,7 +20,7 @@ module Virtus
20
20
 
21
21
  # Evaluates the value via value#call
22
22
  #
23
- # @param [Object]
23
+ # @param [Object] args
24
24
  #
25
25
  # @return [Object] evaluated value
26
26
  #
@@ -17,12 +17,7 @@ module Virtus
17
17
  #
18
18
  # @api private
19
19
  def self.handle?(value)
20
- case value
21
- when *SINGLETON_CLASSES
22
- false
23
- else
24
- true
25
- end
20
+ SINGLETON_CLASSES.none? { |klass| value.kind_of?(klass) }
26
21
  end
27
22
 
28
23
  # Evaluates the value via value#clone
@@ -15,7 +15,7 @@ module Virtus
15
15
  #
16
16
  # @api private
17
17
  def self.handle?(value)
18
- value.is_a?(::Symbol)
18
+ value.is_a?(Symbol)
19
19
  end
20
20
 
21
21
  # Evaluates the value via instance#public_send(value)
@@ -25,8 +25,8 @@ module Virtus
25
25
  # @return [Object] evaluated value
26
26
  #
27
27
  # @api private
28
- def call(instance, *)
29
- instance.respond_to?(@value) ? instance.public_send(@value) : @value
28
+ def call(instance, _)
29
+ instance.respond_to?(@value, true) ? instance.send(@value) : @value
30
30
  end
31
31
 
32
32
  end # class FromSymbol
@@ -22,46 +22,55 @@ module Virtus
22
22
  # user = User.new(:address => {
23
23
  # :street => 'Street 1/2', :zipcode => '12345', :city => 'NYC' })
24
24
  #
25
- class EmbeddedValue < Object
26
- primitive ::OpenStruct
25
+ class EmbeddedValue < Attribute
26
+ TYPES = [Struct, OpenStruct, Virtus].freeze
27
+
28
+ class FromStruct < self
29
+
30
+ # @api public
31
+ def coerce(input)
32
+ if input.kind_of?(primitive)
33
+ input
34
+ elsif not input.nil?
35
+ primitive.new(*input)
36
+ end
37
+ end
38
+ end # FromStruct
39
+
40
+ class FromOpenStruct < self
41
+
42
+ # @api public
43
+ def coerce(input)
44
+ if input.kind_of?(primitive)
45
+ input
46
+ elsif not input.nil?
47
+ primitive.new(input)
48
+ end
49
+ end
50
+ end # FromOpenStruct
27
51
 
28
- # @see Attribute.merge_options
29
- #
30
- # @return [Hash]
31
- # an updated options hash for configuring an EmbeddedValue instance
32
- #
33
52
  # @api private
34
- def self.merge_options(type, options)
35
- options.merge(:primitive => type)
53
+ def self.handles?(klass)
54
+ klass.is_a?(Class) && TYPES.any? { |type| klass <= type }
36
55
  end
37
56
 
38
- # Determine type based on class
39
- #
40
- # Virtus::EmbeddedValue.determine_type(Struct) # => Virtus::EmbeddedValue::FromStruct
41
- # Virtus::EmbeddedValue.determine_type(VirtusClass) # => Virtus::EmbeddedValue::FromOpenStruct
42
- #
43
- # @param [Class] klass
44
- #
45
- # @return [Virtus::Attribute::EmbeddedValue]
46
- #
47
57
  # @api private
48
58
  def self.determine_type(klass)
49
- if klass <= Virtus || klass <= OpenStruct
59
+ if klass < Virtus || klass <= OpenStruct
50
60
  FromOpenStruct
51
- elsif klass <= Struct
61
+ elsif klass < Struct
52
62
  FromStruct
53
63
  end
54
64
  end
55
65
 
56
- # Coerce attributes into a virtus object
57
- #
58
- # @param [Hash,Virtus]
59
- #
60
- # @return [Virtus]
61
- #
62
66
  # @api private
63
- def coerce(value)
64
- value if value.kind_of?(@primitive)
67
+ def self.build_type(options)
68
+ Axiom::Types::Object.new { primitive options[:type] }
69
+ end
70
+
71
+ # @api public
72
+ def primitive
73
+ type.primitive
65
74
  end
66
75
 
67
76
  end # class EmbeddedValue
@@ -12,90 +12,88 @@ module Virtus
12
12
  #
13
13
  # Post.new(:meta => { :tags => %w(foo bar) })
14
14
  #
15
- class Hash < Object
16
- primitive ::Hash
17
- coercion_method :to_hash
18
- default primitive.new
19
-
20
- # The type to which keys of this hash will be coerced
21
- #
22
- # @example
23
- #
24
- # class Request
25
- # include Virtus
26
- #
27
- # attribute :headers, Hash[Symbol => String]
28
- # end
29
- #
30
- # Post.attributes[:headers].key_type # => Virtus::Attribute::Symbol
31
- #
32
- # @return [Virtus::Attribute]
33
- #
34
- # @api public
35
- attr_reader :key_type
36
-
37
- # The type to which values of this hash will be coerced
38
- #
39
- # @example
40
- #
41
- # class Request
42
- # include Virtus
43
- #
44
- # attribute :headers, Hash[Symbol => String]
45
- # end
46
- #
47
- # Post.attributes[:headers].value_type # => Virtus::Attribute::String
48
- #
49
- # @return [Virtus::Attribute]
50
- #
51
- # @api public
52
- attr_reader :value_type
53
-
54
- # Handles hashes with [key_type => value_type] syntax.
55
- #
56
- # @param [Class]
57
- #
58
- # @param [Hash]
59
- #
60
- # @return [Hash]
61
- #
62
- # @api private
63
- def self.merge_options(type, options)
64
- if !type.respond_to?(:size)
65
- options
66
- elsif type.size > 1
67
- raise ArgumentError, "more than one [key => value] pair in `#{type.inspect}`"
68
- else
69
- options.merge(:key_type => type.keys.first, :value_type => type.values.first)
15
+ class Hash < Attribute
16
+ primitive ::Hash
17
+ default primitive.new
18
+
19
+ # FIXME: remove this once axiom-types supports it
20
+ Type = Struct.new(:key_type, :value_type) do
21
+ def self.infer(type)
22
+ if type.is_a?(Class) && type < Axiom::Types::Type
23
+ new(type.key_type, type.value_type)
24
+ else
25
+ type_options = infer_key_and_value_types(type)
26
+ key_class = determine_type(type_options.fetch(:key_type, Object))
27
+ value_class = determine_type(type_options.fetch(:value_type, Object))
28
+
29
+ new(key_class, value_class)
30
+ end
31
+ end
32
+
33
+ def self.determine_type(type)
34
+ if EmbeddedValue.handles?(type)
35
+ type
36
+ else
37
+ Axiom::Types.infer(type)
38
+ end
39
+ end
40
+
41
+ def self.infer_key_and_value_types(type)
42
+ return {} unless type.kind_of?(::Hash)
43
+
44
+ if type.size > 1
45
+ raise ArgumentError, "more than one [key => value] pair in `#{type}`"
46
+ else
47
+ { :key_type => type.keys.first, :value_type => type.values.first }
48
+ end
49
+ end
50
+
51
+ def coercion_method
52
+ :to_hash
53
+ end
54
+
55
+ def primitive
56
+ ::Hash
70
57
  end
71
58
  end
72
59
 
73
- # Initializes an instance of {Virtus::Attribute::Hash}
74
- #
75
60
  # @api private
76
- def initialize(*)
77
- super
78
- @key_type = @options[:key_type] || Object
79
- @value_type = @options[:value_type] || Object
80
- @key_type_instance = Attribute.build(@name, @key_type)
81
- @value_type_instance = Attribute.build(@name, @value_type)
61
+ def self.build_type(options)
62
+ Type.infer(options[:type])
82
63
  end
83
64
 
84
- # Coerce a hash with keys and values
85
- #
86
- # @param [Object]
87
- #
88
- # @return [Object]
89
- #
90
65
  # @api private
91
- def coerce(value)
66
+ def self.merge_options!(type, options)
67
+ unless options.key?(:key_type)
68
+ options[:key_type] = Attribute.build(type.key_type)
69
+ end
70
+
71
+ unless options.key?(:value_type)
72
+ options[:value_type] = Attribute.build(type.value_type)
73
+ end
74
+ end
75
+
76
+ # @api public
77
+ def coerce(*)
92
78
  coerced = super
79
+
93
80
  return coerced unless coerced.respond_to?(:each_with_object)
81
+
94
82
  coerced.each_with_object({}) do |(key, value), hash|
95
- hash[@key_type_instance.coerce(key)] = @value_type_instance.coerce(value)
83
+ hash[key_type.coerce(key)] = value_type.coerce(value)
96
84
  end
97
85
  end
98
86
 
87
+ # @api private
88
+ def key_type
89
+ @options[:key_type]
90
+ end
91
+
92
+ # @api private
93
+ def value_type
94
+ @options[:value_type]
95
+ end
96
+
99
97
  end # class Hash
100
98
  end # class Attribute
101
99
  end # module Virtus
@@ -0,0 +1,18 @@
1
+ module Virtus
2
+ class Attribute
3
+
4
+ module LazyDefault
5
+
6
+ # @api public
7
+ def get(instance)
8
+ if instance.instance_variable_defined?(instance_variable_name)
9
+ super
10
+ else
11
+ set_default_value(instance)
12
+ end
13
+ end
14
+
15
+ end # LazyDefault
16
+
17
+ end # Attribute
18
+ end # Virtus
@@ -0,0 +1,16 @@
1
+ module Virtus
2
+ class Attribute
3
+
4
+ module Strict
5
+
6
+ # @api public
7
+ def coerce(input)
8
+ coerced = super
9
+ raise ArgumentError unless coercer.success?(primitive, coerced)
10
+ coerced
11
+ end
12
+
13
+ end # Strict
14
+
15
+ end # Attribute
16
+ end # Virtus
@@ -4,6 +4,14 @@ module Virtus
4
4
  class AttributeSet < Module
5
5
  include Enumerable
6
6
 
7
+ # @api private
8
+ def self.create(descendant)
9
+ if descendant.respond_to?(:superclass) && descendant.superclass.respond_to?(:attribute_set)
10
+ parent = descendant.superclass.public_send(:attribute_set)
11
+ end
12
+ descendant.instance_variable_set('@attribute_set', AttributeSet.new(parent))
13
+ end
14
+
7
15
  # Initialize an AttributeSet
8
16
  #
9
17
  # @param [AttributeSet] parent
@@ -13,9 +21,9 @@ module Virtus
13
21
  #
14
22
  # @api private
15
23
  def initialize(parent = nil, attributes = [])
16
- @parent = parent
17
- @attributes = attributes.dup
18
- @index = {}
24
+ @parent = parent
25
+ @attributes = attributes.dup
26
+ @index = {}
19
27
  reset
20
28
  end
21
29
 
@@ -67,7 +75,6 @@ module Virtus
67
75
  def <<(attribute)
68
76
  self[attribute.name] = attribute
69
77
  attribute.define_accessor_methods(self)
70
- self
71
78
  end
72
79
 
73
80
  # Get an attribute by name
@@ -117,13 +124,12 @@ module Virtus
117
124
  # @param [Symbol] method_name
118
125
  # @param [Symbol] visibility
119
126
  #
120
- # @return [self]
127
+ # @return [undefined]
121
128
  #
122
129
  # @api private
123
130
  def define_reader_method(attribute, method_name, visibility)
124
131
  define_method(method_name) { attribute.get(self) }
125
132
  send(visibility, method_name)
126
- self
127
133
  end
128
134
 
129
135
  # Defines an attribute writer method
@@ -132,16 +138,82 @@ module Virtus
132
138
  # @param [Symbol] method_name
133
139
  # @param [Symbol] visibility
134
140
  #
135
- # @return [self]
141
+ # @return [undefined]
136
142
  #
137
143
  # @api private
138
144
  def define_writer_method(attribute, method_name, visibility)
139
145
  define_method(method_name) { |value| attribute.set(self, value) }
140
146
  send(visibility, method_name)
141
- self
142
147
  end
143
148
 
144
- private
149
+ # Get values of all attributes defined for this class, ignoring privacy
150
+ #
151
+ # @return [Hash]
152
+ #
153
+ # @api private
154
+ def get(object)
155
+ each_with_object({}) do |attribute, attributes|
156
+ name = attribute.name
157
+ attributes[name] = object.__send__(name) if attribute.public_reader?
158
+ end
159
+ end
160
+
161
+ # Mass-assign attribute values
162
+ #
163
+ # @see Virtus::InstanceMethods#attributes=
164
+ #
165
+ # @return [Hash]
166
+ #
167
+ # @api private
168
+ def set(object, attributes)
169
+ coerce(attributes).each do |name, value|
170
+ writer_name = "#{name}="
171
+ if object.allowed_writer_methods.include?(writer_name)
172
+ object.__send__(writer_name, value)
173
+ end
174
+ end
175
+ end
176
+
177
+ # Set default attributes
178
+ #
179
+ # @return [self]
180
+ #
181
+ # @api private
182
+ def set_defaults(object, filter = method(:skip_default?))
183
+ each do |attribute|
184
+ if filter.call(object, attribute)
185
+ next
186
+ end
187
+ set_default(object, attribute)
188
+ end
189
+ end
190
+
191
+ # Set default attribute
192
+ #
193
+ # @return [default value]
194
+ #
195
+ # @api private
196
+ def set_default(object, attribute)
197
+ attribute.set_default_value(object)
198
+ end
199
+
200
+ # Coerce attributes received to a hash
201
+ #
202
+ # @return [Hash]
203
+ #
204
+ # @api private
205
+ def coerce(attributes)
206
+ ::Hash.try_convert(attributes) or raise(
207
+ NoMethodError, "Expected #{attributes.inspect} to respond to #to_hash"
208
+ )
209
+ end
210
+
211
+ private
212
+
213
+ # @api private
214
+ def skip_default?(object, attribute)
215
+ attribute.lazy? || object.instance_variable_defined?(attribute.instance_variable_name)
216
+ end
145
217
 
146
218
  # Merge the attributes into the index
147
219
  #