virtus 0.0.9 → 0.0.10
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.
- data/.gitignore +11 -0
- data/.travis.yml +0 -1
- data/{History.md → Changelog.md} +10 -3
- data/Gemfile +4 -6
- data/README.md +140 -81
- data/Rakefile +1 -15
- data/config/flay.yml +1 -1
- data/config/roodi.yml +1 -1
- data/config/site.reek +8 -2
- data/examples/custom_coercion_spec.rb +2 -2
- data/examples/override_attribute_methods_spec.rb +40 -0
- data/lib/virtus.rb +1 -0
- data/lib/virtus/attribute/default_value.rb +17 -3
- data/lib/virtus/class_methods.rb +6 -3
- data/lib/virtus/support/type_lookup.rb +3 -4
- data/lib/virtus/version.rb +3 -0
- data/spec/unit/virtus/attribute/default_value/class_methods/new_spec.rb +3 -6
- data/spec/unit/virtus/attribute/default_value/instance_methods/evaluate_spec.rb +10 -2
- data/tasks/metrics/ci.rake +2 -3
- data/tasks/spec.rake +19 -7
- data/virtus.gemspec +17 -186
- metadata +80 -48
- data/tasks/metrics/heckle.rake +0 -261
data/.gitignore
ADDED
data/.travis.yml
CHANGED
data/{History.md → Changelog.md}
RENAMED
@@ -1,15 +1,22 @@
|
|
1
|
-
# v0.0.
|
1
|
+
# v0.0.10 to-be-released
|
2
|
+
|
3
|
+
* [fixed] Default values are now duped on evaluate (rclosner)
|
4
|
+
* [fixed] Allow to override attribute mutator methods (senny)
|
5
|
+
|
6
|
+
[Compare v0.0.9..master](https://github.com/solnic/virtus/compare/v0.0.9...master)
|
7
|
+
|
8
|
+
# v0.0.9 2011-10-11
|
2
9
|
|
3
10
|
* [fixed] Fix in type lookup for anonymous classes (dkubb)
|
4
11
|
|
5
|
-
[Compare v0.0.8..
|
12
|
+
[Compare v0.0.8..v0.0.9](https://github.com/solnic/virtus/compare/v0.0.8...v0.0.9)
|
6
13
|
|
7
14
|
# v0.0.8 2011-08-25
|
8
15
|
|
9
16
|
* [fixed] Fixed conflict with ActiveModel (RichGuk)
|
10
17
|
* [changed] Renamed Coercion::String.to_class => Coercion::String.to_constant (emmanuel)
|
11
18
|
|
12
|
-
[Compare v0.0.7..
|
19
|
+
[Compare v0.0.7..v0.0.8](https://github.com/solnic/virtus/compare/v0.0.7...v0.0.8)
|
13
20
|
|
14
21
|
# v0.0.7 2011-07-31
|
15
22
|
|
data/Gemfile
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
source :rubygems
|
2
2
|
|
3
|
-
|
4
|
-
gem 'backports', '~> 2.3.0'
|
5
|
-
gem 'jeweler', '~> 1.6.4'
|
6
|
-
gem 'rspec', '~> 2.6.0'
|
7
|
-
end
|
3
|
+
gemspec
|
8
4
|
|
9
5
|
group :metrics do
|
10
6
|
gem 'flay', '~> 1.4.2'
|
@@ -12,9 +8,11 @@ group :metrics do
|
|
12
8
|
gem 'reek', '~> 1.2.8', :git => 'git://github.com/dkubb/reek.git'
|
13
9
|
gem 'roodi', '~> 2.1.0'
|
14
10
|
gem 'yardstick', '~> 0.4.0'
|
11
|
+
gem 'fattr'
|
12
|
+
gem 'arrayfields'
|
13
|
+
gem 'map'
|
15
14
|
|
16
15
|
platforms :mri_18 do
|
17
|
-
gem 'heckle', '~> 1.4.3'
|
18
16
|
gem 'json', '~> 1.5.3'
|
19
17
|
gem 'metric_fu', '~> 2.1.1'
|
20
18
|
gem 'mspec', '~> 1.5.17'
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
virtus
|
2
|
+
======
|
3
|
+
|
4
|
+
[](http://travis-ci.org/solnic/virtus)
|
2
5
|
|
3
6
|
This is a partial extraction of the DataMapper [Property
|
4
7
|
API](http://rubydoc.info/github/datamapper/dm-core/master/DataMapper/Property)
|
@@ -8,123 +11,159 @@ reinventing the wheel all over again. It is also suitable for any other
|
|
8
11
|
usecase where you need to extend your ruby objects with attributes that require
|
9
12
|
data type coercions.
|
10
13
|
|
11
|
-
|
14
|
+
Installation
|
15
|
+
------------
|
12
16
|
|
13
|
-
|
17
|
+
``` terminal
|
18
|
+
$ gem install virtus
|
19
|
+
```
|
14
20
|
|
15
|
-
|
21
|
+
or
|
16
22
|
|
17
|
-
|
23
|
+
``` ruby
|
24
|
+
# ./Gemfile
|
18
25
|
|
19
|
-
|
20
|
-
|
26
|
+
gem 'virtus', '0.0.7'
|
27
|
+
```
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
attribute :birthday, DateTime
|
25
|
-
end
|
29
|
+
Examples
|
30
|
+
--------
|
26
31
|
|
27
|
-
# setting attributes in the constructor
|
28
|
-
user = User.new(:name => 'Piotr', :age => 28)
|
29
32
|
|
30
|
-
|
31
|
-
|
33
|
+
``` ruby
|
34
|
+
require 'virtus'
|
32
35
|
|
33
|
-
|
34
|
-
|
36
|
+
class User
|
37
|
+
include Virtus
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
attribute :name, String
|
40
|
+
attribute :age, Integer
|
41
|
+
attribute :birthday, DateTime
|
42
|
+
end
|
39
43
|
|
40
|
-
|
41
|
-
|
44
|
+
user = User.new :name => 'Piotr', :age => 28
|
45
|
+
user.attributes
|
46
|
+
# => { :name => "Piotr", :age => 28 }
|
42
47
|
|
43
|
-
|
48
|
+
user.name
|
49
|
+
# => "Piotr"
|
44
50
|
|
45
|
-
|
51
|
+
user.age = '28'
|
52
|
+
# => 28
|
53
|
+
user.age.class
|
54
|
+
# => Fixnum
|
46
55
|
|
47
|
-
|
48
|
-
|
56
|
+
user.birthday = 'November 18th, 1983'
|
57
|
+
# => #<DateTime: 1983-11-18T00:00:00+00:00 (4891313/2,0/1,2299161)>
|
58
|
+
```
|
49
59
|
|
50
|
-
attribute :title, String
|
51
|
-
attribute :slug, String, :default => lambda { |post, attribute| post.title.downcase.gsub(' ', '-') }
|
52
|
-
attribute :view_count, Integer, :default => 0
|
53
|
-
end
|
54
60
|
|
55
|
-
|
56
|
-
page.slug # => 'virtus-is-awesome'
|
57
|
-
page.view_count # => 0
|
61
|
+
**Default values**
|
58
62
|
|
59
|
-
|
63
|
+
``` ruby
|
64
|
+
require 'virtus'
|
60
65
|
|
61
|
-
|
62
|
-
|
66
|
+
class Page
|
67
|
+
include Virtus
|
63
68
|
|
64
|
-
|
65
|
-
|
69
|
+
attribute :title, String
|
70
|
+
attribute :views, Integer, :default => 0
|
71
|
+
attribute :slug, String, :default => lambda { |page, attribute| page.title.downcase.gsub(' ', '-') }
|
72
|
+
end
|
66
73
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
74
|
+
page = Page.new :title => 'Virtus Is Awesome'
|
75
|
+
page.slug
|
76
|
+
# => 'virtus-is-awesome'
|
77
|
+
page.views
|
78
|
+
# => 0
|
79
|
+
```
|
71
80
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
+
**Adding Coercions**
|
82
|
+
|
83
|
+
Virtus comes with a builtin coercion library.
|
84
|
+
It's super easy to add your own coercion classes.
|
85
|
+
Take a look:
|
86
|
+
|
87
|
+
``` ruby
|
88
|
+
require 'virtus'
|
89
|
+
require 'digest/md5'
|
81
90
|
|
82
|
-
|
83
|
-
|
91
|
+
# Our new attribute type
|
92
|
+
class MD5 < Virtus::Attribute::Object
|
93
|
+
primitive String
|
94
|
+
coercion_method :to_md5
|
95
|
+
end
|
84
96
|
|
85
|
-
|
86
|
-
|
97
|
+
# Defining the Coercion method
|
98
|
+
module Virtus
|
99
|
+
class Coercion
|
100
|
+
class String < Virtus::Coercion::Object
|
101
|
+
def self.to_md5(value)
|
102
|
+
Digest::MD5.hexdigest value
|
103
|
+
end
|
87
104
|
end
|
105
|
+
end
|
106
|
+
end
|
88
107
|
|
89
|
-
|
90
|
-
|
91
|
-
|
108
|
+
# And now the user!
|
109
|
+
class User
|
110
|
+
include Virtus
|
92
111
|
|
93
|
-
|
112
|
+
attribute :name, String
|
113
|
+
attribute :password, MD5
|
114
|
+
end
|
94
115
|
|
95
|
-
|
96
|
-
|
116
|
+
user = User.new :name => 'Piotr', :password => 'foobar'
|
117
|
+
user.name
|
118
|
+
# => 'Piotr'
|
119
|
+
user.password
|
120
|
+
# => '3858f62230ac3c915f300c664312c63f'
|
121
|
+
```
|
97
122
|
|
98
|
-
|
99
|
-
module Attributes
|
100
|
-
class JSON < Virtus::Attribute::Object
|
101
|
-
primitive Hash
|
123
|
+
**Custom Attributes**
|
102
124
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
107
|
-
end
|
125
|
+
``` ruby
|
126
|
+
require 'virtus'
|
127
|
+
require 'json'
|
108
128
|
|
109
|
-
|
110
|
-
include Virtus
|
129
|
+
module MyAppClass
|
111
130
|
|
112
|
-
|
131
|
+
# Defining the custom attribute(s)
|
132
|
+
module Attributes
|
133
|
+
class JSON < Virtus::Attribute::Object
|
134
|
+
primitive Hash
|
135
|
+
|
136
|
+
def coerce(value)
|
137
|
+
::JSON.parse value
|
113
138
|
end
|
114
139
|
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class User
|
143
|
+
include Virtus
|
115
144
|
|
116
|
-
|
145
|
+
attribute :info, Attributes::JSON
|
146
|
+
end
|
147
|
+
end
|
117
148
|
|
118
|
-
|
119
|
-
|
149
|
+
user = MyApp::User.new
|
150
|
+
user.info = '{"email":"john@domain.com"}'
|
151
|
+
# => {"email"=>"john@domain.com"}
|
152
|
+
user.info.class
|
153
|
+
# => Hash
|
154
|
+
```
|
120
155
|
|
121
|
-
|
156
|
+
|
157
|
+
Credits
|
158
|
+
-------
|
122
159
|
|
123
160
|
* Dan Kubb ([dkubb](https://github.com/dkubb))
|
124
161
|
* Chris Corbyn ([d11wtq](https://github.com/d11wtq))
|
125
162
|
* Emmanuel Gomez ([emmanuel](https://github.com/emmanuel))
|
126
163
|
|
127
|
-
|
164
|
+
|
165
|
+
Contributing
|
166
|
+
-------------
|
128
167
|
|
129
168
|
* Fork the project.
|
130
169
|
* Make your feature addition or bug fix.
|
@@ -134,6 +173,26 @@ coercion classes. Take a look:
|
|
134
173
|
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
135
174
|
* Send me a pull request. Bonus points for topic branches.
|
136
175
|
|
137
|
-
|
138
|
-
|
139
|
-
|
176
|
+
License
|
177
|
+
-------
|
178
|
+
|
179
|
+
Copyright (c) 2011 Piotr Solnica
|
180
|
+
|
181
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
182
|
+
a copy of this software and associated documentation files (the
|
183
|
+
"Software"), to deal in the Software without restriction, including
|
184
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
185
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
186
|
+
permit persons to whom the Software is furnished to do so, subject to
|
187
|
+
the following conditions:
|
188
|
+
|
189
|
+
The above copyright notice and this permission notice shall be
|
190
|
+
included in all copies or substantial portions of the Software.
|
191
|
+
|
192
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
193
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
194
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
195
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
196
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
197
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
198
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,21 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
-
require 'jeweler'
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = 'virtus'
|
8
|
-
gem.platform = Gem::Platform::RUBY
|
9
|
-
gem.authors = [ 'Piotr Solnica' ]
|
10
|
-
gem.email = [ 'piotr@rubyverse.com' ]
|
11
|
-
gem.homepage = 'https://github.com/solnic/virtus'
|
12
|
-
gem.summary = 'Attributes for your plain ruby objects'
|
13
|
-
gem.description = gem.summary
|
14
|
-
end
|
15
|
-
|
16
|
-
Jeweler::GemcutterTasks.new
|
17
3
|
|
18
4
|
FileList['tasks/**/*.rake'].each { |task| import task }
|
19
5
|
|
20
|
-
desc 'Default: run specs
|
6
|
+
desc 'Default: run all specs'
|
21
7
|
task :default => :spec
|
data/config/flay.yml
CHANGED
data/config/roodi.yml
CHANGED
@@ -5,7 +5,7 @@ CaseMissingElseCheck: { }
|
|
5
5
|
ClassLineCountCheck: { line_count: 309 }
|
6
6
|
ClassNameCheck: { pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/ }
|
7
7
|
ClassVariableCheck: { }
|
8
|
-
CyclomaticComplexityBlockCheck: { complexity:
|
8
|
+
CyclomaticComplexityBlockCheck: { complexity: 4 }
|
9
9
|
CyclomaticComplexityMethodCheck: { complexity: 4 }
|
10
10
|
EmptyRescueBodyCheck: { }
|
11
11
|
ForLoopCheck: { }
|
data/config/site.reek
CHANGED
@@ -26,7 +26,11 @@ LongParameterList:
|
|
26
26
|
enabled: true
|
27
27
|
overrides: {}
|
28
28
|
FeatureEnvy:
|
29
|
-
exclude: [
|
29
|
+
exclude: [
|
30
|
+
Virtus::Coercion::TimeCoercions#to_string,
|
31
|
+
Virtus::Coercion::TimeCoercions#coerce_with_method,
|
32
|
+
Virtus::TypeLookup#determine_type_from_primitive
|
33
|
+
]
|
30
34
|
enabled: true
|
31
35
|
ClassVariable:
|
32
36
|
exclude: []
|
@@ -55,7 +59,9 @@ LongMethod:
|
|
55
59
|
enabled: true
|
56
60
|
Duplication:
|
57
61
|
allow_calls: []
|
58
|
-
exclude: [
|
62
|
+
exclude: [
|
63
|
+
Virtus::Attribute::DefaultValue#evaluate
|
64
|
+
]
|
59
65
|
enabled: true
|
60
66
|
max_calls: 1
|
61
67
|
UtilityFunction:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require './spec/spec_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class Md5 < Virtus::Attribute::Object
|
4
4
|
primitive String
|
5
5
|
coercion_method :to_md5
|
6
6
|
end
|
@@ -19,7 +19,7 @@ class User
|
|
19
19
|
include Virtus
|
20
20
|
|
21
21
|
attribute :name, String
|
22
|
-
attribute :password,
|
22
|
+
attribute :password, Md5
|
23
23
|
end
|
24
24
|
|
25
25
|
describe User do
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
|
3
|
+
class Article
|
4
|
+
include Virtus
|
5
|
+
|
6
|
+
attribute :author, String
|
7
|
+
|
8
|
+
def author
|
9
|
+
super || '<unknown>'
|
10
|
+
end
|
11
|
+
|
12
|
+
def author=(name)
|
13
|
+
super unless name == 'Brad'
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Article, 'override' do
|
19
|
+
|
20
|
+
it 'Alice is an allowed author' do
|
21
|
+
Article.new(:author => 'Alice').author.should == 'Alice'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'Brad isn not an allowed author' do
|
25
|
+
Article.new(:author => 'Brad').author.should == '<unknown>'
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with author' do
|
29
|
+
subject { Article.new(:author => 'John') }
|
30
|
+
|
31
|
+
its(:author) { should == 'John' }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'without author' do
|
35
|
+
subject { Article.new }
|
36
|
+
|
37
|
+
its(:author) { should == '<unknown>' }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|