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
@@ -13,29 +13,36 @@ module Virtus
13
13
  def self.included(descendant)
14
14
  super
15
15
  descendant.extend(ClassMethods)
16
+ descendant.class_eval { include Methods }
16
17
  descendant.class_eval { include InstanceMethods }
18
+ descendant.class_eval { include InstanceMethods::Constructor }
19
+ descendant.class_eval { include InstanceMethods::MassAssignment }
17
20
  end
18
21
  private_class_method :included
19
22
 
20
- private
23
+ module Methods
21
24
 
22
- # Return class' attribute set
23
- #
24
- # @return [Virtus::AttributeSet]
25
- #
26
- # @api private
27
- def attribute_set
28
- self.class.attribute_set
29
- end
25
+ # Return a list of allowed writer method names
26
+ #
27
+ # @return [Set]
28
+ #
29
+ # @api private
30
+ def allowed_writer_methods
31
+ self.class.allowed_writer_methods
32
+ end
30
33
 
31
- # Return a list of allowed writer method names
32
- #
33
- # @return [Set]
34
- #
35
- # @api private
36
- def allowed_writer_methods
37
- self.class.allowed_writer_methods
38
- end
34
+ private
35
+
36
+ # Return class' attribute set
37
+ #
38
+ # @return [Virtus::AttributeSet]
39
+ #
40
+ # @api private
41
+ def attribute_set
42
+ self.class.attribute_set
43
+ end
44
+
45
+ end # Methods
39
46
 
40
47
  end # module ClassInclusions
41
48
  end # module Virtus
@@ -2,7 +2,8 @@ module Virtus
2
2
 
3
3
  # Class methods that are added when you include Virtus
4
4
  module ClassMethods
5
- include Extensions
5
+ include Extensions::Methods
6
+ include ConstMissingExtensions
6
7
 
7
8
  # Hook called when module is extended
8
9
  #
@@ -13,12 +14,12 @@ module Virtus
13
14
  # @api private
14
15
  def self.extended(descendant)
15
16
  super
17
+ AttributeSet.create(descendant)
16
18
  descendant.module_eval do
17
19
  extend DescendantsTracker
18
20
  include attribute_set
19
21
  end
20
22
  end
21
-
22
23
  private_class_method :extended
23
24
 
24
25
  # Returns all the attributes defined on a Class
@@ -39,11 +40,7 @@ module Virtus
39
40
  #
40
41
  # @api public
41
42
  def attribute_set
42
- return @attribute_set if defined?(@attribute_set)
43
- superclass = self.superclass
44
- method = __method__
45
- parent = superclass.public_send(method) if superclass.respond_to?(method)
46
- @attribute_set = AttributeSet.new(parent)
43
+ @attribute_set
47
44
  end
48
45
 
49
46
  # @see Virtus::ClassMethods.attribute_set
@@ -56,18 +53,7 @@ module Virtus
56
53
  attribute_set
57
54
  end
58
55
 
59
- # Hooks into const missing process to determine types of attributes
60
- #
61
- # @param [String] name
62
- #
63
- # @return [Class]
64
- #
65
- # @api private
66
- def const_missing(name)
67
- Attribute.determine_type(name) or super
68
- end
69
-
70
- private
56
+ private
71
57
 
72
58
  # Setup descendants' own Attribute-accessor-method-hosting modules
73
59
  #
@@ -84,21 +70,10 @@ module Virtus
84
70
  # @api private
85
71
  def inherited(descendant)
86
72
  super
73
+ AttributeSet.create(descendant)
87
74
  descendant.module_eval { include attribute_set }
88
75
  end
89
76
 
90
- # Add the attribute to the class' and descendants' attributes
91
- #
92
- # @param [Attribute]
93
- #
94
- # @return [undefined]
95
- #
96
- # @api private
97
- def virtus_add_attribute(attribute)
98
- super
99
- descendants.each { |descendant| descendant.attribute_set.reset }
100
- end
101
-
102
77
  # The list of allowed public methods
103
78
  #
104
79
  # @return [Array<String>]
@@ -108,5 +83,12 @@ module Virtus
108
83
  public_instance_methods.map(&:to_s)
109
84
  end
110
85
 
