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
@@ -1,3 +1,3 @@
1
1
  module Virtus
2
- VERSION = '0.5.5'
2
+ VERSION = '1.0.0.beta3'
3
3
  end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'I can create a Virtus module' do
4
+ before do
5
+ module Examples
6
+ NoncoercingModule = Virtus.model { |config|
7
+ config.coerce = false
8
+ }
9
+
10
+ CoercingModule = Virtus.model { |config|
11
+ config.coerce = true
12
+
13
+ config.coercer do |coercer|
14
+ coercer.string.boolean_map = { 'yup' => true, 'nope' => false }
15
+ end
16
+ }
17
+
18
+ StrictModule = Virtus.model { |config|
19
+ config.strict = true
20
+ }
21
+
22
+ class NoncoercedUser
23
+ include NoncoercingModule
24
+
25
+ attribute :name, String
26
+ attribute :happy, String
27
+ end
28
+
29
+ class CoercedUser
30
+ include CoercingModule
31
+
32
+ attribute :name, String
33
+ attribute :happy, Boolean
34
+ end
35
+
36
+ class StrictModel
37
+ include StrictModule
38
+
39
+ attribute :stuff, Hash
40
+ attribute :happy, Boolean, :strict => false
41
+ end
42
+ end
43
+ end
44
+
45
+ specify 'including a custom module with coercion disabled' do
46
+ user = Examples::NoncoercedUser.new(:name => :Giorgio, :happy => 'yes')
47
+
48
+ expect(user.name).to be(:Giorgio)
49
+ expect(user.happy).to eql('yes')
50
+ end
51
+
52
+ specify 'including a custom module with coercion enabled' do
53
+ user = Examples::CoercedUser.new(:name => 'Paul', :happy => 'nope')
54
+
55
+ expect(user.name).to eql('Paul')
56
+ expect(user.happy).to be(false)
57
+ end
58
+
59
+ specify 'including a custom module with strict enabled' do
60
+ model = Examples::StrictModel.new
61
+
62
+ expect { model.stuff = 'foo' }.to raise_error(ArgumentError)
63
+
64
+ model.happy = 'foo'
65
+
66
+ expect(model.happy).to eql('foo')
67
+ end
68
+ end
@@ -2,24 +2,17 @@ require 'spec_helper'
2
2
 
3
3
  describe 'custom attributes' do
4
4
 
5
- module Virtus
6
- class Coercion
7
- class String < Virtus::Coercion::Object
8
- def self.to_upcase(value)
9
- value.upcase
10
- end
11
- end
12
- end
13
- end
14
-
15
5
  before do
16
6
  module Examples
17
- class UpperCase < Virtus::Attribute::Object
18
- primitive String
19
- coercion_method :to_upcase
7
+ class NoisyString < Virtus::Attribute
8
+ lazy true
9
+
10
+ def coerce(input)
11
+ input.to_s.upcase
12
+ end
20
13
  end
21
14
 
22
- class RegularExpression < Virtus::Attribute::Object
15
+ class RegularExpression < Virtus::Attribute
23
16
  primitive Regexp
24
17
  end
25
18
 
@@ -27,7 +20,7 @@ describe 'custom attributes' do
27
20
  include Virtus
28
21
 
29
22
  attribute :name, String
30
- attribute :scream, UpperCase
23
+ attribute :scream, NoisyString
31
24
  attribute :expression, RegularExpression
32
25
  end
33
26
  end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'custom collection attributes' do
