virtus 0.5.1 → 0.5.2
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/.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
|
[](http://travis-ci.org/solnic/virtus)
|
5
5
|
[](https://gemnasium.com/solnic/virtus)
|
6
|
-
|
7
|
-
[Metrics on CodeClimate](https://codeclimate.com/github/solnic/virtus)
|
6
|
+
[](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
|