86
+ # @api private
87
+ def assert_valid_name(name)
88
+ if instance_methods.include?(:attributes) && name.to_sym == :attributes
89
+ raise ArgumentError, "#{name.inspect} is not allowed as an attribute name"
90
+ end
91
+ end
92
+
111
93
  end # module ClassMethods
112
94
  end # module Virtus
@@ -0,0 +1,77 @@
1
+ module Virtus
2
+
3
+ # A Configuration instance
4
+ class Configuration
5
+
6
+ # Access the coerce setting for this instance
7
+ attr_accessor :coerce
8
+
9
+ # Access the strict setting for this instance
10
+ attr_accessor :strict
11
+
12
+ # Access the constructor setting for this instance
13
+ attr_accessor :constructor
14
+
15
+ # Access the mass-assignment setting for this instance
16
+ attr_accessor :mass_assignment
17
+
18
+ # Build new configuration instance using the passed block
19
+ #
20
+ # @example
21
+ # Configuration.build do |config|
22
+ # config.coerce = false
23
+ # end
24
+ #
25
+ # @return [Configuration]
26
+ #
27
+ # @api public
28
+ def self.build(&block)
29
+ new.call(&block)
30
+ end
31
+
32
+ # Initialized a configuration instance
33
+ #
34
+ # @return [undefined]
35
+ #
36
+ # @api private
37
+ def initialize
38
+ @coerce = true
39
+ @strict = false
40
+ @constructor = true
41
+ @mass_assignment = true
42
+ @coercer = Coercible::Coercer.new
43
+ end
44
+
45
+ # Provide access to the attributes and methods via the passed block
46
+ #
47
+ # @example
48
+ # configuration.call do |config|
49
+ # config.coerce = false
50
+ # end
51
+ #
52
+ # @return [self]
53
+ #
54
+ # @api private
55
+ def call(&block)
56
+ block.call(self) if block_given?
57
+ self
58
+ end
59
+
60
+ # Access the coercer for this instance and optional configure a
61
+ # new coercer with the passed block
62
+ #
63
+ # @example
64
+ # configuration.coercer do |config|
65
+ # config.string.boolean_map = { true => '1', false => '0' }
66
+ # end
67
+ #
68
+ # @return [Coercer]
69
+ #
70
+ # @api private
71
+ def coercer(&block)
72
+ @coercer = Coercible::Coercer.new(&block) if block_given?
73
+ @coercer
74
+ end
75
+
76
+ end # class Configuration
77
+ end # module Virtus
@@ -0,0 +1,16 @@
1
+ module Virtus
2
+ module ConstMissingExtensions
3
+
4
+ # Hooks into const missing process to determine types of attributes
5
+ #
6
+ # @param [String] name
7
+ #
8
+ # @return [Class]
9
+ #
10
+ # @api private
11
+ def const_missing(name)
12
+ Attribute::Builder.determine_type(name) or Axiom::Types.const_get(name) or super
13
+ end
14
+
15
+ end
16
+ end
@@ -4,6 +4,7 @@ module Virtus
4
4
  module Extensions
5
5
  WRITER_METHOD_REGEXP = /=\z/.freeze
6
6
  INVALID_WRITER_METHODS = %w[ == != === []= attributes= ].to_set.freeze
7
+ RESERVED_NAMES = [:attributes].to_set.freeze
7
8
 
8
9
  # A hook called when an object is extended with Virtus
9
10
  #
@@ -15,78 +16,102 @@ module Virtus
15
16
  def self.extended(object)
16
17
  super
17
18
  object.instance_eval do
19
+ extend Methods
18
20
  extend InstanceMethods
21
+ extend InstanceMethods::Constructor
22
+ extend InstanceMethods::MassAssignment
19
23
  extend attribute_set
20
24
  end
21
25
  end
22
26
  private_class_method :extended
23
27
 
24
- # Defines an attribute on an object's class
25
- #
26
- # @example
27
- # class Book
28
- # include Virtus
29
- #
30
- # attribute :title, String
31
- # attribute :author, String
32
- # attribute :published_at, DateTime
33
- # attribute :page_count, Integer
34
- # attribute :index # defaults to Object
35
- # end
36
- #
37
- # @param [Symbol] name
38
- # the name of an attribute
39
- #
40
- # @param [Class] type
41
- # the type class of an attribute
42
- #
43
- # @param [#to_hash] options
44
- # the extra options hash
45
- #
46
- # @return [self]
47
- #
48
- # @see Attribute.build
49
- #
50
- # @api public
51
- def attribute(*args)
52
- attribute = Attribute.build(*args)
53
- virtus_add_attribute(attribute)
54
- self
55
- end
28
+ module Methods
56
29
 