4
+ let(:library) { Examples::Library.new }
5
+ let(:books) { library.books }
6
+
7
+ before do
8
+ module Examples end
9
+ Examples.const_set 'BookCollection', book_collection_class
10
+
11
+ module Examples
12
+ class Book
13
+ include Virtus
14
+
15
+ attribute :title, String
16
+ end
17
+
18
+ class BookCollectionAttribute < Virtus::Attribute::Collection
19
+ primitive BookCollection
20
+ end
21
+
22
+ class Library
23
+ include Virtus
24
+
25
+ attribute :books, BookCollection[Book]
26
+ end
27
+ end
28
+ end
29
+
30
+ after do
31
+ [:BookCollectionAttribute, :BookCollection, :Book, :Library].each do |const|
32
+ Examples.send(:remove_const, const)
33
+ end
34
+ end
35
+
36
+ shared_examples_for 'a collection' do
37
+ it 'can be used as Virtus attributes' do
38
+ attribute = Examples::Library.attribute_set[:books]
39
+ attribute.should be_kind_of(Examples::BookCollectionAttribute)
40
+ end
41
+
42
+ it 'defaults to an empty collection' do
43
+ books_should_be_an_empty_collection
44
+ end
45
+
46
+ it 'coerces nil' do
47
+ library.books = nil
48
+ books_should_be_an_empty_collection
49
+ end
50
+
51
+ it 'coerces an empty array' do
52
+ library.books = []
53
+ books_should_be_an_empty_collection
54
+ end
55
+
56
+ it 'coerces an array of attribute hashes' do
57
+ library.books = [{ :title => 'Foo' }]
58
+ books.should be_kind_of(Examples::BookCollection)
59
+ end
60
+
61
+ it 'coerces its members' do
62
+ library.books = [{ :title => 'Foo' }]
63
+ books.count.should == 1
64
+ books.first.should be_kind_of(Examples::Book)
65
+ end
66
+
67
+ def books_should_be_an_empty_collection
68
+ books.should be_kind_of(Examples::BookCollection)
69
+ books.count.should == 0
70
+ end
71
+ end
72
+
73
+ context 'with an array subclass' do
74
+ let(:book_collection_class) { Class.new(Array) }
75
+
76
+ it_behaves_like 'a collection'
77
+ end
78
+
79
+ context 'with an enumerable' do
80
+ require 'forwardable'
81
+
82
+ let(:book_collection_class) {
83
+ Class.new do
84
+ extend Forwardable
85
+ include Enumerable
86
+
87
+ def_delegators :@array, :each, :<<
88
+
89
+ def initialize(*args)
90
+ @array = Array[*args]
91
+ end
92
+
93
+ def self.[](*args)
94
+ new(*args)
95
+ end
96
+ end
97
+ }
98
+
99
+ it_behaves_like 'a collection'
100
+ end
101
+ end
@@ -15,10 +15,10 @@ describe "default values" do
15
15
  include Virtus
16
16
 
17
17
  attribute :title, String
18
- attribute :slug, String, :default => lambda { |post, attribute| post.title.downcase.gsub(' ', '-') }
18
+ attribute :slug, String, :default => lambda { |post, attribute| post.title.downcase.gsub(' ', '-') }, :lazy => true
19
19
  attribute :view_count, Integer, :default => 0
20
20
  attribute :published, Boolean, :default => false, :accessor => :private
21
- attribute :editor_title, String, :default => :default_editor_title
21
+ attribute :editor_title, String, :default => :default_editor_title, :lazy => true
22
22
  attribute :reference, String, :default => Reference.new
23
23
  attribute :revisions, Array
24
24
  attribute :index, Hash
@@ -52,6 +52,13 @@ describe "default values" do
52
52
  subject.editor_title.should == 'UNPUBLISHED: Top Secret'
53
53
  end
54
54
 
55
+ specify 'you can reset attribute to its default' do
56
+ subject.view_count = 10
57
+ expect do
58
+ subject.reset_attribute(:view_count)
59
+ end.to change { subject.view_count }.to(0)
60
+ end
61
+
55
62
  context 'a ValueObject' do
56
63
  it 'does not duplicate the ValueObject' do
57
64
  page1 = Examples::Page.new
@@ -10,7 +10,7 @@ describe "virtus attribute definitions" do
10
10
  attribute :name, String
11
11
  attribute :age, Integer
12
12
  attribute :doctor, Boolean
13
- attribute :salery, Decimal
13
+ attribute :salary, Decimal
14
14
  end
15
15
 
16
16
  class Manager < Person
@@ -39,7 +39,7 @@ describe "virtus attribute definitions" do
39
39
  end
40
40
 
41
41
  context 'with attributes' do
42
- let(:attributes) { {:name => 'Jane', :age => 45, :doctor => true, :salery => 4500} }
42
+ let(:attributes) { {:name => 'Jane', :age => 45, :doctor => true, :salary => 4500} }
43
43
  subject { Examples::Person.new(attributes) }
44
44
 
45
45
  specify "#attributes returns the object's attributes as a hash" do
@@ -11,6 +11,7 @@ describe 'I can define attributes within a module' do
11
11
  include Common
12
12
 
13
13
  attribute :name, String
14
+ attribute :gamer, Boolean
14
15
  end
15
16
 
16
17
  module Age
@@ -32,10 +33,11 @@ describe 'I can define attributes within a module' do
32
33
  end
33
34
 
34
35
  specify 'including a module with attributes into a class' do
35
- Examples::User.attribute_set[:name].should be_instance_of(Virtus::Attribute::String)
36
+ Examples::User.attribute_set[:name].should be_instance_of(Virtus::Attribute)
37
+ Examples::User.attribute_set[:gamer].should be_instance_of(Virtus::Attribute::Boolean)
36
38
 
