virtus 1.0.0.beta8 → 1.0.0.rc1
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/Changelog.md +28 -1
- data/LICENSE +1 -1
- data/README.md +12 -22
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/mutant.yml +2 -1
- data/config/reek.yml +67 -17
- data/lib/virtus.rb +61 -5
- data/lib/virtus/attribute.rb +107 -26
- data/lib/virtus/attribute/builder.rb +42 -34
- data/lib/virtus/attribute/collection.rb +11 -6
- data/lib/virtus/attribute/embedded_value.rb +12 -7
- data/lib/virtus/attribute/hash.rb +19 -8
- data/lib/virtus/attribute_set.rb +2 -13
- data/lib/virtus/builder.rb +137 -0
- data/lib/virtus/builder/hook_context.rb +51 -0
- data/lib/virtus/class_inclusions.rb +1 -0
- data/lib/virtus/class_methods.rb +1 -5
- data/lib/virtus/configuration.rb +12 -2
- data/lib/virtus/extensions.rb +28 -32
- data/lib/virtus/instance_methods.rb +2 -2
- data/lib/virtus/model.rb +3 -1
- data/lib/virtus/module_extensions.rb +1 -1
- data/lib/virtus/value_object.rb +1 -1
- data/lib/virtus/version.rb +1 -1
- data/spec/integration/inheritance_spec.rb +42 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +10 -0
- data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +14 -0
- data/spec/unit/virtus/class_methods/finalize_spec.rb +10 -4
- data/spec/unit/virtus/model_spec.rb +35 -3
- data/spec/unit/virtus/module_spec.rb +44 -3
- data/spec/unit/virtus/value_object_spec.rb +39 -22
- data/virtus.gemspec +2 -1
- metadata +9 -6
- data/lib/virtus/module_builder.rb +0 -192
@@ -14,10 +14,6 @@ describe Virtus::ValueObject do
|
|
14
14
|
expect(subject.class.attribute_set[:name]).to_not be_public_writer
|
15
15
|
end
|
16
16
|
|
17
|
-
it 'disallows mass-assignment' do
|
18
|
-
expect(subject.private_methods).to include(:attributes=)
|
19
|
-
end
|
20
|
-
|
21
17
|
it 'disallows cloning' do
|
22
18
|
expect(subject.clone).to be(subject)
|
23
19
|
end
|
@@ -41,10 +37,20 @@ describe Virtus::ValueObject do
|
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
40
|
+
share_examples_for 'a valid value object with mass-assignment turned on' do
|
41
|
+
subject { model.new }
|
42
|
+
|
43
|
+
it 'disallows mass-assignment' do
|
44
|
+
expect(subject.private_methods).to include(:attributes=)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
44
48
|
context 'using new values {} block' do
|
45
49
|
let(:model) {
|
50
|
+
model = Virtus.value_object(:coerce => false, :mass_assignment => mass_assignment)
|
51
|
+
|
46
52
|
Class.new {
|
47
|
-
include
|
53
|
+
include model
|
48
54
|
|
49
55
|
def self.name
|
50
56
|
'Model'
|
@@ -57,30 +63,41 @@ describe Virtus::ValueObject do
|
|
57
63
|
}
|
58
64
|
}
|
59
65
|
|
60
|
-
|
66
|
+
context 'without mass-assignment' do
|
67
|
+
let(:mass_assignment) { false }
|
61
68
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
69
|
+
it_behaves_like 'a valid value object'
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with mass-assignment' do
|
73
|
+
let(:mass_assignment) { true }
|
74
|
+
|
75
|
+
it_behaves_like 'a valid value object'
|
76
|
+
it_behaves_like 'a valid value object with mass-assignment turned on'
|
77
|
+
|
78
|
+
context 'with a model subclass' do
|
79
|
+
let(:subclass) {
|
80
|
+
Class.new(model) {
|
81
|
+
values do
|
82
|
+
attribute :email, String
|
83
|
+
end
|
84
|
+
}
|
68
85
|
}
|
69
|
-
}
|
70
86
|
|
71
|
-
|
72
|
-
|
87
|
+
it_behaves_like 'a valid value object' do
|
88
|
+
subject { subclass.new(attributes) }
|
73
89
|
|
74
|
-
|
90
|
+
let(:attributes) { Hash[:id => 1, :name => 'Jane Doe', :email => 'jane@doe.com'] }
|
75
91
|
|
76
|
-
|
92
|
+
its(:email) { should eql('jane@doe.com') }
|
77
93
|
|
78
|
-
|
79
|
-
|
80
|
-
|
94
|
+
it 'sets private writers for additional values' do
|
95
|
+
expect(subclass.attribute_set[:email]).to_not be_public_writer
|
96
|
+
end
|
81
97
|
|
82
|
-
|
83
|
-
|
98
|
+
it 'defines valid #== for a subclass' do
|
99
|
+
expect(subject == subject.class.new(attributes.merge(:id => 2))).to be(false)
|
100
|
+
end
|
84
101
|
end
|
85
102
|
end
|
86
103
|
end
|
data/virtus.gemspec
CHANGED
@@ -10,6 +10,7 @@ Gem::Specification.new do |gem|
|
|
10
10
|
gem.description = "Attributes on Steroids for Plain Old Ruby Objects"
|
11
11
|
gem.summary = gem.description
|
12
12
|
gem.homepage = "https://github.com/solnic/virtus"
|
13
|
+
gem.license = 'MIT'
|
13
14
|
|
14
15
|
gem.require_paths = [ "lib" ]
|
15
16
|
gem.files = `git ls-files`.split("\n")
|
@@ -19,5 +20,5 @@ Gem::Specification.new do |gem|
|
|
19
20
|
gem.add_dependency('descendants_tracker', '~> 0.0.1')
|
20
21
|
gem.add_dependency('equalizer', '~> 0.0.7')
|
21
22
|
gem.add_dependency('coercible', '~> 0.2')
|
22
|
-
gem.add_dependency('axiom-types', '~> 0.0.
|
23
|
+
gem.add_dependency('axiom-types', '~> 0.0.4')
|
23
24
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virtus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc1
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: descendants_tracker
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
requirements:
|
67
67
|
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 0.0.
|
69
|
+
version: 0.0.4
|
70
70
|
type: :runtime
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -74,7 +74,7 @@ dependencies:
|
|
74
74
|
requirements:
|
75
75
|
- - ~>
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: 0.0.
|
77
|
+
version: 0.0.4
|
78
78
|
description: Attributes on Steroids for Plain Old Ruby Objects
|
79
79
|
email:
|
80
80
|
- piotr.solnica@gmail.com
|
@@ -122,6 +122,8 @@ files:
|
|
122
122
|
- lib/virtus/attribute/lazy_default.rb
|
123
123
|
- lib/virtus/attribute/strict.rb
|
124
124
|
- lib/virtus/attribute_set.rb
|
125
|
+
- lib/virtus/builder.rb
|
126
|
+
- lib/virtus/builder/hook_context.rb
|
125
127
|
- lib/virtus/class_inclusions.rb
|
126
128
|
- lib/virtus/class_methods.rb
|
127
129
|
- lib/virtus/configuration.rb
|
@@ -129,7 +131,6 @@ files:
|
|
129
131
|
- lib/virtus/extensions.rb
|
130
132
|
- lib/virtus/instance_methods.rb
|
131
133
|
- lib/virtus/model.rb
|
132
|
-
- lib/virtus/module_builder.rb
|
133
134
|
- lib/virtus/module_extensions.rb
|
134
135
|
- lib/virtus/support/equalizer.rb
|
135
136
|
- lib/virtus/support/options.rb
|
@@ -145,6 +146,7 @@ files:
|
|
145
146
|
- spec/integration/embedded_value_spec.rb
|
146
147
|
- spec/integration/extending_objects_spec.rb
|
147
148
|
- spec/integration/hash_attributes_coercion_spec.rb
|
149
|
+
- spec/integration/inheritance_spec.rb
|
148
150
|
- spec/integration/injectible_coercers_spec.rb
|
149
151
|
- spec/integration/mass_assignment_with_accessors_spec.rb
|
150
152
|
- spec/integration/overriding_virtus_spec.rb
|
@@ -201,7 +203,8 @@ files:
|
|
201
203
|
- spec/unit/virtus/value_object_spec.rb
|
202
204
|
- virtus.gemspec
|
203
205
|
homepage: https://github.com/solnic/virtus
|
204
|
-
licenses:
|
206
|
+
licenses:
|
207
|
+
- MIT
|
205
208
|
post_install_message:
|
206
209
|
rdoc_options: []
|
207
210
|
require_paths:
|
@@ -1,192 +0,0 @@
|
|
1
|
-
module Virtus
|
2
|
-
|
3
|
-
# Class to build a Virtus module with it's own configuration
|
4
|
-
#
|
5
|
-
# This allows for individual Virtus modules to be included in
|
6
|
-
# classes and not impacted by the global Virtus configuration,
|
7
|
-
# which is implemented using Virtus::Configuration.
|
8
|
-
#
|
9
|
-
# @private
|
10
|
-
class ExtensionBuilder
|
11
|
-
|
12
|
-
# Return module
|
13
|
-
#
|
14
|
-
# @return [Module]
|
15
|
-
#
|
16
|
-
# @api private
|
17
|
-
attr_reader :module
|
18
|
-
|
19
|
-
# Return configuration
|
20
|
-
#
|
21
|
-
# @return [Configuration]
|
22
|
-
#
|
23
|
-
# @api private
|
24
|
-
attr_reader :configuration
|
25
|
-
|
26
|
-
# Builds a new Virtus module
|
27
|
-
#
|
28
|
-
# The block is passed to Virtus::Configuration
|
29
|
-
#
|
30
|
-
# @example
|
31
|
-
# ModuleBuilder.call do |config|
|
32
|
-
# # config settings
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# @return [Module]
|
36
|
-
#
|
37
|
-
# @api public
|
38
|
-
def self.call(options = {}, &block)
|
39
|
-
config = Configuration.build(&block)
|
40
|
-
options.each { |key, value| config.public_send("#{key}=", value) }
|
41
|
-
builder = new(config)
|
42
|
-
builder.add_included_hook
|
43
|
-
builder.add_extended_hook
|
44
|
-
builder.module
|
45
|
-
end
|
46
|
-
|
47
|
-
# @api private
|
48
|
-
def self.pending
|
49
|
-
@pending ||= []
|
50
|
-
end
|
51
|
-
|
52
|
-
# Initializes a new ModuleBuilder
|
53
|
-
#
|
54
|
-
# @param [Configuration] configuration
|
55
|
-
#
|
56
|
-
# @param [Module] mod
|
57
|
-
#
|
58
|
-
# @return [undefined]
|
59
|
-
#
|
60
|
-
# @api private
|
61
|
-
def initialize(configuration, mod = Module.new)
|
62
|
-
@configuration = configuration
|
63
|
-
@module = mod
|
64
|
-
end
|
65
|
-
|
66
|
-
# Adds the .included hook to the anonymous module which then defines the
|
67
|
-
# .attribute method to override the default.
|
68
|
-
#
|
69
|
-
# @return [Module]
|
70
|
-
#
|
71
|
-
# @api private
|
72
|
-
def add_included_hook
|
73
|
-
attribute_proc = attribute_method(configuration)
|
74
|
-
constructor = configuration.constructor
|
75
|
-
mass_assignment = configuration.mass_assignment
|
76
|
-
finalize = configuration.finalize
|
77
|
-
extensions = core_extensions
|
78
|
-
inclusions = core_inclusions
|
79
|
-
|
80
|
-
self.module.define_singleton_method :included do |object|
|
81
|
-
super(object)
|
82
|
-
ExtensionBuilder.pending << object unless finalize
|
83
|
-
extensions.each { |mod| object.extend(mod) }
|
84
|
-
inclusions.each { |mod| object.send(:include, mod) }
|
85
|
-
object.send(:include, Model::Constructor) if constructor
|
86
|
-
object.send(:include, Model::MassAssignment) if mass_assignment
|
87
|
-
object.send(:define_singleton_method, :attribute, attribute_proc)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# @api private
|
92
|
-
def add_extended_hook
|
93
|
-
attribute_proc = attribute_method(configuration)
|
94
|
-
mass_assignment = configuration.mass_assignment
|
95
|
-
extensions = core_inclusions + core_extensions
|
96
|
-
|
97
|
-
self.module.define_singleton_method :extended do |object|
|
98
|
-
super(object)
|
99
|
-
extensions.each { |mod| object.extend(mod) }
|
100
|
-
object.extend(Model::MassAssignment) if mass_assignment
|
101
|
-
object.send :define_singleton_method, :attribute, attribute_proc
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# Wrapper for the attribute method that is used in .add_included_hook
|
106
|
-
# The coercer is passed in the unused key :configured_coercer to allow the
|
107
|
-
# property encapsulation by Virtus::Attribute::Coercer, where the
|
108
|
-
# coercion method is known.
|
109
|
-
#
|
110
|
-
# @return [Proc(lambda)]
|
111
|
-
#
|
112
|
-
# @api private
|
113
|
-
def attribute_method(configuration)
|
114
|
-
module_options = self.module_options
|
115
|
-
|
116
|
-
lambda do |name, type = Object, options = {}|
|
117
|
-
super(name, type, module_options.merge(options))
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# @api private
|
122
|
-
def module_options
|
123
|
-
{ :coerce => configuration.coerce,
|
124
|
-
:finalize => configuration.finalize,
|
125
|
-
:strict => configuration.strict,
|
126
|
-
:configured_coercer => configuration.coercer }
|
127
|
-
end
|
128
|
-
|
129
|
-
# @api private
|
130
|
-
def core_inclusions
|
131
|
-
[Model::Core]
|
132
|
-
end
|
133
|
-
|
134
|
-
# @api private
|
135
|
-
def core_extensions
|
136
|
-
[]
|
137
|
-
end
|
138
|
-
|
139
|
-
end # class ExtensionBuilder
|
140
|
-
|
141
|
-
# @private
|
142
|
-
class ModelExtensionBuilder < ExtensionBuilder
|
143
|
-
end # ModelExtensionBuilder
|
144
|
-
|
145
|
-
# @private
|
146
|
-
class ModuleExtensionBuilder < ExtensionBuilder
|
147
|
-
|
148
|
-
# @api private
|
149
|
-
def add_included_hook
|
150
|
-
attribute_proc = attribute_method(configuration)
|
151
|
-
inclusions = core_inclusions
|
152
|
-
|
153
|
-
inclusions << Model::Constructor if configuration.constructor
|
154
|
-
inclusions << Model::MassAssignment if configuration.mass_assignment
|
155
|
-
|
156
|
-
self.module.define_singleton_method :included do |object|
|
157
|
-
super(object)
|
158
|
-
object.extend(ModuleExtensions)
|
159
|
-
object.instance_variable_set('@inclusions', inclusions)
|
160
|
-
object.send(:define_singleton_method, :attribute, attribute_proc)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
end # ModuleExtensionBuilder
|
165
|
-
|
166
|
-
# @private
|
167
|
-
class ValueObjectExtensionBuilder < ExtensionBuilder
|
168
|
-
|
169
|
-
# @api private
|
170
|
-
def initialize(configuration, mod = Module.new)
|
171
|
-
super
|
172
|
-
@configuration.constructor = true
|
173
|
-
end
|
174
|
-
|
175
|
-
# @api private
|
176
|
-
def module_options
|
177
|
-
super.update(:writer => :private)
|
178
|
-
end
|
179
|
-
|
180
|
-
# @api private
|
181
|
-
def core_inclusions
|
182
|
-
super << ValueObject::AllowedWriterMethods << ValueObject::InstanceMethods
|
183
|
-
end
|
184
|
-
|
185
|
-
# @api private
|
186
|
-
def core_extensions
|
187
|
-
super << ValueObject::AllowedWriterMethods
|
188
|
-
end
|
189
|
-
|
190
|
-
end # ValueObjectBuilder
|
191
|
-
|
192
|
-
end # module Virtus
|