57
- # The list of writer methods that can be mass-assigned to in #attributes=
58
- #
59
- # @return [Set]
60
- #
61
- # @api private
62
- def allowed_writer_methods
63
- @allowed_writer_methods ||=
64
- begin
65
- allowed_writer_methods = allowed_methods.grep(WRITER_METHOD_REGEXP).to_set
66
- allowed_writer_methods -= INVALID_WRITER_METHODS
67
- allowed_writer_methods.freeze
30
+ # @api private
31
+ def self.extended(descendant)
32
+ AttributeSet.create(descendant)
33
+ descendant.instance_eval do
34
+ extend attribute_set
35
+ end
68
36
  end
69
- end
37
+ private_class_method :extended
70
38
 
71
- private
39
+ # Defines an attribute on an object's class
40
+ #
41
+ # @example
42
+ # class Book
43
+ # include Virtus
44
+ #
45
+ # attribute :title, String
46
+ # attribute :author, String
47
+ # attribute :published_at, DateTime
48
+ # attribute :page_count, Integer
49
+ # attribute :index # defaults to Object
50
+ # end
51
+ #
52
+ # @param [Symbol] name
53
+ # the name of an attribute
54
+ #
55
+ # @param [Class] type
56
+ # the type class of an attribute
57
+ #
58
+ # @param [#to_hash] options
59
+ # the extra options hash
60
+ #
61
+ # @return [self]
62
+ #
63
+ # @see Attribute.build
64
+ #
65
+ # @api public
66
+ def attribute(name, type, options = {})
67
+ assert_valid_name(name)
68
+ attribute_set << Attribute.build(type, merge_options(name, options))
69
+ self
70
+ end
72
71
 
73
- # Return an attribute set for that instance
74
- #
75
- # @return [AttributeSet]
76
- #
77
- # @api private
78
- def attribute_set
79
- @attribute_set ||= AttributeSet.new
80
- end
72
+ # @api public
73
+ def values(&block)
74
+ private :attributes=
75
+ yield
76
+ include(::Equalizer.new(*attribute_set.map(&:name)))
77
+ self
78
+ end
81
79
 
82
- # Add an attribute to the attribute set
83
- #
84
- # @return [AttributeSet]
85
- #
86
- # @api private
87
- def virtus_add_attribute(attribute)
88
- attribute_set << attribute
89
- end
80
+ # The list of writer methods that can be mass-assigned to in #attributes=
81
+ #
82
+ # @return [Set]
83
+ #
84
+ # @api private
85
+ def allowed_writer_methods
86
+ @allowed_writer_methods ||=
87
+ begin
88
+ allowed_writer_methods = allowed_methods.grep(WRITER_METHOD_REGEXP).to_set
89
+ allowed_writer_methods -= INVALID_WRITER_METHODS
90
+ allowed_writer_methods.freeze
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ # Return an attribute set for that instance
97
+ #
98
+ # @return [AttributeSet]
99
+ #
100
+ # @api private
101
+ def attribute_set
102
+ @attribute_set
103
+ end
104
+
105
+ # Merge default options
106
+ #
107
+ # @return [Hash]
108
+ #
109
+ # @api private
110
+ def merge_options(name, options)
111
+ { :name => name }.merge(options)
112
+ end
113
+
114
+ end # Methods
90
115
 
91
116
  end # module Extensions
92
117
  end # module Virtus
@@ -3,17 +3,74 @@ module Virtus
3
3
  # Instance methods that are added when you include Virtus
4
4
  module InstanceMethods
5
5
 
