virtus 0.0.10 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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