37
- Examples::Admin.attribute_set[:name].should be_instance_of(Virtus::Attribute::String)
38
- Examples::Admin.attribute_set[:age].should be_instance_of(Virtus::Attribute::Integer)
39
+ Examples::Admin.attribute_set[:name].should be_instance_of(Virtus::Attribute)
40
+ Examples::Admin.attribute_set[:age].should be_instance_of(Virtus::Attribute)
39
41
 
40
42
  user = Examples::Admin.new(:name => 'Piotr', :age => 29)
41
43
  user.name.should eql('Piotr')
@@ -1,7 +1,7 @@
1
1
  shared_examples_for 'a #freeze method' do
2
2
  let(:sample_exception) do
3
3
  begin
4
- object.dup.freeze.instance_variable_set(:@foo,:bar)
4
+ object.dup.freeze.instance_variable_set(:@foo, :bar)
5
5
  rescue => exception
6
6
  exception
7
7
  end
@@ -26,7 +26,7 @@ shared_examples_for 'a #freeze method' do
26
26
  it 'prevents future modifications' do
27
27
  subject
28
28
  expectation = raise_error(expected_exception_class,expected_exception_message)
29
- expect { object.instance_variable_set(:@foo,:bar) }.to(expectation)
29
+ expect { object.instance_variable_set(:@foo, :bar) }.to(expectation)
30
30
  end
31
31
 
32
32
  its(:frozen?) { should be(true) }
@@ -8,7 +8,7 @@ shared_examples_for 'an options class method' do
8
8
  context 'with a default value' do
9
9
  subject { object.send(method, value) }
10
10
 
11
- let(:value) { stub('value') }
11
+ let(:value) { double('value') }
12
12
 
13
13
  it { should equal(object) }
14
14
 
@@ -1,13 +1,33 @@
1
+ if ENV['COVERAGE'] == 'true'
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+
10
+ SimpleCov.start do
11
+ command_name 'spec:unit'
12
+ add_filter 'config/'
13
+ add_filter 'spec'
14
+ add_filter '.bundle'
15
+ minimum_coverage 99
16
+ end
17
+ end
18
+
1
19
  require 'rspec'
20
+ require 'bogus/rspec'
2
21
  require 'virtus'
3
22
 
4
- ENV['TZ'] = 'UTC'
5
-
6
- if ENV['COVERAGE']
7
- require 'simplecov'
8
- SimpleCov.start
23
+ module Virtus
24
+ def self.warn(*)
25
+ # shut up in tests
26
+ end
9
27
  end
10
28
 
29
+ ENV['TZ'] = 'UTC'
30
+
11
31
  # require spec support files and shared behavior
12
32
  Dir[File.expand_path('../shared/**/*.rb', __FILE__)].each { |file| require file }
13
33
 
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::Boolean, '#value_coerced?' do
4
+ subject { object.value_coerced?(input) }
5
+
6
+ let(:object) { described_class.build('Boolean') }
7
+
8
+ context 'when input is true' do
9
+ let(:input) { true }
10
+
11
+ it { should be(true) }
12
+ end
13
+
14
+ context 'when input is false' do
15
+ let(:input) { false }
16
+
17
+ it { should be(true) }
18
+ end
19
+
20
+ context 'when input is not coerced' do
21
+ let(:input) { 1 }
22
+
23
+ it { should be(false) }
24
+ end
25
+ end
@@ -1,47 +1,140 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus::Attribute, '.build' do
4
- let(:object) { described_class }
5
- let(:name) { :name }
6
- let(:type) { String }
4
+ subject { described_class.build(type, options.merge(:name => name)) }
5
+
6
+ let(:name) { :test }
7
+ let(:type) { String }
8
+ let(:options) { {} }
9
+
10
+ share_examples_for 'a valid attribute instance' do
11
+ it { should be_instance_of(Virtus::Attribute) }
12
+
13
+ it { should be_frozen }
14
+ end
7
15
 
8
16
  context 'without options' do