6
- # Set attributes during initialization of an object
7
- #
8
- # @param [#to_hash] attributes
9
- # the attributes hash to be set
10
- #
11
- # @return [undefined]
12
- #
13
- # @api private
14
- def initialize(attributes = nil)
15
- self.attributes = attributes if attributes
16
- end
6
+ module Constructor
7
+
8
+ # Set attributes during initialization of an object
9
+ #
10
+ # @param [#to_hash] attributes
11
+ # the attributes hash to be set
12
+ #
13
+ # @return [undefined]
14
+ #
15
+ # @api private
16
+ def initialize(attributes = nil)
17
+ attribute_set.set(self, attributes) if attributes
18
+ set_default_attributes
19
+ end
20
+
21
+ end # Constructor
22
+
23
+ module MassAssignment
24
+
25
+ # Returns a hash of all publicly accessible attributes
26
+ #
27
+ # @example
28
+ # class User
29
+ # include Virtus
30
+ #
31
+ # attribute :name, String
32
+ # attribute :age, Integer
33
+ # end
34
+ #
35
+ # user = User.new(:name => 'John', :age => 28)
36
+ # user.attributes # => { :name => 'John', :age => 28 }
37
+ #
38
+ # @return [Hash]
39
+ #
40
+ # @api public
41
+ def attributes
42
+ attribute_set.get(self)
43
+ end
44
+ alias_method :to_hash, :attributes
45
+
46
+ # Mass-assign attribute values
47
+ #
48
+ # Keys in the +attributes+ param can be symbols or strings.
49
+ # All referenced Attribute writer methods *will* be called.
50
+ # Non-attribute setter methods on the receiver *will* be called.
51
+ #
52
+ # @example
53
+ # class User
54
+ # include Virtus
55
+ #
56
+ # attribute :name, String
57
+ # attribute :age, Integer
58
+ # end
59
+ #
60
+ # user = User.new
61
+ # user.attributes = { :name => 'John', 'age' => 28 }
62
+ #
63
+ # @param [#to_hash] attributes
64
+ # a hash of attribute names and values to set on the receiver
65
+ #
66
+ # @return [Hash]
67
+ #
68
+ # @api public
69
+ def attributes=(attributes)
70
+ attribute_set.set(self, attributes)
71
+ end
72
+
73
+ end # MassAssignment
17
74
 
18
75
  # Returns a value of the attribute with the given name
19
76
  #
@@ -35,7 +92,7 @@ module Virtus
35
92
  #
36
93
  # @api public
37
94
  def [](name)
38
- get_attribute(name)
95
+ public_send(name)
39
96
  end
40
97
 
41
98
  # Sets a value of the attribute with the given name
@@ -62,59 +119,17 @@ module Virtus
62
119
  #
63
120
  # @api public
64
121
  def []=(name, value)
65
- set_attribute(name, value)
122
+ public_send("#{name}=", value)
66
123
  end
67
124
 
68
- # Returns a hash of all publicly accessible attributes
69
- #
70
- # @example
71
- # class User
72
- # include Virtus
73
- #
74
- # attribute :name, String
75
- # attribute :age, Integer
76
- # end
77
- #
78
- # user = User.new(:name => 'John', :age => 28)
79
- # user.attributes # => { :name => 'John', :age => 28 }
125
+ # Freeze object
80
126
  #
81
- # @return [Hash]
127
+ # @return [self]
82
128
  #
83
129
  # @api public
84
- def attributes
85
- get_attributes(&:public_reader?)
86
- end
87
-
88
- # Mass-assign attribute values
89
- #
90
- # Keys in the +attributes+ param can be symbols or strings.
91
- # All referenced Attribute writer methods *will* be called.
92
- # Non-attribute setter methods on the receiver *will* be called.
93
130
  #
94
131
  # @example
95
- # class User
96
- # include Virtus
97
- #
98
- # attribute :name, String
99
- # attribute :age, Integer
100
- # end
101
- #
102
- # user = User.new
103
- # user.attributes = { :name => 'John', 'age' => 28 }
104
- #
105
- # @param [#to_hash] attributes
106
- # a hash of attribute names and values to set on the receiver
107
132
  #
108
- # @return [Hash]
109
- #
110
- # @api public
111
- def attributes=(attributes)
112
- set_attributes(attributes)
113
- end
114
-
115
- # Returns a hash of all publicly accessible attributes
116
- #
117
- # @example
118
133
  # class User
119
134
  # include Virtus
120
135
  #
@@ -123,16 +138,17 @@ module Virtus
123
138
  # end
124
139
  #
125
140
  # user = User.new(:name => 'John', :age => 28)
