virtus 0.0.10 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. data/.gitignore +33 -7
  2. data/.travis.yml +4 -74
  3. data/Changelog.md +11 -2
  4. data/Gemfile +10 -9
  5. data/README.md +85 -1
  6. data/TODO +18 -0
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/roodi.yml +3 -3
  10. data/config/site.reek +8 -3
  11. data/lib/virtus.rb +5 -0
  12. data/lib/virtus/attribute.rb +137 -30
  13. data/lib/virtus/attribute/array.rb +17 -1
  14. data/lib/virtus/attribute/boolean.rb +8 -13
  15. data/lib/virtus/attribute/collection.rb +96 -0
  16. data/lib/virtus/attribute/default_value.rb +11 -7
  17. data/lib/virtus/attribute/embedded_value.rb +70 -0
  18. data/lib/virtus/attribute/set.rb +25 -0
  19. data/lib/virtus/attribute_set.rb +18 -16
  20. data/lib/virtus/attributes_accessor.rb +66 -0
  21. data/lib/virtus/class_methods.rb +50 -28
  22. data/lib/virtus/coercion/array.rb +23 -0
  23. data/lib/virtus/coercion/string.rb +10 -4
  24. data/lib/virtus/instance_methods.rb +38 -31
  25. data/lib/virtus/support/options.rb +6 -8
  26. data/lib/virtus/support/type_lookup.rb +4 -11
  27. data/lib/virtus/version.rb +1 -1
  28. data/spec/integration/collection_member_coercion_spec.rb +75 -0
  29. data/spec/integration/custom_attributes_spec.rb +49 -0
  30. data/spec/integration/default_values_spec.rb +32 -0
  31. data/spec/integration/defining_attributes_spec.rb +79 -0
  32. data/spec/integration/embedded_value_spec.rb +50 -0
  33. data/spec/integration/overriding_virtus_spec.rb +46 -0
  34. data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
  35. data/spec/rcov.opts +1 -0
  36. data/spec/shared/constants_helpers.rb +9 -0
  37. data/spec/shared/options_class_method.rb +19 -0
  38. data/spec/spec_helper.rb +20 -7
  39. data/spec/unit/virtus/attribute/array/coerce_spec.rb +13 -0
  40. data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +85 -0
  41. data/spec/unit/virtus/attribute/boolean/define_reader_method_spec.rb +15 -0
  42. data/spec/unit/virtus/attribute/boolean/value_coerced_spec.rb +97 -0
  43. data/spec/unit/virtus/attribute/boolean_spec.rb +2 -81
  44. data/spec/unit/virtus/attribute/class/coerce_spec.rb +13 -0
  45. data/spec/unit/virtus/attribute/class_methods/accessor_spec.rb +12 -0
  46. data/spec/unit/virtus/attribute/class_methods/build_spec.rb +37 -0
  47. data/spec/unit/virtus/attribute/class_methods/coercion_method_spec.rb +9 -0
  48. data/spec/unit/virtus/attribute/class_methods/default_spec.rb +9 -0
  49. data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +31 -1
  50. data/spec/unit/virtus/attribute/class_methods/merge_options_spec.rb +11 -0
  51. data/spec/unit/virtus/attribute/class_methods/primitive_spec.rb +9 -0
  52. data/spec/unit/virtus/attribute/class_methods/reader_spec.rb +9 -0
  53. data/spec/unit/virtus/attribute/class_methods/writer_spec.rb +9 -0
  54. data/spec/unit/virtus/attribute/coerce_spec.rb +30 -0
  55. data/spec/unit/virtus/attribute/coercion_method_spec.rb +12 -0
  56. data/spec/unit/virtus/attribute/collection/class_methods/merge_options_spec.rb +40 -0
  57. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +26 -0
  58. data/spec/unit/virtus/attribute/date/coerce_spec.rb +47 -0
  59. data/spec/unit/virtus/attribute/date/value_coerced_spec.rb +46 -0
  60. data/spec/unit/virtus/attribute/date_time/coerce_spec.rb +68 -0
  61. data/spec/unit/virtus/attribute/decimal/coerce_spec.rb +117 -0
  62. data/spec/unit/virtus/attribute/default_spec.rb +32 -0
  63. data/spec/unit/virtus/attribute/default_value/attribute_spec.rb +11 -0
  64. data/spec/unit/virtus/attribute/default_value/class_methods/new_spec.rb +4 -2
  65. data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +51 -0
  66. data/spec/unit/virtus/attribute/default_value/instance_methods/evaluate_spec.rb +9 -6
  67. data/spec/unit/virtus/attribute/default_value/value_spec.rb +11 -0
  68. data/spec/unit/virtus/attribute/define_accessor_methods_spec.rb +26 -0
  69. data/spec/unit/virtus/attribute/define_reader_method_spec.rb +24 -0
  70. data/spec/unit/virtus/attribute/define_writer_method_spec.rb +24 -0
  71. data/spec/unit/virtus/attribute/embedded_value/class_methods/merge_options_spec.rb +17 -0
  72. data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +50 -0
  73. data/spec/unit/virtus/attribute/float/coerce_spec.rb +117 -0
  74. data/spec/unit/virtus/attribute/get_spec.rb +80 -0
  75. data/spec/unit/virtus/attribute/inspect_spec.rb +27 -0
  76. data/spec/unit/virtus/attribute/instance_variable_name_spec.rb +12 -0
  77. data/spec/unit/virtus/attribute/integer/coerce_spec.rb +105 -0
  78. data/spec/unit/virtus/attribute/name_spec.rb +12 -0
  79. data/spec/unit/virtus/attribute/numeric/class_methods/descendants_spec.rb +1 -1
  80. data/spec/unit/virtus/attribute/numeric/class_methods/max_spec.rb +9 -0
  81. data/spec/unit/virtus/attribute/numeric/class_methods/min_spec.rb +9 -0
  82. data/spec/unit/virtus/attribute/object/class_methods/descendants_spec.rb +9 -7
  83. data/spec/unit/virtus/attribute/options_spec.rb +14 -0
  84. data/spec/unit/virtus/attribute/public_reader_spec.rb +24 -0
  85. data/spec/unit/virtus/attribute/public_writer_spec.rb +24 -0
  86. data/spec/unit/virtus/attribute/reader_visibility_spec.rb +24 -0
  87. data/spec/unit/virtus/attribute/set/coerce_spec.rb +13 -0
  88. data/spec/unit/virtus/attribute/set_spec.rb +49 -0
  89. data/spec/unit/virtus/attribute/string/coerce_spec.rb +11 -0
  90. data/spec/unit/virtus/attribute/time/coerce_spec.rb +67 -0
  91. data/spec/unit/virtus/attribute/value_coerced_spec.rb +19 -0
  92. data/spec/unit/virtus/attribute/writer_visibility_spec.rb +24 -0
  93. data/spec/unit/virtus/attribute_set/append_spec.rb +12 -0
  94. data/spec/unit/virtus/attribute_set/element_reference_spec.rb +4 -0
  95. data/spec/unit/virtus/attribute_set/element_set_spec.rb +29 -9
  96. data/spec/unit/virtus/attributes_accessor/inspect_spec.rb +9 -0
  97. data/spec/unit/virtus/class_methods/attribute_spec.rb +23 -5
  98. data/spec/unit/virtus/class_methods/attributes_spec.rb +3 -5
  99. data/spec/unit/virtus/class_methods/const_missing_spec.rb +27 -0
  100. data/spec/unit/virtus/class_methods/inherited_spec.rb +21 -0
  101. data/spec/unit/virtus/coercion/array/to_set_spec.rb +12 -0
  102. data/spec/unit/virtus/coercion/date/class_methods/to_date_spec.rb +10 -0
  103. data/spec/unit/virtus/coercion/date_time/class_methods/to_datetime_spec.rb +10 -0
  104. data/spec/unit/virtus/coercion/hash/class_methods/to_date_spec.rb +10 -3
  105. data/spec/unit/virtus/coercion/hash/class_methods/to_datetime_spec.rb +10 -3
  106. data/spec/unit/virtus/coercion/hash/class_methods/to_time_spec.rb +10 -3
  107. data/spec/unit/virtus/coercion/object/class_methods/method_missing_spec.rb +8 -8
  108. data/spec/unit/virtus/coercion/string/class_methods/to_boolean_spec.rb +2 -2
  109. data/spec/unit/virtus/coercion/string/class_methods/to_constant_spec.rb +1 -1
  110. data/spec/unit/virtus/coercion/string/class_methods/to_date_spec.rb +1 -1
  111. data/spec/unit/virtus/coercion/string/class_methods/to_datetime_spec.rb +13 -13
  112. data/spec/unit/virtus/coercion/string/class_methods/to_decimal_spec.rb +25 -1
  113. data/spec/unit/virtus/coercion/string/class_methods/to_float_spec.rb +25 -1
  114. data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +30 -1
  115. data/spec/unit/virtus/coercion/string/class_methods/to_time_spec.rb +13 -13
  116. data/spec/unit/virtus/coercion/time/class_methods/to_time_spec.rb +10 -0
  117. data/spec/unit/virtus/coercion/true_class/class_methods/to_string_spec.rb +1 -1
  118. data/spec/unit/virtus/instance_methods/attributes_spec.rb +77 -20
  119. data/spec/unit/virtus/instance_methods/element_reference_spec.rb +1 -1
  120. data/spec/unit/virtus/instance_methods/element_set_spec.rb +2 -2
  121. data/spec/unit/virtus/instance_methods/to_hash_spec.rb +23 -0
  122. data/spec/unit/virtus/options/accept_options_spec.rb +10 -11
  123. data/spec/unit/virtus/options/accepted_options_spec.rb +1 -1
  124. data/spec/unit/virtus/options/options_spec.rb +27 -4
  125. data/tasks/metrics/ci.rake +2 -1
  126. data/tasks/metrics/heckle.rake +207 -0
  127. data/tasks/spec.rake +14 -7
  128. data/virtus.gemspec +1 -1
  129. metadata +111 -97
  130. data/VERSION +0 -1
  131. data/examples/custom_coercion_spec.rb +0 -50
  132. data/examples/default_values_spec.rb +0 -21
  133. data/examples/override_attribute_methods_spec.rb +0 -40
  134. data/spec/integration/virtus/attributes/attribute/set_spec.rb +0 -36
  135. data/spec/integration/virtus/class_methods/attribute_spec.rb +0 -82
  136. data/spec/integration/virtus/class_methods/attributes_spec.rb +0 -22
  137. data/spec/integration/virtus/class_methods/const_missing_spec.rb +0 -44
  138. data/spec/unit/shared/attribute.rb +0 -7
  139. data/spec/unit/shared/attribute/accept_options.rb +0 -37
  140. data/spec/unit/shared/attribute/accepted_options.rb +0 -5
  141. data/spec/unit/shared/attribute/get.rb +0 -44
  142. data/spec/unit/shared/attribute/inspect.rb +0 -7
  143. data/spec/unit/shared/attribute/set.rb +0 -37
  144. data/spec/unit/virtus/attribute/array_spec.rb +0 -24
  145. data/spec/unit/virtus/attribute/class_spec.rb +0 -24
  146. data/spec/unit/virtus/attribute/date_spec.rb +0 -59
  147. data/spec/unit/virtus/attribute/date_time_spec.rb +0 -87
  148. data/spec/unit/virtus/attribute/decimal_spec.rb +0 -109
  149. data/spec/unit/virtus/attribute/float_spec.rb +0 -109
  150. data/spec/unit/virtus/attribute/hash_spec.rb +0 -11
  151. data/spec/unit/virtus/attribute/integer_spec.rb +0 -99
  152. data/spec/unit/virtus/attribute/string_spec.rb +0 -21
  153. data/spec/unit/virtus/attribute/time_spec.rb +0 -82
  154. data/spec/unit/virtus/class_methods/new_spec.rb +0 -41