9
- subject { object.build(name, type) }
17
+ it_behaves_like 'a valid attribute instance'
18
+
19
+ it { should be_coercible }
20
+ it { should be_public_reader }
21
+ it { should be_public_writer }
22
+ it { should_not be_lazy }
23
+
24
+ it 'sets up a coercer' do
25
+ expect(subject.options[:coerce]).to be(true)
26
+ expect(subject.coercer).to be_instance_of(Virtus::Attribute::Coercer)
27
+ end
28
+ end
29
+
30
+ context 'when name is passed as a string' do
31
+ let(:name) { 'something' }
32
+
33
+ its(:name) { should be(:something) }
34
+ end
35
+
36
+ context 'when coercion is turned off in options' do
37
+ let(:options) { { :coerce => false } }
38
+
39
+ it_behaves_like 'a valid attribute instance'
40
+
41
+ it { should_not be_coercible }
42
+ end
43
+
44
+ context 'when options specify reader visibility' do
45
+ let(:options) { { :reader => :private } }
46
+
47
+ it_behaves_like 'a valid attribute instance'
48
+
49
+ it { should_not be_public_reader }
50
+ it { should be_public_writer }
51
+ end
52
+
53
+ context 'when options specify writer visibility' do
54
+ let(:options) { { :writer => :private } }
10
55
 
11
- it { should be_instance_of(Virtus::Attribute::String) }
56
+ it_behaves_like 'a valid attribute instance'
12
57
 
13
- its(:name) { should be(name) }
58
+ it { should be_public_reader }
59
+ it { should_not be_public_writer }
60
+ end
61
+
62
+ context 'when options specify lazy accessor' do
63
+ let(:options) { { :lazy => true } }
64
+
65
+ it_behaves_like 'a valid attribute instance'
14
66
 
15
- its(:options) { should == Virtus::Attribute::String.options }
67
+ it { should be_lazy }
16
68
  end
17
69
 
18
- context 'with options' do
19
- subject { object.build(name, type, options) }
70
+ context 'when options specify strict mode' do
71
+ let(:options) { { :strict => true } }
20
72
 
21
- let(:options) { {} }
73
+ it_behaves_like 'a valid attribute instance'
74
+
75
+ it { should be_strict }
76
+ end
22
77
 
23
- it { should be_instance_of(Virtus::Attribute::String) }
78
+ context 'when type is a string' do
79
+ let(:type) { 'Integer' }
24
80
 
25
- its(:name) { should be(name) }
81
+ it_behaves_like 'a valid attribute instance'
26
82
 
27
- its(:options) { should == Virtus::Attribute::String.options }
83
+ its(:type) { should be(Axiom::Types::Integer) }
28
84
  end
29
85
 
30
- context 'without a type' do
31
- subject { object.build(name) }
86
+ context 'when type is a symbol of an existing class constant' do
87
+ let(:type) { :String }
32
88
 
33
- it { should be_instance_of(Virtus::Attribute::Object) }
89
+ it_behaves_like 'a valid attribute instance'
34
90
 
35
- its(:name) { should be(name) }
91
+ its(:type) { should be(Axiom::Types::String) }
92
+ end
93
+
94
+ context 'when type is an axiom type' do
95
+ let(:type) { Axiom::Types::Integer }
96
+
97
+ it_behaves_like 'a valid attribute instance'
36
98
 
37
- its(:options) { should == Virtus::Attribute::Object.options }
99
+ its(:type) { should be(type) }
38
100
  end
39
101
 
40
- context 'with an invalid type' do
41
- subject { object.build(name, type) }
102
+ context 'when custom attribute class exists for a given primitive' do
103
+ let(:type) { Class.new }
104
+ let(:attribute) { Class.new(Virtus::Attribute) }
105
+
106
+ before do
107
+ attribute.primitive(type)
108
+ end
109
+
110
+ it { should be_instance_of(attribute) }
111
+
112
+ its(:type) { should be(Axiom::Types::Object) }
113
+ end
114
+
115
+ context 'when custom attribute class exists for a given array with member coercion defined' do
116
+ let(:type) { Class.new(Array)[String] }
117
+ let(:attribute) { Class.new(Virtus::Attribute) }
118
+
119
+ before do
120
+ attribute.primitive(type.class)
121
+ end
122
+
123
+ it { should be_instance_of(attribute) }
124
+
125
+ its(:type) { should be < Axiom::Types::Collection }
126
+ end
127
+
128
+ context 'when custom collection-like attribute class exists for a given enumerable primitive' do
129
+ let(:type) { Class.new { include Enumerable } }
130
+ let(:attribute) { Class.new(Virtus::Attribute::Collection) }
131
+
132
+ before do
133
+ attribute.primitive(type)
134
+ end
42
135
 
43
- let(:type) { 'test' }
136
+ it { should be_instance_of(attribute) }
44
137
 
45
- specify { expect { subject }.to raise_error(ArgumentError, '"test" does not map to an attribute type') }
138
+ its(:type) { should be < Axiom::Types::Collection }
46
139
  end
47
140
  end