virtus 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -1
- data/Changelog.md +10 -0
- data/README.md +1 -2
- data/config/flay.yml +1 -1
- data/config/site.reek +2 -1
- data/lib/virtus.rb +0 -1
- data/lib/virtus/attribute.rb +4 -3
- data/lib/virtus/attribute/boolean.rb +1 -1
- data/lib/virtus/attribute/embedded_value.rb +1 -1
- data/lib/virtus/attribute_set.rb +32 -1
- data/lib/virtus/class_inclusions.rb +1 -1
- data/lib/virtus/class_methods.rb +15 -23
- data/lib/virtus/coercion/string.rb +8 -4
- data/lib/virtus/extensions.rb +5 -6
- data/lib/virtus/instance_methods.rb +43 -2
- data/lib/virtus/module_extensions.rb +10 -2
- data/lib/virtus/value_object.rb +4 -3
- data/lib/virtus/version.rb +1 -1
- data/spec/integration/using_modules_spec.rb +6 -2
- data/spec/integration/virtus/value_object_spec.rb +1 -1
- data/spec/shared/freeze_method_behavior.rb +37 -0
- data/spec/unit/virtus/attribute/class_methods/build_spec.rb +10 -0
- data/spec/unit/virtus/attribute_set/append_spec.rb +2 -0
- data/spec/unit/virtus/{attributes_accessor → attribute_set}/define_reader_method_spec.rb +2 -2
- data/spec/unit/virtus/{attributes_accessor → attribute_set}/define_writer_method_spec.rb +2 -2
- data/spec/unit/virtus/{attributes_accessor → attribute_set}/inspect_spec.rb +2 -2
- data/spec/unit/virtus/attribute_set/merge_spec.rb +2 -0
- data/spec/unit/virtus/attribute_set/reset_spec.rb +4 -0
- data/spec/unit/virtus/class_methods/attribute_spec.rb +1 -0
- data/spec/unit/virtus/class_methods/attributes_spec.rb +22 -0
- data/spec/unit/virtus/class_methods/inherited_spec.rb +3 -3
- data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +6 -0
- data/spec/unit/virtus/instance_methods/freeze_spec.rb +64 -0
- data/spec/unit/virtus/module_extensions/attribute_spec.rb +31 -0
- data/spec/unit/virtus/value_object/class_methods/attribute_spec.rb +37 -10
- data/spec/unit/virtus/value_object/instance_methods/clone_spec.rb +21 -0
- data/tasks/metrics/heckle.rake +4 -3
- metadata +13 -10
- data/lib/virtus/attributes_accessor.rb +0 -68
- data/spec/unit/virtus/value_object/instance_methods/duplicates_spec.rb +0 -22
data/.travis.yml
CHANGED
data/Changelog.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# v0.5.2 2012-09-01
|
2
|
+
|
3
|
+
* [feature] Object is now the default attribute type (dkubb)
|
4
|
+
* [fixed] Fix module inclusion problems (dkubb)
|
5
|
+
* [fixed] Evaluate default values when freezing an object (mbj)
|
6
|
+
* [fixed] String representation of a big integer is now properly coerced to an integer (greyblake)
|
7
|
+
* [changed] AttributeSet is now a module responsible for defining attribute methods (emmanuel)
|
8
|
+
|
9
|
+
[Compare v0.5.1..v0.5.2](https://github.com/solnic/virtus/compare/v0.5.1...v0.5.2)
|
10
|
+
|
1
11
|
# v0.5.1 2012-06-11
|
2
12
|
|
3
13
|
* [fixed] EV properly handle nil as the value (solnic)
|
data/README.md
CHANGED
@@ -3,8 +3,7 @@ virtus
|
|
3
3
|
|
4
4
|
[![Build Status](https://secure.travis-ci.org/solnic/virtus.png)](http://travis-ci.org/solnic/virtus)
|
5
5
|
[![Dependency Status](https://gemnasium.com/solnic/virtus.png)](https://gemnasium.com/solnic/virtus)
|
6
|
-
|
7
|
-
[Metrics on CodeClimate](https://codeclimate.com/github/solnic/virtus)
|
6
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/solnic/virtus)
|
8
7
|
|
9
8
|
This is a partial extraction of the DataMapper [Property
|
10
9
|
API](http://rubydoc.info/github/datamapper/dm-core/master/DataMapper/Property)
|
data/config/flay.yml
CHANGED
data/config/site.reek
CHANGED
@@ -31,7 +31,8 @@ FeatureEnvy:
|
|
31
31
|
Virtus::ClassMethods#build_attribute,
|
32
32
|
Virtus::Coercion::TimeCoercions#to_string,
|
33
33
|
Virtus::Coercion::TimeCoercions#coerce_with_method,
|
34
|
-
Virtus::TypeLookup#determine_type_from_primitive
|
34
|
+
Virtus::TypeLookup#determine_type_from_primitive,
|
35
|
+
Virtus::ValueObject::ClassMethods#attribute
|
35
36
|
]
|
36
37
|
enabled: true
|
37
38
|
ClassVariable:
|
data/lib/virtus.rb
CHANGED
data/lib/virtus/attribute.rb
CHANGED
@@ -7,6 +7,7 @@ module Virtus
|
|
7
7
|
extend DescendantsTracker
|
8
8
|
extend TypeLookup
|
9
9
|
extend Options
|
10
|
+
include Equalizer.new(inspect) << :name << :options
|
10
11
|
|
11
12
|
accept_options :primitive, :accessor, :reader,
|
12
13
|
:writer, :coercion_method, :default
|
@@ -50,15 +51,15 @@ module Virtus
|
|
50
51
|
# the name of an attribute
|
51
52
|
#
|
52
53
|
# @param [Class] type
|
53
|
-
#
|
54
|
+
# optional type class of an attribute
|
54
55
|
#
|
55
56
|
# @param [#to_hash] options
|
56
|
-
#
|
57
|
+
# optional extra options hash
|
57
58
|
#
|
58
59
|
# @return [Attribute]
|
59
60
|
#
|
60
61
|
# @api private
|
61
|
-
def self.build(name, type, options = {})
|
62
|
+
def self.build(name, type = Object, options = {})
|
62
63
|
attribute_class = determine_type(type) or
|
63
64
|
raise ArgumentError, "#{type.inspect} does not map to an attribute type"
|
64
65
|
attribute_options = attribute_class.merge_options(type, options)
|
data/lib/virtus/attribute_set.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Virtus
|
2
2
|
|
3
3
|
# A set of Attribute objects
|
4
|
-
class AttributeSet
|
4
|
+
class AttributeSet < Module
|
5
5
|
include Enumerable
|
6
6
|
|
7
7
|
# Initialize an AttributeSet
|
@@ -66,6 +66,7 @@ module Virtus
|
|
66
66
|
# @api public
|
67
67
|
def <<(attribute)
|
68
68
|
self[attribute.name] = attribute
|
69
|
+
attribute.define_accessor_methods(self)
|
69
70
|
self
|
70
71
|
end
|
71
72
|
|
@@ -110,6 +111,36 @@ module Virtus
|
|
110
111
|
self
|
111
112
|
end
|
112
113
|
|
114
|
+
# Defines an attribute reader method
|
115
|
+
#
|
116
|
+
# @param [Attribute] attribute
|
117
|
+
# @param [Symbol] method_name
|
118
|
+
# @param [Symbol] visibility
|
119
|
+
#
|
120
|
+
# @return [self]
|
121
|
+
#
|
122
|
+
# @api private
|
123
|
+
def define_reader_method(attribute, method_name, visibility)
|
124
|
+
define_method(method_name) { attribute.get(self) }
|
125
|
+
send(visibility, method_name)
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
# Defines an attribute writer method
|
130
|
+
#
|
131
|
+
# @param [Attribute] attribute
|
132
|
+
# @param [Symbol] method_name
|
133
|
+
# @param [Symbol] visibility
|
134
|
+
#
|
135
|
+
# @return [self]
|
136
|
+
#
|
137
|
+
# @api private
|
138
|
+
def define_writer_method(attribute, method_name, visibility)
|
139
|
+
define_method(method_name) { |value| attribute.set(self, value) }
|
140
|
+
send(visibility, method_name)
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
113
144
|
private
|
114
145
|
|
115
146
|
# Merge the attributes into the index
|
data/lib/virtus/class_methods.rb
CHANGED
@@ -15,7 +15,7 @@ module Virtus
|
|
15
15
|
super
|
16
16
|
descendant.module_eval do
|
17
17
|
extend DescendantsTracker
|
18
|
-
|
18
|
+
include attribute_set
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -31,7 +31,7 @@ module Virtus
|
|
31
31
|
# attribute :age, Integer
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
# User.
|
34
|
+
# User.attribute_set # =>
|
35
35
|
#
|
36
36
|
# TODO: implement inspect so the output is not cluttered - solnic
|
37
37
|
#
|
@@ -56,16 +56,15 @@ module Virtus
|
|
56
56
|
attribute_set
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
# Set up the anonymous module which will host Attribute accessor methods
|
59
|
+
# Hooks into const missing process to determine types of attributes
|
62
60
|
#
|
63
|
-
# @
|
61
|
+
# @param [String] name
|
62
|
+
#
|
63
|
+
# @return [Class]
|
64
64
|
#
|
65
65
|
# @api private
|
66
|
-
def
|
67
|
-
|
68
|
-
include @virtus_attributes_accessor_module
|
66
|
+
def const_missing(name)
|
67
|
+
Attribute.determine_type(name) or super
|
69
68
|
end
|
70
69
|
|
71
70
|
private
|
@@ -85,18 +84,7 @@ module Virtus
|
|
85
84
|
# @api private
|
86
85
|
def inherited(descendant)
|
87
86
|
super
|
88
|
-
descendant.
|
89
|
-
end
|
90
|
-
|
91
|
-
# Hooks into const missing process to determine types of attributes
|
92
|
-
#
|
93
|
-
# @param [String] name
|
94
|
-
#
|
95
|
-
# @return [Class]
|
96
|
-
#
|
97
|
-
# @api private
|
98
|
-
def const_missing(name)
|
99
|
-
Attribute.determine_type(name) or super
|
87
|
+
descendant.module_eval { include attribute_set }
|
100
88
|
end
|
101
89
|
|
102
90
|
# Add the attribute to the class' and descendants' attributes
|
@@ -111,9 +99,13 @@ module Virtus
|
|
111
99
|
descendants.each { |descendant| descendant.attribute_set.reset }
|
112
100
|
end
|
113
101
|
|
102
|
+
# The list of allowed public methods
|
103
|
+
#
|
104
|
+
# @return [Array<String>]
|
105
|
+
#
|
114
106
|
# @api private
|
115
|
-
def
|
116
|
-
public_instance_methods
|
107
|
+
def allowed_methods
|
108
|
+
public_instance_methods.map(&:to_s)
|
117
109
|
end
|
118
110
|
|
119
111
|
end # module ClassMethods
|
@@ -133,10 +133,14 @@ module Virtus
|
|
133
133
|
#
|
134
134
|
# @api public
|
135
135
|
def self.to_integer(value)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
136
|
+
if value =~ /\A#{INTEGER_REGEXP}\z/
|
137
|
+
value.to_i
|
138
|
+
else
|
139
|
+
# coerce to a Float first to evaluate scientific notation (if any)
|
140
|
+
# that may change the integer part, then convert to an integer
|
141
|
+
coerced = to_float(value)
|
142
|
+
::Float === coerced ? coerced.to_i : coerced
|
143
|
+
end
|
140
144
|
end
|
141
145
|
|
142
146
|
# Coerce value to float
|
data/lib/virtus/extensions.rb
CHANGED
@@ -13,10 +13,10 @@ module Virtus
|
|
13
13
|
#
|
14
14
|
# @api private
|
15
15
|
def self.extended(object)
|
16
|
-
|
16
|
+
super
|
17
17
|
object.instance_eval do
|
18
|
-
|
19
|
-
extend
|
18
|
+
extend InstanceMethods
|
19
|
+
extend attribute_set
|
20
20
|
end
|
21
21
|
end
|
22
22
|
private_class_method :extended
|
@@ -31,6 +31,7 @@ module Virtus
|
|
31
31
|
# attribute :author, String
|
32
32
|
# attribute :published_at, DateTime
|
33
33
|
# attribute :page_count, Integer
|
34
|
+
# attribute :index # defaults to Object
|
34
35
|
# end
|
35
36
|
#
|
36
37
|
# @param [Symbol] name
|
@@ -49,7 +50,6 @@ module Virtus
|
|
49
50
|
# @api public
|
50
51
|
def attribute(*args)
|
51
52
|
attribute = Attribute.build(*args)
|
52
|
-
attribute.define_accessor_methods(@virtus_attributes_accessor_module)
|
53
53
|
virtus_add_attribute(attribute)
|
54
54
|
self
|
55
55
|
end
|
@@ -62,8 +62,7 @@ module Virtus
|
|
62
62
|
def allowed_writer_methods
|
63
63
|
@allowed_writer_methods ||=
|
64
64
|
begin
|
65
|
-
allowed_writer_methods =
|
66
|
-
allowed_writer_methods = allowed_writer_methods.grep(WRITER_METHOD_REGEXP).to_set
|
65
|
+
allowed_writer_methods = allowed_methods.grep(WRITER_METHOD_REGEXP).to_set
|
67
66
|
allowed_writer_methods -= INVALID_WRITER_METHODS
|
68
67
|
allowed_writer_methods.freeze
|
69
68
|
end
|
@@ -132,6 +132,32 @@ module Virtus
|
|
132
132
|
attributes
|
133
133
|
end
|
134
134
|
|
135
|
+
# Freeze object
|
136
|
+
#
|
137
|
+
# @return [self]
|
138
|
+
#
|
139
|
+
# @api public
|
140
|
+
#
|
141
|
+
# @example
|
142
|
+
#
|
143
|
+
# class User
|
144
|
+
# include Virtus
|
145
|
+
#
|
146
|
+
# attribute :name, String
|
147
|
+
# attribute :age, Integer
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# user = User.new(:name => 'John', :age => 28)
|
151
|
+
# user.frozen? # => false
|
152
|
+
# user.freeze
|
153
|
+
# user.frozen? # => true
|
154
|
+
#
|
155
|
+
# @api public
|
156
|
+
def freeze
|
157
|
+
set_defaults
|
158
|
+
super
|
159
|
+
end
|
160
|
+
|
135
161
|
private
|
136
162
|
|
137
163
|
# Get values of all attributes defined for this class, ignoring privacy
|
@@ -146,6 +172,17 @@ module Virtus
|
|
146
172
|
end
|
147
173
|
end
|
148
174
|
|
175
|
+
# Ensure all defaults are set
|
176
|
+
#
|
177
|
+
# @return [AttributeSet]
|
178
|
+
#
|
179
|
+
# @api private
|
180
|
+
def set_defaults
|
181
|
+
attribute_set.each do |attribute|
|
182
|
+
get_attribute(attribute.name)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
149
186
|
# Mass-assign attribute values
|
150
187
|
#
|
151
188
|
# @see Virtus::InstanceMethods#attributes=
|
@@ -181,9 +218,13 @@ module Virtus
|
|
181
218
|
__send__("#{name}=", value)
|
182
219
|
end
|
183
220
|
|
221
|
+
# The list of allowed public methods
|
222
|
+
#
|
223
|
+
# @return [Array<String>]
|
224
|
+
#
|
184
225
|
# @api private
|
185
|
-
def
|
186
|
-
public_methods
|
226
|
+
def allowed_methods
|
227
|
+
public_methods.map(&:to_s)
|
187
228
|
end
|
188
229
|
|
189
230
|
end # module InstanceMethods
|
@@ -4,8 +4,16 @@ module Virtus
|
|
4
4
|
#
|
5
5
|
module ModuleExtensions
|
6
6
|
|
7
|
+
# Define an attribute in the module
|
8
|
+
#
|
9
|
+
# @see Virtus::Extensions#attribute
|
10
|
+
#
|
11
|
+
# @return [self]
|
12
|
+
#
|
13
|
+
# @api private
|
7
14
|
def attribute(*args)
|
8
15
|
attribute_definitions << args
|
16
|
+
self
|
9
17
|
end
|
10
18
|
|
11
19
|
private
|
@@ -32,7 +40,7 @@ module Virtus
|
|
32
40
|
# @api private
|
33
41
|
def included(object)
|
34
42
|
super
|
35
|
-
object.
|
43
|
+
object.module_eval { include Virtus }
|
36
44
|
define_attributes(object)
|
37
45
|
end
|
38
46
|
|
@@ -58,5 +66,5 @@ module Virtus
|
|
58
66
|
end
|
59
67
|
end
|
60
68
|
|
61
|
-
end #
|
69
|
+
end # module ModuleExtensions
|
62
70
|
end # module Virtus
|
data/lib/virtus/value_object.rb
CHANGED
@@ -35,6 +35,7 @@ module Virtus
|
|
35
35
|
include ::Virtus
|
36
36
|
include InstanceMethods
|
37
37
|
extend ClassMethods
|
38
|
+
private :attributes=
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
@@ -93,10 +94,10 @@ module Virtus
|
|
93
94
|
# @return [self]
|
94
95
|
#
|
95
96
|
# @api public
|
96
|
-
def attribute(name,
|
97
|
+
def attribute(name, *args)
|
97
98
|
equalizer << name
|
98
|
-
options
|
99
|
-
super
|
99
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
100
|
+
super name, *args << options.merge(:writer => :private)
|
100
101
|
end
|
101
102
|
|
102
103
|
# Define and include a module that provides Value Object semantics
|
data/lib/virtus/version.rb
CHANGED
@@ -3,14 +3,18 @@ require 'spec_helper'
|
|
3
3
|
describe 'I can define attributes within a module' do
|
4
4
|
before do
|
5
5
|
module Examples
|
6
|
-
module
|
6
|
+
module Common
|
7
7
|
include Virtus
|
8
|
+
end
|
9
|
+
|
10
|
+
module Name
|
11
|
+
include Common
|
8
12
|
|
9
13
|
attribute :name, String
|
10
14
|
end
|
11
15
|
|
12
16
|
module Age
|
13
|
-
include
|
17
|
+
include Common
|
14
18
|
|
15
19
|
attribute :age, Integer
|
16
20
|
end
|
@@ -40,7 +40,7 @@ describe Virtus::ValueObject do
|
|
40
40
|
it 'writer methods are set to private' do
|
41
41
|
private_methods = class_under_test.private_instance_methods
|
42
42
|
private_methods.map! { |m| m.to_s }
|
43
|
-
private_methods.should include('latitude=', 'longitude=')
|
43
|
+
private_methods.should include('latitude=', 'longitude=', 'attributes=')
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'attempts to call attribute writer methods raises NameError' do
|
@@ -0,0 +1,37 @@
|
|
1
|
+
shared_examples_for 'a #freeze method' do
|
2
|
+
let(:sample_exception) do
|
3
|
+
begin
|
4
|
+
object.dup.freeze.instance_variable_set(:@foo,:bar)
|
5
|
+
rescue => exception
|
6
|
+
exception
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:expected_exception_class) do
|
11
|
+
# Ruby 1.8 blows up with TypeError Ruby 1.9 with RuntimeError
|
12
|
+
sample_exception.class
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:expected_exception_message) do
|
16
|
+
# Ruby 1.8 blows up with a different message than Ruby 1.9
|
17
|
+
sample_exception.message
|
18
|
+
end
|
19
|
+
|
20
|
+
it_should_behave_like 'an idempotent method'
|
21
|
+
|
22
|
+
it 'returns object' do
|
23
|
+
should be(object)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'prevents future modifications' do
|
27
|
+
subject
|
28
|
+
expectation = raise_error(expected_exception_class,expected_exception_message)
|
29
|
+
expect { object.instance_variable_set(:@foo,:bar) }.to(expectation)
|
30
|
+
end
|
31
|
+
|
32
|
+
its(:frozen?) { should be(true) }
|
33
|
+
|
34
|
+
it 'allows to access attribute' do
|
35
|
+
subject.name.should eql('John')
|
36
|
+
end
|
37
|
+
end
|
@@ -27,6 +27,16 @@ describe Virtus::Attribute, '.build' do
|
|
27
27
|
its(:options) { should == Virtus::Attribute::String.options }
|
28
28
|
end
|
29
29
|
|
30
|
+
context 'without a type' do
|
31
|
+
subject { object.build(name) }
|
32
|
+
|
33
|
+
it { should be_instance_of(Virtus::Attribute::Object) }
|
34
|
+
|
35
|
+
its(:name) { should be(name) }
|
36
|
+
|
37
|
+
its(:options) { should == Virtus::Attribute::Object.options }
|
38
|
+
end
|
39
|
+
|
30
40
|
context 'with an invalid type' do
|
31
41
|
subject { object.build(name, type) }
|
32
42
|
|
@@ -8,6 +8,8 @@ describe Virtus::AttributeSet, '#<<' do
|
|
8
8
|
let(:object) { described_class.new(parent, attributes) }
|
9
9
|
let(:name) { :name }
|
10
10
|
|
11
|
+
before { attribute.stub(:define_accessor_methods) }
|
12
|
+
|
11
13
|
context 'with a new attribute' do
|
12
14
|
let(:attribute) { mock('Attribute', :name => name) }
|
13
15
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Virtus::
|
4
|
-
subject { described_class.new
|
3
|
+
describe Virtus::AttributeSet, '#define_reader_method' do
|
4
|
+
subject { described_class.new }
|
5
5
|
|
6
6
|
let(:attribute) { mock('attribute') }
|
7
7
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Virtus::
|
4
|
-
subject { described_class.new
|
3
|
+
describe Virtus::AttributeSet, '#define_writer_method' do
|
4
|
+
subject { described_class.new }
|
5
5
|
|
6
6
|
let(:attribute) { mock('attribute') }
|
7
7
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Virtus::
|
3
|
+
describe Virtus::AttributeSet, '#inspect' do
|
4
4
|
subject { object.inspect }
|
5
5
|
|
6
6
|
let(:object) { described_class.new('Test') }
|
7
7
|
|
8
|
-
it { should eql('Test::
|
8
|
+
it { pending; should eql('Test::AttributeSet') }
|
9
9
|
end
|
@@ -8,6 +8,8 @@ describe Virtus::AttributeSet, '#reset' do
|
|
8
8
|
let(:attributes) { [ attribute ] }
|
9
9
|
let(:object) { described_class.new(parent, attributes) }
|
10
10
|
|
11
|
+
before { attribute.stub(:define_accessor_methods) }
|
12
|
+
|
11
13
|
context 'when the parent has no attributes' do
|
12
14
|
let(:parent) { described_class.new }
|
13
15
|
|
@@ -39,6 +41,8 @@ describe Virtus::AttributeSet, '#reset' do
|
|
39
41
|
let(:parent) { described_class.new([ parent_attribute ]) }
|
40
42
|
let(:new_attribute) { mock('New Attribute', :name => :parent_name) }
|
41
43
|
|
44
|
+
before { new_attribute.stub(:define_accessor_methods) }
|
45
|
+
|
42
46
|
it { should equal(object) }
|
43
47
|
|
44
48
|
it 'includes changes from the parent' do
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::ClassMethods, '#attributes' do
|
4
|
+
subject { object.attributes }
|
5
|
+
|
6
|
+
before do
|
7
|
+
@original_stderr, $stderr = $stderr, StringIO.new
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
$stderr = @original_stderr
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:object) { Class.new { extend Virtus::ClassMethods } }
|
15
|
+
|
16
|
+
it { should be_instance_of(Virtus::AttributeSet) }
|
17
|
+
|
18
|
+
it 'returns a deprecation warning' do
|
19
|
+
subject
|
20
|
+
$stderr.string.should =~ /\A#{object}.attributes is deprecated. Use #{object}.attribute_set instead: #{__FILE__}:4\b/
|
21
|
+
end
|
22
|
+
end
|
@@ -5,15 +5,15 @@ describe Virtus::ClassMethods, '#inherited' do
|
|
5
5
|
|
6
6
|
let(:object) { Class.new { extend Virtus::ClassMethods } }
|
7
7
|
|
8
|
-
it 'includes an
|
8
|
+
it 'includes an AttributeSet module' do
|
9
9
|
descendant = subject
|
10
10
|
|
11
11
|
# return the descendant's attribute accessor modules (superclass + own)
|
12
|
-
modules = descendant.ancestors.grep(Virtus::
|
12
|
+
modules = descendant.ancestors.grep(Virtus::AttributeSet)
|
13
13
|
modules.size.should be(2)
|
14
14
|
|
15
15
|
# remove the superclass' attribute accessor module
|
16
|
-
modules -= object.ancestors.grep(Virtus::
|
16
|
+
modules -= object.ancestors.grep(Virtus::AttributeSet)
|
17
17
|
|
18
18
|
# the descendant should have it's own attribute accessor module
|
19
19
|
modules.size.should be(1)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::InstanceMethods, '#freeze' do
|
4
|
+
subject { object.freeze }
|
5
|
+
|
6
|
+
let(:object) do
|
7
|
+
described_class.new(attributes)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'on class with no defaults' do
|
11
|
+
let(:described_class) do
|
12
|
+
Class.new do
|
13
|
+
include Virtus
|
14
|
+
attribute :name, String
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:attributes) { { :name => 'John' } }
|
19
|
+
|
20
|
+
it_should_behave_like 'a #freeze method'
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'on class with literal default' do
|
24
|
+
let(:described_class) do
|
25
|
+
Class.new do
|
26
|
+
include Virtus
|
27
|
+
attribute :name, String, :default => 'John'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when value is provided' do
|
32
|
+
let(:attributes) { { :name => 'John' } }
|
33
|
+
|
34
|
+
it_should_behave_like 'a #freeze method'
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when value is NOT provided' do
|
38
|
+
let(:attributes) {}
|
39
|
+
|
40
|
+
it_should_behave_like 'a #freeze method'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'on class with computed default' do
|
45
|
+
let(:described_class) do
|
46
|
+
Class.new do
|
47
|
+
include Virtus
|
48
|
+
attribute :name, String, :default => proc { 'John' }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when value is provided' do
|
53
|
+
let(:attributes) { { :name => 'John' } }
|
54
|
+
|
55
|
+
it_should_behave_like 'a #freeze method'
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when value is NOT provided' do
|
59
|
+
let(:attributes) {}
|
60
|
+
|
61
|
+
it_should_behave_like 'a #freeze method'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::ModuleExtensions, '#attribute' do
|
4
|
+
subject { object.attribute(name, type, options) }
|
5
|
+
|
6
|
+
let(:object) { Module.new }
|
7
|
+
let(:name) { :name }
|
8
|
+
let(:type) { String }
|
9
|
+
let(:options) { { :default => default } }
|
10
|
+
let(:default) { 'John Doe'.freeze }
|
11
|
+
|
12
|
+
before do
|
13
|
+
object.extend(Virtus::ModuleExtensions)
|
14
|
+
end
|
15
|
+
|
16
|
+
it { should be(object) }
|
17
|
+
|
18
|
+
it 'tracks the attribute for extension' do
|
19
|
+
subject
|
20
|
+
instance = Object.new
|
21
|
+
instance.extend(object)
|
22
|
+
instance.attributes[name].should eql(default)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'tracks the attribute for inclusion' do
|
26
|
+
subject
|
27
|
+
klass = Class.new
|
28
|
+
klass.send(:include, object)
|
29
|
+
klass.attribute_set[name].should eql(Virtus::Attribute::String.new(name, options))
|
30
|
+
end
|
31
|
+
end
|
@@ -1,23 +1,50 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Virtus::ValueObject, '.attribute' do
|
4
|
-
subject { object.attribute(name, type) }
|
5
|
-
|
6
4
|
let(:object) { Class.new { include Virtus::ValueObject } }
|
7
5
|
let(:name) { :latitude }
|
8
6
|
let(:type) { Float }
|
9
7
|
let(:attribute) { object.attribute_set[name] }
|
10
8
|
|
11
|
-
|
9
|
+
context 'without options' do
|
10
|
+
subject { object.attribute(name, type) }
|
11
|
+
|
12
|
+
it { should be(object) }
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
it 'adds the attribute to the equalizer' do
|
15
|
+
object.new.inspect.should_not match(/\b#{name}=\b/)
|
16
|
+
subject
|
17
|
+
object.new.inspect.should match(/\b#{name}=\b/)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'sets the writer to be private' do
|
21
|
+
subject
|
22
|
+
attribute.options[:writer].should be(:private)
|
23
|
+
end
|
17
24
|
end
|
18
25
|
|
19
|
-
|
20
|
-
subject
|
21
|
-
|
26
|
+
context 'with options' do
|
27
|
+
subject { object.attribute(name, type, options) }
|
28
|
+
|
29
|
+
let(:options) { { :default => default } }
|
30
|
+
let(:default) { 1.0 }
|
31
|
+
|
32
|
+
it { should be(object) }
|
33
|
+
|
34
|
+
it 'adds the attribute to the equalizer' do
|
35
|
+
object.new.inspect.should_not match(/\b#{name}=\b/)
|
36
|
+
subject
|
37
|
+
object.new.inspect.should match(/\b#{name}=\b/)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'sets the writer to be private' do
|
41
|
+
subject
|
42
|
+
attribute.options[:writer].should be(:private)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'sets the default' do
|
46
|
+
subject
|
47
|
+
attribute.default.value.should eql(1.0)
|
48
|
+
end
|
22
49
|
end
|
23
50
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
[ :clone, :dup ].each do |method|
|
4
|
+
describe Virtus::ValueObject::InstanceMethods, "##{method}" do
|
5
|
+
subject { object.send(method) }
|
6
|
+
|
7
|
+
let(:object) { described_class.new }
|
8
|
+
|
9
|
+
let(:described_class) do
|
10
|
+
Class.new do
|
11
|
+
include Virtus::ValueObject
|
12
|
+
|
13
|
+
attribute :name, String
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns the same instance' do
|
18
|
+
should equal(object)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/tasks/metrics/heckle.rake
CHANGED
@@ -45,7 +45,10 @@ begin
|
|
45
45
|
end
|
46
46
|
|
47
47
|
aliases = Hash.new { |h,mod| h[mod] = Hash.new { |h,method| h[method] = method } }
|
48
|
-
|
48
|
+
|
49
|
+
aliases['Virtus::ValueObject::InstanceMethods']['dup'] = 'clone'
|
50
|
+
|
51
|
+
map = NameMap.new
|
49
52
|
|
50
53
|
heckle_caught_modules = Hash.new { |hash, key| hash[key] = [] }
|
51
54
|
unhandled_mutations = 0
|
@@ -111,7 +114,6 @@ begin
|
|
111
114
|
|
112
115
|
unless spec_file.file?
|
113
116
|
raise "No spec file #{spec_file} for #{mod}.#{method}"
|
114
|
-
next
|
115
117
|
end
|
116
118
|
|
117
119
|
specs << [ ".#{method}", [ spec_file ] ]
|
@@ -126,7 +128,6 @@ begin
|
|
126
128
|
|
127
129
|
unless spec_file.file?
|
128
130
|
raise "No spec file #{spec_file} for #{mod}##{method}"
|
129
|
-
next
|
130
131
|
end
|
131
132
|
|
132
133
|
specs << [ "##{method}", [ spec_file ] ]
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virtus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 0.5.
|
9
|
+
- 2
|
10
|
+
version: 0.5.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Piotr Solnica
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-09-01 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: backports
|
@@ -136,7 +136,6 @@ files:
|
|
136
136
|
- lib/virtus/attribute/symbol.rb
|
137
137
|
- lib/virtus/attribute/time.rb
|
138
138
|
- lib/virtus/attribute_set.rb
|
139
|
-
- lib/virtus/attributes_accessor.rb
|
140
139
|
- lib/virtus/class_inclusions.rb
|
141
140
|
- lib/virtus/class_methods.rb
|
142
141
|
- lib/virtus/coercion.rb
|
@@ -179,6 +178,7 @@ files:
|
|
179
178
|
- spec/integration/virtus/value_object_spec.rb
|
180
179
|
- spec/rcov.opts
|
181
180
|
- spec/shared/constants_helpers.rb
|
181
|
+
- spec/shared/freeze_method_behavior.rb
|
182
182
|
- spec/shared/idempotent_method_behaviour.rb
|
183
183
|
- spec/shared/options_class_method.rb
|
184
184
|
- spec/spec_helper.rb
|
@@ -247,16 +247,17 @@ files:
|
|
247
247
|
- spec/unit/virtus/attribute/time/coerce_spec.rb
|
248
248
|
- spec/unit/virtus/attribute/value_coerced_spec.rb
|
249
249
|
- spec/unit/virtus/attribute_set/append_spec.rb
|
250
|
+
- spec/unit/virtus/attribute_set/define_reader_method_spec.rb
|
251
|
+
- spec/unit/virtus/attribute_set/define_writer_method_spec.rb
|
250
252
|
- spec/unit/virtus/attribute_set/each_spec.rb
|
251
253
|
- spec/unit/virtus/attribute_set/element_reference_spec.rb
|
252
254
|
- spec/unit/virtus/attribute_set/element_set_spec.rb
|
255
|
+
- spec/unit/virtus/attribute_set/inspect_spec.rb
|
253
256
|
- spec/unit/virtus/attribute_set/merge_spec.rb
|
254
257
|
- spec/unit/virtus/attribute_set/reset_spec.rb
|
255
|
-
- spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb
|
256
|
-
- spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb
|
257
|
-
- spec/unit/virtus/attributes_accessor/inspect_spec.rb
|
258
258
|
- spec/unit/virtus/class_methods/attribute_set_spec.rb
|
259
259
|
- spec/unit/virtus/class_methods/attribute_spec.rb
|
260
|
+
- spec/unit/virtus/class_methods/attributes_spec.rb
|
260
261
|
- spec/unit/virtus/class_methods/const_missing_spec.rb
|
261
262
|
- spec/unit/virtus/class_methods/inherited_spec.rb
|
262
263
|
- spec/unit/virtus/coercion/array/class_methods/to_set_spec.rb
|
@@ -323,8 +324,10 @@ files:
|
|
323
324
|
- spec/unit/virtus/instance_methods/attributes_spec.rb
|
324
325
|
- spec/unit/virtus/instance_methods/element_reference_spec.rb
|
325
326
|
- spec/unit/virtus/instance_methods/element_set_spec.rb
|
327
|
+
- spec/unit/virtus/instance_methods/freeze_spec.rb
|
326
328
|
- spec/unit/virtus/instance_methods/initialize_spec.rb
|
327
329
|
- spec/unit/virtus/instance_methods/to_hash_spec.rb
|
330
|
+
- spec/unit/virtus/module_extensions/attribute_spec.rb
|
328
331
|
- spec/unit/virtus/options/accept_options_spec.rb
|
329
332
|
- spec/unit/virtus/options/accepted_options_spec.rb
|
330
333
|
- spec/unit/virtus/options/options_spec.rb
|
@@ -335,7 +338,7 @@ files:
|
|
335
338
|
- spec/unit/virtus/value_object/class_methods/attribute_spec.rb
|
336
339
|
- spec/unit/virtus/value_object/class_methods/equalizer_spec.rb
|
337
340
|
- spec/unit/virtus/value_object/initialize_spec.rb
|
338
|
-
- spec/unit/virtus/value_object/instance_methods/
|
341
|
+
- spec/unit/virtus/value_object/instance_methods/clone_spec.rb
|
339
342
|
- spec/unit/virtus/value_object/instance_methods/with_spec.rb
|
340
343
|
- tasks/metrics/ci.rake
|
341
344
|
- tasks/metrics/flay.rake
|
@@ -377,7 +380,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
377
380
|
requirements: []
|
378
381
|
|
379
382
|
rubyforge_project:
|
380
|
-
rubygems_version: 1.8.
|
383
|
+
rubygems_version: 1.8.15
|
381
384
|
signing_key:
|
382
385
|
specification_version: 3
|
383
386
|
summary: Attributes on Steroids for Plain Old Ruby Objects
|
@@ -1,68 +0,0 @@
|
|
1
|
-
module Virtus
|
2
|
-
|
3
|
-
# Host attribute accessor methods
|
4
|
-
class AttributesAccessor < Module
|
5
|
-
|
6
|
-
# Initialize a module for hosting Attribute access methods
|
7
|
-
#
|
8
|
-
# @param [Symbol, String] name
|
9
|
-
#
|
10
|
-
# @api private
|
11
|
-
def initialize(name)
|
12
|
-
super()
|
13
|
-
@name = name
|
14
|
-
end
|
15
|
-
|
16
|
-
# Defines an attribute reader method
|
17
|
-
#
|
18
|
-
# @param [Attribute] attribute
|
19
|
-
# @param [Symbol] method_name
|
20
|
-
# @param [Symbol] visibility
|
21
|
-
#
|
22
|
-
# @return [self]
|
23
|
-
#
|
24
|
-
# @api private
|
25
|
-
def define_reader_method(attribute, method_name, visibility)
|
26
|
-
define_method(method_name) { attribute.get(self) }
|
27
|
-
send(visibility, method_name)
|
28
|
-
self
|
29
|
-
end
|
30
|
-
|
31
|
-
# Defines an attribute writer method
|
32
|
-
#
|
33
|
-
# @param [Attribute] attribute
|
34
|
-
# @param [Symbol] method_name
|
35
|
-
# @param [Symbol] visibility
|
36
|
-
#
|
37
|
-
# @return [self]
|
38
|
-
#
|
39
|
-
# @api private
|
40
|
-
def define_writer_method(attribute, method_name, visibility)
|
41
|
-
define_method(method_name) { |value| attribute.set(self, value) }
|
42
|
-
send(visibility, method_name)
|
43
|
-
self
|
44
|
-
end
|
45
|
-
|
46
|
-
# The inspect value of this Module
|
47
|
-
#
|
48
|
-
# This provides meaningful output when inspecting the ancestors
|
49
|
-
# of a class/module that includes this module
|
50
|
-
#
|
51
|
-
# @example
|
52
|
-
#
|
53
|
-
# class ClassWithAttributes
|
54
|
-
# include Virtus
|
55
|
-
# end
|
56
|
-
#
|
57
|
-
# mod = ClassWithAttributes.send(:virtus_setup_attributes_accessor_module)
|
58
|
-
# mod.inspect
|
59
|
-
#
|
60
|
-
# @return [String]
|
61
|
-
#
|
62
|
-
# @api public
|
63
|
-
def inspect
|
64
|
-
"#{@name}::AttributesAccessor"
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Virtus::ValueObject::InstanceMethods, 'duplication' do
|
4
|
-
let(:described_class) do
|
5
|
-
Class.new do
|
6
|
-
include Virtus::ValueObject
|
7
|
-
|
8
|
-
attribute :name, String
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
subject { described_class.new }
|
13
|
-
|
14
|
-
it '#clone returns the same instance' do
|
15
|
-
subject.should equal(subject.clone)
|
16
|
-
end
|
17
|
-
|
18
|
-
it '#dup returns the same instance' do
|
19
|
-
subject.should equal(subject.dup)
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|