data/.gitignore CHANGED
@@ -1,11 +1,37 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## Rubinius
17
+ *.rbc
18
+ .rbx
19
+
20
+ ## PROJECT::GENERAL
21
+ *.gem
22
+ coverage
23
+ profiling
24
+ turbulence
25
+ rdoc
26
+ pkg
27
+ tmp
1
28
  doc
29
+ log
2
30
  .yardoc
3
- *.gem
31
+ measurements
32
+
33
+ ## BUNDLER
4
34
  .bundle
5
35
  Gemfile.lock
6
- pkg/*
7
- measurements/
8
- tmp/
9
- profiling/
10
- development
11
- .rbx/
36
+
37
+ ## PROJECT::SPECIFIC
@@ -6,80 +6,10 @@ rvm:
6
6
  - 1.9.3
7
7
  - ruby-head
8
8
  - ree
9
- - jruby
10
- - rbx
11
- env:
12
- -
13
- - JRUBY_OPTS="--1.8"
14
- - JRUBY_OPTS="--1.9"
15
- - RBXOPT="-X18"
16
- matrix:
17
- exclude:
18
- # exclude 1.8.7
19
- - rvm: 1.8.7
20
- env: JRUBY_OPTS="--1.8"
21
- - rvm: 1.8.7
22
- env: JRUBY_OPTS="--1.9"
23
- - rvm: 1.8.7
24
- env: RBXOPT="-X18"
25
- - rvm: 1.8.7
26
- env: RBXOPT="-X19"
27
-
28
- # exclude 1.9.2
29
- - rvm: 1.9.2
30
- env: JRUBY_OPTS="--1.8"
31
- - rvm: 1.9.2
32
- env: JRUBY_OPTS="--1.9"
33
- - rvm: 1.9.2
34
- env: RBXOPT="-X18"
35
- - rvm: 1.9.2
36
- env: RBXOPT="-X19"
37
-
38
- # exclude 1.9.3
39
- - rvm: 1.9.3
40
- env: JRUBY_OPTS="--1.8"
41
- - rvm: 1.9.3
42
- env: JRUBY_OPTS="--1.9"
43
- - rvm: 1.9.3
44
- env: RBXOPT="-X18"
45
- - rvm: 1.9.3
46
- env: RBXOPT="-X19"
47
-
48
- # exclude ruby-head
49
- - rvm: ruby-head
50
- env: JRUBY_OPTS="--1.8"
51
- - rvm: ruby-head
52
- env: JRUBY_OPTS="--1.9"
53
- - rvm: ruby-head
54
- env: RBXOPT="-X18"
55
- - rvm: ruby-head
56
- env: RBXOPT="-X19"
57
-
58
- # exclude ree
59
- - rvm: ree
60
- env: JRUBY_OPTS="--1.8"
61
- - rvm: ree
62
- env: JRUBY_OPTS="--1.9"
63
- - rvm: ree
64
- env: RBXOPT="-X18"
65
- - rvm: ree
66
- env: RBXOPT="-X19"
67
-
68
- # exclude jruby
69
- - rvm: jruby
70
- env:
71
- - rvm: jruby
72
- env: RBXOPT="-X18"
73
- - rvm: jruby
74
- env: RBXOPT="-X19"
75
-
76
- # exclude rbx
77
- - rvm: rbx
78
- env:
79
- - rvm: rbx
80
- env: JRUBY_OPTS="--1.8"
81
- - rvm: rbx
82
- env: JRUBY_OPTS="--1.9"
9
+ - jruby-18mode
10
+ - jruby-19mode
11
+ - rbx-18mode
12
+ # - rbx-19mode # FIXME: uncomment when rbx 1.9 is more stable
83
13
  notifications:
84
14
  email:
85
15
  - piotr.solnica@gmail.com
@@ -1,9 +1,18 @@
1
- # v0.0.10 to-be-released
1
+ # v0.1.0 to-be-released
2
+
3
+ [Compare v0.0.10..master](https://github.com/solnic/virtus/compare/v0.0.10...master)
4
+
5
+ * [feature] New EmbeddedValue attribute (solnic)
6
+ * [feature] Support for scientific notation handling in string => integer coercion (dkubb)
7
+ * [feature] Handling of string => numeric coercion with a leading + sign (dkubb)
8
+
9
+
10
+ # v0.0.10 2011-11-21
2
11
 
3
12
  * [fixed] Default values are now duped on evaluate (rclosner)
4
13
  * [fixed] Allow to override attribute mutator methods (senny)
5
14
 
6
- [Compare v0.0.9..master](https://github.com/solnic/virtus/compare/v0.0.9...master)
15
+ [Compare v0.0.9..v0.0.10](https://github.com/solnic/virtus/compare/v0.0.9...v0.0.10)
7
16
 
8
17
  # v0.0.9 2011-10-11
9
18
 
data/Gemfile CHANGED
@@ -3,17 +3,18 @@ source :rubygems
3
3
  gemspec
4
4
 
5
5
  group :metrics do
6
- gem 'flay', '~> 1.4.2'
7
- gem 'flog', '~> 2.5.1'
8
- gem 'reek', '~> 1.2.8', :git => 'git://github.com/dkubb/reek.git'
9
- gem 'roodi', '~> 2.1.0'
10
- gem 'yardstick', '~> 0.4.0'
11
- gem 'fattr'
12
- gem 'arrayfields'
13
- gem 'map'
6
+ gem 'fattr', '~> 2.2.0'
7
+ gem 'arrayfields', '~> 4.7.4'
8
+ gem 'flay', '~> 1.4.2'
9
+ gem 'flog', '~> 2.5.1'
10
+ gem 'map', '~> 5.2.0'
11
+ gem 'reek', '~> 1.2.8', :git => 'git://github.com/dkubb/reek.git'
12
+ gem 'roodi', '~> 2.1.0'
13
+ gem 'yardstick', '~> 0.4.0'
14
14
 
15
15
  platforms :mri_18 do
16
- gem 'json', '~> 1.5.3'
16
+ gem 'heckle', '~> 1.4.3'
17
+ gem 'json', '~> 1.6.4'
17
18
  gem 'metric_fu', '~> 2.1.1'
18
19
  gem 'mspec', '~> 1.5.17'
19
20
  gem 'rcov', '~> 0.9.9'
data/README.md CHANGED
@@ -23,9 +23,11 @@ or
23
23
  ``` ruby
24
24
  # ./Gemfile
25
25
 
26
- gem 'virtus', '0.0.7'
26
+ gem 'virtus'
27
27
  ```
28
28
 
29
+ *IMPORTANT*: If you are still using Ruby 1.8.7 then you also have to install backports gem!
30
+
29
31
  Examples
30
32
  --------
31
33
 
@@ -78,6 +80,86 @@ page.views
78
80
  # => 0
79
81
  ```
80
82
 
83
+ **Embedded Value**
84
+
85
+ ``` ruby
86
+ class City
87
+ include Virtus
88
+
89
+ attribute :name, String
90
+ end
91
+
92
+ class Address
93
+ include Virtus
94
+
95
+ attribute :street, String
96
+ attribute :zipcode, String
97
+ attribute :city, City
98
+ end
99
+
100
+ class User
101
+ include Virtus
102
+
103
+ attribute :name, String
104
+ attribute :address, Address
105
+ end
106
+
107
+ user = User.new(:address => {
108
+ :street => 'Street 1/2', :zipcode => '12345', :city => { :name => 'NYC' } })
109
+
110
+ user.address.street # => "Street 1/2"
111
+ user.address.city.name # => "NYC"
112
+ ```
113
+
114
+ **Collection Member Coercions**
115
+
116
+ ``` ruby
117
+ # Support "primitive" classes
118
+
119
+ class Book
120
+ include Virtus
121
+
122
+ attribute :page_numbers, Array[Integer]
123
+ end
124
+
125
+ book = Book.new(:page_numbers => %w[1 2 3])
126
+ book.page_numbers # => [1, 2, 3]
127
+
128
+ # Support EmbeddedValues, too!
129
+ class Address
130
+ include Virtus
131
+
132
+ attribute :address, String
133
+ attribute :locality, String
134
+ attribute :region, String
135
+ attribute :postal_code, String
136
+ end
137
+
138
+ class PhoneNumber
139
+ include Virtus
140
+
141
+ attribute :number, String
142
+ end
143
+
144
+ class User
145
+ include Virtus
146
+
147
+ attribute :phone_numbers, Array[PhoneNumber]
148
+ attribute :addresses, Set[Address]
149
+ end
150
+
151
+ user = User.new(
152
+ :phone_numbers => [
153
+ { :number => '212-555-1212' },
154
+ { :number => '919-444-3265' } ],
155
+ :addresses => [
156
+ { :address => '1234 Any St.', :locality => 'Anytown', :region => "DC", :postal_code => "21234" } ])
157
+
158
+ user.phone_numbers # => [#<PhoneNumber:0x007fdb2d3bef88 @number="212-555-1212">, #<PhoneNumber:0x007fdb2d3beb00 @number="919-444-3265">]
159
+
160
+ user.addresses # => #<Set: {#<Address:0x007fdb2d3be448 @address="1234 Any St.", @locality="Anytown", @region="DC", @postal_code="21234">}>
161
+ ```
162
+
81
163
  **Adding Coercions**
82
164
 
83
165
  Virtus comes with a builtin coercion library.
@@ -160,6 +242,8 @@ Credits
160
242
  * Dan Kubb ([dkubb](https://github.com/dkubb))
161
243
  * Chris Corbyn ([d11wtq](https://github.com/d11wtq))
162
244
  * Emmanuel Gomez ([emmanuel](https://github.com/emmanuel))
245
+ * Ryan Closner ([rclosner](https://github.com/rclosner))
246
+ * Yves Senn ([senny](https://github.com/senny))
163
247
 
164
248
 
165
249
  Contributing
data/TODO CHANGED
@@ -1,2 +1,20 @@
1
+ * Add missing specs:
2
+ * Add spec file spec/unit/virtus/coercion/time_coercions/to_date_spec.rb for Virtus::Coercion::TimeCoercions#to_date
3
+ * Add spec file spec/unit/virtus/coercion/time_coercions/to_time_spec.rb for Virtus::Coercion::TimeCoercions#to_time
4
+ * Add spec file spec/unit/virtus/coercion/time_coercions/to_string_spec.rb for Virtus::Coercion::TimeCoercions#to_string
5
+ * Add spec file spec/unit/virtus/coercion/time_coercions/to_datetime_spec.rb for Virtus::Coercion::TimeCoercions#to_datetime
6
+ * Add spec file spec/unit/virtus/coercion/decimal/class_methods/to_decimal_spec.rb for Virtus::Coercion::Decimal.to_decimal
7
+ * Add spec file spec/unit/virtus/coercion/float/class_methods/to_float_spec.rb for Virtus::Coercion::Float.to_float
8
+ * Add spec file spec/unit/virtus/coercion/integer/class_methods/to_integer_spec.rb for Virtus::Coercion::Integer.to_integer
9
+ * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_float_spec.rb for Virtus::Coercion::Numeric.to_float
10
+ * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_integer_spec.rb for Virtus::Coercion::Numeric.to_integer
11
+ * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_string_spec.rb for Virtus::Coercion::Numeric.to_string
12
+ * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_decimal_spec.rb for Virtus::Coercion::Numeric.to_decimal
13
+ * Add spec file spec/unit/virtus/coercion/class_methods/element_reference_spec.rb for Virtus::Coercion.[]
14
+ * Add spec file spec/unit/virtus/coercion/class_methods/primitive_spec.rb for Virtus::Coercion.primitive
15
+ * Add spec file spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb for Virtus::AttributesAccessor#define_writer_method
16
+ * Add spec file spec/unit/virtus/attributes_accessor/inspect_spec.rb for Virtus::AttributesAccessor#inspect
17
+ * Add spec file spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb for Virtus::AttributesAccessor#define_reader_method
18
+
1
19
  * Make #to_time #to_date and #to_datetime work on Ruby 1.8.7 instead of typecasting to string and parsing the value
2
20
  * Add support for defining attributes on Modules
@@ -1,3 +1,3 @@
1
1
  ---
2
- threshold: 20
3
- total_score: 257
2
+ threshold: 19
3
+ total_score: 322
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 15.9
2
+ threshold: 16.8
@@ -1,8 +1,8 @@
1
1
  ---
2
- AbcMetricMethodCheck: { score: 10.1 }
2
+ AbcMetricMethodCheck: { score: 12.1 }
3
3
  AssignmentInConditionalCheck: { }
4
4
  CaseMissingElseCheck: { }
5
- ClassLineCountCheck: { line_count: 309 }
5
+ ClassLineCountCheck: { line_count: 319 }
6
6
  ClassNameCheck: { pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/ }
7
7
  ClassVariableCheck: { }
8
8
  CyclomaticComplexityBlockCheck: { complexity: 4 }
@@ -11,7 +11,7 @@ EmptyRescueBodyCheck: { }
11
11
  ForLoopCheck: { }
12
12
  MethodLineCountCheck: { line_count: 9 }
13
13
  MethodNameCheck: { pattern: !ruby/regexp /\A(?:[a-z\d](?:_?[a-z\d])+[?!=]?|\[\]=?|==|<=>|<<|[+*&|-])\z/ }
14
- ModuleLineCountCheck: { line_count: 315 }
14
+ ModuleLineCountCheck: { line_count: 325 }
15
15
  ModuleNameCheck: { pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/ }
16
16
  # TODO: decrease parameter_count to 2 or less
17
17
  ParameterNumberCheck: { parameter_count: 3 }
@@ -8,10 +8,10 @@ UncommunicativeParameterName:
8
8
  - !ruby/regexp /[0-9]$/
9
9
  - !ruby/regexp /[A-Z]/
10
10
  LargeClass:
11
- max_methods: 11
11
+ max_methods: 14
12
12
  exclude: []
13
13
  enabled: true
14
- max_instance_variables: 7
14
+ max_instance_variables: 8
15
15
  UncommunicativeMethodName:
16
16
  accept: []
17
17
  exclude: []
@@ -27,6 +27,8 @@ LongParameterList:
27
27
  overrides: {}
28
28
  FeatureEnvy:
29
29
  exclude: [
30
+ Virtus::Attribute::Boolean#value_coerced?,
31
+ Virtus::ClassMethods#build_attribute,
30
32
  Virtus::Coercion::TimeCoercions#to_string,
31
33
  Virtus::Coercion::TimeCoercions#coerce_with_method,
32
34
  Virtus::TypeLookup#determine_type_from_primitive
@@ -66,7 +68,10 @@ Duplication:
66
68
  max_calls: 1
67
69
  UtilityFunction:
68
70
  max_helper_calls: 1
69
- exclude: []
71
+ exclude: [
72
+ Virtus::Attribute::Boolean#value_coerced?,
73
+ Virtus::ClassMethods#build_attribute
74
+ ]
70
75
  enabled: true
71
76
  Attribute:
72
77
  exclude: []
@@ -31,6 +31,7 @@ require 'virtus/support/descendants_tracker'
31
31
  require 'virtus/support/type_lookup'
32
32
  require 'virtus/support/options'
33
33
 
34
+ require 'virtus/attributes_accessor'
34
35
  require 'virtus/class_methods'
35
36
  require 'virtus/instance_methods'
36
37
 
@@ -45,6 +46,7 @@ require 'virtus/coercion/decimal'
45
46
  require 'virtus/coercion/false_class'
46
47
  require 'virtus/coercion/true_class'
47
48
  require 'virtus/coercion/hash'
49
+ require 'virtus/coercion/array'
48
50
  require 'virtus/coercion/time_coercions'
49
51
  require 'virtus/coercion/date'
50
52
  require 'virtus/coercion/date_time'
@@ -56,7 +58,9 @@ require 'virtus/attribute/default_value'
56
58
  require 'virtus/attribute'
57
59
  require 'virtus/attribute/object'
58
60
  require 'virtus/attribute/class'
61
+ require 'virtus/attribute/collection'
59
62
  require 'virtus/attribute/array'
63
+ require 'virtus/attribute/set'
60
64
  require 'virtus/attribute/boolean'
61
65
  require 'virtus/attribute/date'
62
66
  require 'virtus/attribute/date_time'
@@ -67,3 +71,4 @@ require 'virtus/attribute/hash'
67
71
  require 'virtus/attribute/integer'
68
72
  require 'virtus/attribute/string'
69
73
  require 'virtus/attribute/time'
74
+ require 'virtus/attribute/embedded_value'
@@ -8,6 +8,11 @@ module Virtus
8
8
  extend TypeLookup
9
9
  extend Options
10
10
 
11
+ accept_options :primitive, :accessor, :reader,
12
+ :writer, :coercion_method, :default
13
+
14
+ accessor :public
15
+
11
16
  # Returns name of the attribute
12
17
  #
13
18
  # @example
@@ -27,7 +32,7 @@ module Virtus
27
32
 
28
33
  # Returns instance variable name of the attribute
29
34
  #
30
- # @return [String]
35
+ # @return [Symbol]
31
36
  #
32
37
  # @api private
33
38
  attr_reader :instance_variable_name
@@ -60,16 +65,75 @@ module Virtus
60
65
  # @api private
61
66
  attr_reader :default
62
67
 
63
- DEFAULT_ACCESSOR = :public
68
+ # Builds an attribute instance
69
+ #
70
+ # @param [Symbol] name
71
+ # the name of an attribute
72
+ #
73
+ # @param [Class] type
74
+ # the type class of an attribute
75
+ #
76
+ # @param [#to_hash] options
77
+ # the extra options hash
78
+ #
79
+ # @return [Attribute]
80
+ #
81
+ # @api private
82
+ def self.build(name, type, options = {})
83
+ attribute_class = determine_type(type) or
84
+ raise ArgumentError, "#{type.inspect} does not map to an attribute type"
85
+ attribute_options = attribute_class.merge_options(type, options)
86
+ attribute_class.new(name, attribute_options)
87
+ end
64
88
 
65
- OPTIONS = [ :primitive, :accessor, :reader,
66
- :writer, :coercion_method, :default ].freeze
89
+ # Determine attribute type based on class or name
90
+ #
91
+ # Returns Attribute::EmbeddedValue if a virtus class is passed
92
+ #
93
+ # @example
94
+ # address_class = Class.new { include Virtus }
95
+ # Virtus::Attribute.determine_type(address_class) # => Virtus::Attribute::EmbeddedValue
96
+ #
97
+ # @see Virtus::Support::TypeLookup.determine_type
98
+ #
99
+ # @return [Class]
100
+ #
101
+ # @api public
102
+ def self.determine_type(class_or_name)
103
+ case class_or_name
104
+ when ::Class
105
+ if class_or_name <= Virtus
106
+ Attribute::EmbeddedValue
107
+ else
108
+ super
109
+ end
110
+ when ::Array, ::Set
111
+ super(class_or_name.class)
112
+ else
113
+ super
114
+ end
115
+ end
67
116
 
68
- accept_options *OPTIONS
117
+ # A hook for Attributes to update options based on the type from the caller
118
+ #
119
+ # @param [Object] type
120
+ # The raw type, typically given by the caller of ClassMethods#attribute
121
+ # @param [Hash] options
122
+ # Attribute configuration options
123
+ #
124
+ # @return [Hash]
125
+ # New Hash instance, potentially updated with information from the args
126
+ #
127
+ # @api private
128
+ #
129
+ # @todo add type arg to Attribute#initialize signature and handle there?
130
+ def self.merge_options(type, options)
131
+ options
132
+ end
69
133
 
70
134
  # Initializes an attribute instance
71
135
  #
72
- # @param [Symbol] name
136
+ # @param [#to_sym] name
73
137
  # the name of an attribute
74
138
  #
75
139
  # @param [#to_hash] options
@@ -79,14 +143,13 @@ module Virtus
79
143
  #
80
144
  # @api private
81
145
  def initialize(name, options = {})
82
- @name = name
83
- @options = self.class.options.merge(options.to_hash).freeze
84
-
85
- @instance_variable_name = "@#{@name}".freeze
146
+ @name = name.to_sym
147
+ @options = self.class.options.merge(options).freeze
148
+ @instance_variable_name = "@#{@name}".to_sym
149
+ @primitive = @options.fetch(:primitive)
86
150
  @coercion_method = @options.fetch(:coercion_method)
87
151
  @default = DefaultValue.new(self, @options[:default])
88
-
89
- set_visibility
152
+ initialize_visibility
90
153
  end
91
154
 
92
155
  # Returns a concise string representation of the attribute instance
@@ -99,7 +162,7 @@ module Virtus
99
162
  #
100
163
  # @api public
101
164
  def inspect
102
- "#<#{self.class.name} @name=#{name.inspect}>"
165
+ "#<#{self.class.inspect} @name=#{name.inspect}>"
103
166
  end
104
167
 
105
168
  # Returns value of an attribute for the given instance
@@ -118,7 +181,9 @@ module Virtus
118
181
  if instance.instance_variable_defined?(instance_variable_name)
119
182
  get!(instance)
120
183
  else
121
- set!(instance, default.evaluate(instance))
184
+ value = default.evaluate(instance)
185
+ set!(instance, value)
186
+ value
122
187
  end
123
188
  end
124
189
 
@@ -157,6 +222,7 @@ module Virtus
157
222
  # @api public
158
223
  def set!(instance, value)
159
224
  instance.instance_variable_set(instance_variable_name, value)
225
+ self
160
226
  end
161
227
 
162
228
  # Converts the given value to the primitive type
@@ -175,6 +241,39 @@ module Virtus
175
241
  Coercion[value.class].send(coercion_method, value)
176
242
  end
177
243
 
244
+ # Is the given value coerced into the target type for this attribute?
245
+ #
246
+ # @example
247
+ # string_attribute = Virtus::Attribute::String.new(:str)
248
+ # string_attribute.value_coerced?('foo') # => true
249
+ # string_attribute.value_coerced?(:foo) # => false
250
+ # integer_attribute = Virtus::Attribute::Integer.new(:integer)
251
+ # integer_attribute.value_coerced?(5) # => true
252
+ # integer_attribute.value_coerced?('5') # => false
253
+ # date_attribute = Virtus::Attribute::Date.new(:date)
254
+ # date_attribute.value_coerced?('2011-12-31') # => false
255
+ # date_attribute.value_coerced?(Date.today) # => true
256
+ #
257
+ # @return [Boolean]
258
+ #
259
+ # @api private
260
+ def value_coerced?(value)
261
+ @primitive === value
262
+ end
263
+
264
+ # Define reader and writer methods for an Attribute
265
+ #
266
+ # @param [Attribute] attribute
267
+ #
268
+ # @return [self]
269
+ #
270
+ # @api private
271
+ def define_accessor_methods(mod)
272
+ define_reader_method(mod)
273
+ define_writer_method(mod)
274
+ self
275
+ end
276
+
178
277
  # Creates an attribute reader method
179
278
  #
180
279
  # @param [Module] mod
@@ -183,12 +282,7 @@ module Virtus
183
282
  #
184
283
  # @api private
185
284
  def define_reader_method(mod)
186
- reader_method_name = name
187
- attribute = self
188
-
189
- mod.send(:define_method, reader_method_name) { attribute.get(self) }
190
- mod.send(reader_visibility, reader_method_name)
191
-
285
+ mod.define_reader_method(self, name, reader_visibility)
192
286
  self
193
287
  end
194
288
 
@@ -200,26 +294,39 @@ module Virtus
200
294
  #
201
295
  # @api private
202
296
  def define_writer_method(mod)
203
- writer_method_name = "#{name}="
204
- attribute = self
297
+ mod.define_writer_method(self, "#{name}=".to_sym, writer_visibility)
298
+ self
299
+ end
205
300
 
206
- mod.send(:define_method, writer_method_name) { |value| attribute.set(self, value) }
207
- mod.send(writer_visibility, writer_method_name)
301
+ # Returns a Boolean indicating whether the reader method is public
302
+ #
303
+ # @return [Boolean]
304
+ #
305
+ # @api private
306
+ def public_reader?
307
+ reader_visibility == :public
308
+ end
208
309
 
209
- self
310
+ # Returns a Boolean indicating whether the writer method is public
311
+ #
312
+ # @return [Boolean]
313
+ #
314
+ # @api private
315
+ def public_writer?
316
+ writer_visibility == :public
210
317
  end
211
318
 
212
319
  private
213
320
 
214
- # Sets visibility of reader/write methods based on the options hash
321
+ # Initialize visibility of reader/write methods based on the options hash
215
322
  #
216
323
  # @return [undefined]
217
324
  #
218
325
  # @api private
219
- def set_visibility
220
- default_accessor = @options.fetch(:accessor, self.class::DEFAULT_ACCESSOR)
221
- @reader_visibility = @options.fetch(:reader, default_accessor)
222
- @writer_visibility = @options.fetch(:writer, default_accessor)
326
+ def initialize_visibility
327
+ default_accessor = @options.fetch(:accessor)
328
+ @reader_visibility = @options.fetch(:reader, default_accessor)
329
+ @writer_visibility = @options.fetch(:writer, default_accessor)
223
330
  end
224
331
 
225
332
  end # class Attribute