126
- # user.attributes # => { :name => 'John', :age => 28 }
127
- #
128
- # @return [Hash]
141
+ # user.frozen? # => false
142
+ # user.freeze
143
+ # user.frozen? # => true
129
144
  #
130
145
  # @api public
131
- def to_hash
132
- attributes
146
+ def freeze
147
+ set_default_attributes!
148
+ super
133
149
  end
134
150
 
135
- # Freeze object
151
+ # Reset an attribute to its default
136
152
  #
137
153
  # @return [self]
138
154
  #
@@ -143,87 +159,43 @@ module Virtus
143
159
  # class User
144
160
  # include Virtus
145
161
  #
146
- # attribute :name, String
147
- # attribute :age, Integer
162
+ # attribute :age, Integer, default: 21
148
163
  # end
149
164
  #
150
165
  # user = User.new(:name => 'John', :age => 28)
151
- # user.frozen? # => false
152
- # user.freeze
153
- # user.frozen? # => true
166
+ # user.age = 30
167
+ # user.age # => 30
168
+ # user.reset_attribute(:age)
169
+ # user.age # => 21
154
170
  #
155
171
  # @api public
156
- def freeze
157
- set_defaults
158
- super
172
+ def reset_attribute(attribute_name)
173
+ attribute = attribute_set[attribute_name]
174
+ attribute_set.set_default(self, attribute) if attribute
175
+ self
159
176
  end
160
177
 
161
- private
162
-
163
- # Get values of all attributes defined for this class, ignoring privacy
164
- #
165
- # @return [Hash]
166
- #
167
- # @api private
168
- def get_attributes
169
- attribute_set.each_with_object({}) do |attribute, attributes|
170
- name = attribute.name
171
- attributes[name] = get_attribute(name) if yield(attribute)
172
- end
173
- end
174
-
175
- # Ensure all defaults are set
176
- #
177
- # @return [AttributeSet]
178
- #
179
- # @api private
180
- def set_defaults
181
- attribute_set.each do |attribute|
182
- get_attribute(attribute.name)
183
- end
184
- end
185
-
186
- # Mass-assign attribute values
187
- #
188
- # @see Virtus::InstanceMethods#attributes=
178
+ # Set default attributes
189
179
  #
190
- # @return [Hash]
180
+ # @return [self]
191
181
  #
192
182
  # @api private
193
- def set_attributes(attributes)
194
- hash = ::Hash.try_convert(attributes)
195
-
196
- if hash.nil?
197
- raise NoMethodError,
198
- "Expected #{attributes.inspect} to respond to #to_hash"
199
- end
200
-
201
- hash.each do |name, value|
202
- set_attribute(name, value) if allowed_writer_methods.include?("#{name}=")
203
- end
183
+ def set_default_attributes
184
+ attribute_set.set_defaults(self)
185
+ self
204
186
  end
205
187
 
206
- # Returns a value of the attribute with the given name
188
+ # Set default attributes even lazy ones
207
189
  #
208
- # @see Virtus::InstanceMethods#[]
209
- #
210
- # @return [Object]
190
+ # @return [self]
211
191
  #
212
- # @api private
213
- def get_attribute(name)
214
- __send__(name)
192
+ # @api public
193
+ def set_default_attributes!
194
+ attribute_set.set_defaults(self, proc { |_| false })
195
+ self
215
196
  end
216
197
 
217
- # Sets a value of the attribute with the given name
218
- #
219
- # @see Virtus::InstanceMethods#[]=
220
- #
221
- # @return [Object]
222
- #
223
- # @api private
224
- def set_attribute(name, value)
225
- __send__("#{name}=", value)
226
- end
198
+ private
227
199
 
228
200
  # The list of allowed public methods
229
201
  #
@@ -234,5 +206,12 @@ module Virtus
234
206
  public_methods.map(&:to_s)
235
207
  end
236
208
 
209
+ # @api private
210
+ def assert_valid_name(name)
211
+ if respond_to?(:attributes) && name.to_sym == :attributes || name.to_sym == :attribute_set
212
+ raise ArgumentError, "#{name.inspect} is not allowed as an attribute name"
213
+ end
214
+ end
215
+
237
216
  end # module InstanceMethods
238
217
  end # module Virtus