virtus2 2.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +39 -0
- data/.rspec +2 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +18 -0
- data/Changelog.md +258 -0
- data/Gemfile +10 -0
- data/Guardfile +19 -0
- data/LICENSE +20 -0
- data/README.md +630 -0
- data/Rakefile +15 -0
- data/TODO.md +6 -0
- data/lib/virtus/attribute/accessor.rb +103 -0
- data/lib/virtus/attribute/boolean.rb +55 -0
- data/lib/virtus/attribute/builder.rb +182 -0
- data/lib/virtus/attribute/coercer.rb +45 -0
- data/lib/virtus/attribute/coercible.rb +20 -0
- data/lib/virtus/attribute/collection.rb +103 -0
- data/lib/virtus/attribute/default_value/from_callable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_clonable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_symbol.rb +35 -0
- data/lib/virtus/attribute/default_value.rb +51 -0
- data/lib/virtus/attribute/embedded_value.rb +67 -0
- data/lib/virtus/attribute/enum.rb +45 -0
- data/lib/virtus/attribute/hash.rb +130 -0
- data/lib/virtus/attribute/lazy_default.rb +18 -0
- data/lib/virtus/attribute/nullify_blank.rb +24 -0
- data/lib/virtus/attribute/strict.rb +26 -0
- data/lib/virtus/attribute.rb +245 -0
- data/lib/virtus/attribute_set.rb +240 -0
- data/lib/virtus/builder/hook_context.rb +51 -0
- data/lib/virtus/builder.rb +133 -0
- data/lib/virtus/class_inclusions.rb +48 -0
- data/lib/virtus/class_methods.rb +90 -0
- data/lib/virtus/coercer.rb +41 -0
- data/lib/virtus/configuration.rb +72 -0
- data/lib/virtus/const_missing_extensions.rb +18 -0
- data/lib/virtus/extensions.rb +105 -0
- data/lib/virtus/instance_methods.rb +218 -0
- data/lib/virtus/model.rb +68 -0
- data/lib/virtus/module_extensions.rb +88 -0
- data/lib/virtus/support/equalizer.rb +128 -0
- data/lib/virtus/support/options.rb +113 -0
- data/lib/virtus/support/type_lookup.rb +109 -0
- data/lib/virtus/value_object.rb +150 -0
- data/lib/virtus/version.rb +3 -0
- data/lib/virtus.rb +310 -0
- data/spec/integration/attributes_attribute_spec.rb +28 -0
- data/spec/integration/building_module_spec.rb +90 -0
- data/spec/integration/collection_member_coercion_spec.rb +96 -0
- data/spec/integration/custom_attributes_spec.rb +42 -0
- data/spec/integration/custom_collection_attributes_spec.rb +101 -0
- data/spec/integration/default_values_spec.rb +87 -0
- data/spec/integration/defining_attributes_spec.rb +86 -0
- data/spec/integration/embedded_value_spec.rb +50 -0
- data/spec/integration/extending_objects_spec.rb +35 -0
- data/spec/integration/hash_attributes_coercion_spec.rb +54 -0
- data/spec/integration/inheritance_spec.rb +42 -0
- data/spec/integration/injectible_coercers_spec.rb +48 -0
- data/spec/integration/mass_assignment_with_accessors_spec.rb +44 -0
- data/spec/integration/overriding_virtus_spec.rb +46 -0
- data/spec/integration/required_attributes_spec.rb +25 -0
- data/spec/integration/struct_as_embedded_value_spec.rb +28 -0
- data/spec/integration/using_modules_spec.rb +55 -0
- data/spec/integration/value_object_with_custom_constructor_spec.rb +42 -0
- data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
- data/spec/integration/virtus/value_object_spec.rb +99 -0
- data/spec/shared/constants_helpers.rb +9 -0
- data/spec/shared/freeze_method_behavior.rb +40 -0
- data/spec/shared/idempotent_method_behaviour.rb +5 -0
- data/spec/shared/options_class_method.rb +19 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +43 -0
- data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +25 -0
- data/spec/unit/virtus/attribute/class_methods/build_spec.rb +180 -0
- data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +32 -0
- data/spec/unit/virtus/attribute/coerce_spec.rb +129 -0
- data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +105 -0
- data/spec/unit/virtus/attribute/collection/coerce_spec.rb +74 -0
- data/spec/unit/virtus/attribute/collection/value_coerced_predicate_spec.rb +31 -0
- data/spec/unit/virtus/attribute/comparison_spec.rb +20 -0
- data/spec/unit/virtus/attribute/custom_collection_spec.rb +29 -0
- data/spec/unit/virtus/attribute/defined_spec.rb +20 -0
- data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +70 -0
- data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +91 -0
- data/spec/unit/virtus/attribute/get_spec.rb +32 -0
- data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +106 -0
- data/spec/unit/virtus/attribute/hash/coerce_spec.rb +92 -0
- data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/rename_spec.rb +16 -0
- data/spec/unit/virtus/attribute/required_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute/set_default_value_spec.rb +107 -0
- data/spec/unit/virtus/attribute/set_spec.rb +29 -0
- data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute_set/append_spec.rb +47 -0
- data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/each_spec.rb +65 -0
- data/spec/unit/virtus/attribute_set/element_reference_spec.rb +17 -0
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +64 -0
- data/spec/unit/virtus/attribute_set/merge_spec.rb +34 -0
- data/spec/unit/virtus/attribute_set/reset_spec.rb +71 -0
- data/spec/unit/virtus/attribute_spec.rb +229 -0
- data/spec/unit/virtus/attributes_reader_spec.rb +41 -0
- data/spec/unit/virtus/attributes_writer_spec.rb +51 -0
- data/spec/unit/virtus/class_methods/finalize_spec.rb +67 -0
- data/spec/unit/virtus/class_methods/new_spec.rb +39 -0
- data/spec/unit/virtus/config_spec.rb +13 -0
- data/spec/unit/virtus/element_reader_spec.rb +21 -0
- data/spec/unit/virtus/element_writer_spec.rb +19 -0
- data/spec/unit/virtus/freeze_spec.rb +41 -0
- data/spec/unit/virtus/model_spec.rb +197 -0
- data/spec/unit/virtus/module_spec.rb +174 -0
- data/spec/unit/virtus/set_default_attributes_spec.rb +32 -0
- data/spec/unit/virtus/value_object_spec.rb +138 -0
- data/virtus2.gemspec +26 -0
- metadata +225 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
# Attribute extension which raises CoercionError when coercion failed
|
5
|
+
#
|
6
|
+
module Strict
|
7
|
+
|
8
|
+
# @see [Attribute#coerce]
|
9
|
+
#
|
10
|
+
# @raises [CoercionError] when coercer failed
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
def coerce(*)
|
14
|
+
output = super
|
15
|
+
|
16
|
+
if value_coerced?(output) || !required? && output.nil?
|
17
|
+
output
|
18
|
+
else
|
19
|
+
raise CoercionError.new(output, self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end # Strict
|
24
|
+
|
25
|
+
end # Attribute
|
26
|
+
end # Virtus
|
@@ -0,0 +1,245 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Attribute objects handle coercion and provide interface to hook into an
|
4
|
+
# attribute set instance that's included into a class or object
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# # non-strict mode
|
9
|
+
# attr = Virtus::Attribute.build(Integer)
|
10
|
+
# attr.coerce('1')
|
11
|
+
# # => 1
|
12
|
+
#
|
13
|
+
# # strict mode
|
14
|
+
# attr = Virtus::Attribute.build(Integer, :strict => true)
|
15
|
+
# attr.coerce('not really coercible')
|
16
|
+
# # => Virtus::CoercionError: Failed to coerce "not really coercible" into Integer
|
17
|
+
#
|
18
|
+
class Attribute
|
19
|
+
extend DescendantsTracker, Options, TypeLookup
|
20
|
+
|
21
|
+
include Equalizer.new(inspect) << :type << :options
|
22
|
+
|
23
|
+
accept_options :primitive, :accessor, :default, :lazy, :strict, :required, :finalize, :nullify_blank
|
24
|
+
|
25
|
+
strict false
|
26
|
+
required true
|
27
|
+
accessor :public
|
28
|
+
finalize true
|
29
|
+
nullify_blank false
|
30
|
+
|
31
|
+
# @see Virtus.coerce
|
32
|
+
#
|
33
|
+
# @deprecated
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def self.coerce(value = Undefined)
|
37
|
+
Virtus.warn "#{self}.coerce is deprecated and will be removed in 1.0.0. Use Virtus.coerce instead: ##{caller.first}"
|
38
|
+
return Virtus.coerce if value.equal?(Undefined)
|
39
|
+
Virtus.coerce = value
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return type of this attribute
|
44
|
+
#
|
45
|
+
# @return [Axiom::Types::Type]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
attr_reader :type
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
attr_reader :primitive, :options, :default_value, :coercer
|
52
|
+
|
53
|
+
# Builds an attribute instance
|
54
|
+
#
|
55
|
+
# @param [Class,Array,Hash,String,Symbol] type
|
56
|
+
# this can be an explicit class or an object from which virtus can infer
|
57
|
+
# the type
|
58
|
+
#
|
59
|
+
# @param [#to_hash] options
|
60
|
+
# optional extra options hash
|
61
|
+
#
|
62
|
+
# @return [Attribute]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def self.build(type, options = {})
|
66
|
+
Builder.call(type, options)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @api private
|
70
|
+
def self.build_coercer(type, options = {})
|
71
|
+
Coercer.new(type, options.fetch(:configured_coercer) { Virtus.coercer })
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
def self.build_type(definition)
|
76
|
+
Axiom::Types.infer(definition.primitive)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @api private
|
80
|
+
def self.merge_options!(*)
|
81
|
+
# noop
|
82
|
+
end
|
83
|
+
|
84
|
+
# @api private
|
85
|
+
def initialize(type, options)
|
86
|
+
@type = type
|
87
|
+
@primitive = type.primitive
|
88
|
+
@options = options
|
89
|
+
@default_value = options.fetch(:default_value)
|
90
|
+
@coercer = options.fetch(:coercer)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Coerce the input into the expected type
|
94
|
+
#
|
95
|
+
# @example
|
96
|
+
#
|
97
|
+
# attr = Virtus::Attribute.build(String)
|
98
|
+
# attr.coerce(:one) # => 'one'
|
99
|
+
#
|
100
|
+
# @param [Object] input
|
101
|
+
#
|
102
|
+
# @api public
|
103
|
+
def coerce(input)
|
104
|
+
coercer.call(input)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return a new attribute with the new name
|
108
|
+
#
|
109
|
+
# @param [Symbol] name
|
110
|
+
#
|
111
|
+
# @return [Attribute]
|
112
|
+
#
|
113
|
+
# @api public
|
114
|
+
def rename(name)
|
115
|
+
self.class.build(type, options.merge(:name => name))
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return if the given value was coerced
|
119
|
+
#
|
120
|
+
# @param [Object] value
|
121
|
+
#
|
122
|
+
# @return [Boolean]
|
123
|
+
#
|
124
|
+
# @api public
|
125
|
+
def value_coerced?(value)
|
126
|
+
coercer.success?(primitive, value)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Return if the attribute is coercible
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
#
|
133
|
+
# attr = Virtus::Attribute.build(String, :coerce => true)
|
134
|
+
# attr.coercible? # => true
|
135
|
+
#
|
136
|
+
# attr = Virtus::Attribute.build(String, :coerce => false)
|
137
|
+
# attr.coercible? # => false
|
138
|
+
#
|
139
|
+
# @return [Boolean]
|
140
|
+
#
|
141
|
+
# @api public
|
142
|
+
def coercible?
|
143
|
+
kind_of?(Coercible)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Return if the attribute has lazy default value evaluation
|
147
|
+
#
|
148
|
+
# @example
|
149
|
+
#
|
150
|
+
# attr = Virtus::Attribute.build(String, :lazy => true)
|
151
|
+
# attr.lazy? # => true
|
152
|
+
#
|
153
|
+
# attr = Virtus::Attribute.build(String, :lazy => false)
|
154
|
+
# attr.lazy? # => false
|
155
|
+
#
|
156
|
+
# @return [Boolean]
|
157
|
+
#
|
158
|
+
# @api public
|
159
|
+
def lazy?
|
160
|
+
kind_of?(LazyDefault)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Return if the attribute is in the strict coercion mode
|
164
|
+
#
|
165
|
+
# @example
|
166
|
+
#
|
167
|
+
# attr = Virtus::Attribute.build(String, :strict => true)
|
168
|
+
# attr.strict? # => true
|
169
|
+
#
|
170
|
+
# attr = Virtus::Attribute.build(String, :strict => false)
|
171
|
+
# attr.strict? # => false
|
172
|
+
#
|
173
|
+
# @return [Boolean]
|
174
|
+
#
|
175
|
+
# @api public
|
176
|
+
def strict?
|
177
|
+
kind_of?(Strict)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Return if the attribute is in the nullify blank coercion mode
|
181
|
+
#
|
182
|
+
# @example
|
183
|
+
#
|
184
|
+
# attr = Virtus::Attribute.build(String, :nullify_blank => true)
|
185
|
+
# attr.nullify_blank? # => true
|
186
|
+
#
|
187
|
+
# attr = Virtus::Attribute.build(String, :nullify_blank => false)
|
188
|
+
# attr.nullify_blank? # => false
|
189
|
+
#
|
190
|
+
# @return [Boolean]
|
191
|
+
#
|
192
|
+
# @api public
|
193
|
+
def nullify_blank?
|
194
|
+
kind_of?(NullifyBlank)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Return if the attribute is accepts nil values as valid coercion output
|
198
|
+
#
|
199
|
+
# @example
|
200
|
+
#
|
201
|
+
# attr = Virtus::Attribute.build(String, :required => true)
|
202
|
+
# attr.required? # => true
|
203
|
+
#
|
204
|
+
# attr = Virtus::Attribute.build(String, :required => false)
|
205
|
+
# attr.required? # => false
|
206
|
+
#
|
207
|
+
# @return [Boolean]
|
208
|
+
#
|
209
|
+
# @api public
|
210
|
+
def required?
|
211
|
+
options[:required]
|
212
|
+
end
|
213
|
+
|
214
|
+
# Return if the attribute was already finalized
|
215
|
+
#
|
216
|
+
# @example
|
217
|
+
#
|
218
|
+
# attr = Virtus::Attribute.build(String, :finalize => true)
|
219
|
+
# attr.finalized? # => true
|
220
|
+
#
|
221
|
+
# attr = Virtus::Attribute.build(String, :finalize => false)
|
222
|
+
# attr.finalized? # => false
|
223
|
+
#
|
224
|
+
# @return [Boolean]
|
225
|
+
#
|
226
|
+
# @api public
|
227
|
+
def finalized?
|
228
|
+
frozen?
|
229
|
+
end
|
230
|
+
|
231
|
+
# @api private
|
232
|
+
def define_accessor_methods(attribute_set)
|
233
|
+
attribute_set.define_reader_method(self, name, options[:reader])
|
234
|
+
attribute_set.define_writer_method(self, "#{name}=", options[:writer])
|
235
|
+
end
|
236
|
+
|
237
|
+
# @api private
|
238
|
+
def finalize
|
239
|
+
freeze
|
240
|
+
self
|
241
|
+
end
|
242
|
+
|
243
|
+
end # class Attribute
|
244
|
+
|
245
|
+
end # module Virtus
|
@@ -0,0 +1,240 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# A set of Attribute objects
|
4
|
+
class AttributeSet < Module
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
def self.create(descendant)
|
9
|
+
if descendant.respond_to?(:superclass) && descendant.superclass.respond_to?(:attribute_set)
|
10
|
+
parent = descendant.superclass.public_send(:attribute_set)
|
11
|
+
end
|
12
|
+
descendant.instance_variable_set('@attribute_set', AttributeSet.new(parent))
|
13
|
+
end
|
14
|
+
|
15
|
+
# Initialize an AttributeSet
|
16
|
+
#
|
17
|
+
# @param [AttributeSet] parent
|
18
|
+
# @param [Array] attributes
|
19
|
+
#
|
20
|
+
# @return [undefined]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def initialize(parent = nil, attributes = [])
|
24
|
+
@parent = parent
|
25
|
+
@attributes = attributes.dup
|
26
|
+
@index = {}
|
27
|
+
reset
|
28
|
+
end
|
29
|
+
|
30
|
+
# Iterate over each attribute in the set
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# attribute_set = AttributeSet.new(attributes, parent)
|
34
|
+
# attribute_set.each { |attribute| ... }
|
35
|
+
#
|
36
|
+
# @yield [attribute]
|
37
|
+
#
|
38
|
+
# @yieldparam [Attribute] attribute
|
39
|
+
# each attribute in the set
|
40
|
+
#
|
41
|
+
# @return [self]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def each
|
45
|
+
return to_enum unless block_given?
|
46
|
+
@index.each { |name, attribute| yield attribute if name.kind_of?(Symbol) }
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Adds the attributes to the set
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# attribute_set.merge(attributes)
|
54
|
+
#
|
55
|
+
# @param [Array<Attribute>] attributes
|
56
|
+
#
|
57
|
+
# @return [self]
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def merge(attributes)
|
61
|
+
attributes.each { |attribute| self << attribute }
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Adds an attribute to the set
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# attribute_set << attribute
|
69
|
+
#
|
70
|
+
# @param [Attribute] attribute
|
71
|
+
#
|
72
|
+
# @return [self]
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def <<(attribute)
|
76
|
+
self[attribute.name] = attribute
|
77
|
+
attribute.define_accessor_methods(self) if attribute.finalized?
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# Get an attribute by name
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# attribute_set[:name] # => Attribute object
|
85
|
+
#
|
86
|
+
# @param [Symbol] name
|
87
|
+
#
|
88
|
+
# @return [Attribute]
|
89
|
+
#
|
90
|
+
# @api public
|
91
|
+
def [](name)
|
92
|
+
@index[name]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Set an attribute by name
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# attribute_set[:name] = attribute
|
99
|
+
#
|
100
|
+
# @param [Symbol] name
|
101
|
+
# @param [Attribute] attribute
|
102
|
+
#
|
103
|
+
# @return [Attribute]
|
104
|
+
#
|
105
|
+
# @api public
|
106
|
+
def []=(name, attribute)
|
107
|
+
@attributes << attribute
|
108
|
+
update_index(name, attribute)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Reset the index when the parent is updated
|
112
|
+
#
|
113
|
+
# @return [self]
|
114
|
+
#
|
115
|
+
# @api private
|
116
|
+
def reset
|
117
|
+
merge_attributes(@parent) if @parent
|
118
|
+
merge_attributes(@attributes)
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
# Defines an attribute reader method
|
123
|
+
#
|
124
|
+
# @param [Attribute] attribute
|
125
|
+
# @param [Symbol] method_name
|
126
|
+
# @param [Symbol] visibility
|
127
|
+
#
|
128
|
+
# @return [undefined]
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
def define_reader_method(attribute, method_name, visibility)
|
132
|
+
define_method(method_name) { attribute.get(self) }
|
133
|
+
send(visibility, method_name)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Defines an attribute writer method
|
137
|
+
#
|
138
|
+
# @param [Attribute] attribute
|
139
|
+
# @param [Symbol] method_name
|
140
|
+
# @param [Symbol] visibility
|
141
|
+
#
|
142
|
+
# @return [undefined]
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
def define_writer_method(attribute, method_name, visibility)
|
146
|
+
define_method(method_name) { |value| attribute.set(self, value) }
|
147
|
+
send(visibility, method_name)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Get values of all attributes defined for this class, ignoring privacy
|
151
|
+
#
|
152
|
+
# @return [Hash]
|
153
|
+
#
|
154
|
+
# @api private
|
155
|
+
def get(object)
|
156
|
+
each_with_object({}) do |attribute, attributes|
|
157
|
+
name = attribute.name
|
158
|
+
attributes[name] = object.__send__(name) if attribute.public_reader?
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Mass-assign attribute values
|
163
|
+
#
|
164
|
+
# @see Virtus::InstanceMethods#attributes=
|
165
|
+
#
|
166
|
+
# @return [Hash]
|
167
|
+
#
|
168
|
+
# @api private
|
169
|
+
def set(object, attributes)
|
170
|
+
coerce(attributes).each do |name, value|
|
171
|
+
writer_name = "#{name}="
|
172
|
+
if object.allowed_writer_methods.include?(writer_name)
|
173
|
+
object.__send__(writer_name, value)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Set default attributes
|
179
|
+
#
|
180
|
+
# @return [self]
|
181
|
+
#
|
182
|
+
# @api private
|
183
|
+
def set_defaults(object, filter = method(:skip_default?))
|
184
|
+
each do |attribute|
|
185
|
+
next if filter.call(object, attribute)
|
186
|
+
attribute.set_default_value(object)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Coerce attributes received to a hash
|
191
|
+
#
|
192
|
+
# @return [Hash]
|
193
|
+
#
|
194
|
+
# @api private
|
195
|
+
def coerce(attributes)
|
196
|
+
::Hash.try_convert(attributes) or raise(
|
197
|
+
NoMethodError, "Expected #{attributes.inspect} to respond to #to_hash"
|
198
|
+
)
|
199
|
+
end
|
200
|
+
|
201
|
+
# @api private
|
202
|
+
def finalize
|
203
|
+
each do |attribute|
|
204
|
+
self << attribute.finalize unless attribute.finalized?
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
# @api private
|
211
|
+
def skip_default?(object, attribute)
|
212
|
+
attribute.lazy? || attribute.defined?(object)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Merge the attributes into the index
|
216
|
+
#
|
217
|
+
# @param [Array<Attribute>] attributes
|
218
|
+
#
|
219
|
+
# @return [undefined]
|
220
|
+
#
|
221
|
+
# @api private
|
222
|
+
def merge_attributes(attributes)
|
223
|
+
attributes.each { |attribute| update_index(attribute.name, attribute) }
|
224
|
+
end
|
225
|
+
|
226
|
+
# Update the symbol and string indexes with the attribute
|
227
|
+
#
|
228
|
+
# @param [Symbol] name
|
229
|
+
#
|
230
|
+
# @param [Attribute] attribute
|
231
|
+
#
|
232
|
+
# @return [undefined]
|
233
|
+
#
|
234
|
+
# @api private
|
235
|
+
def update_index(name, attribute)
|
236
|
+
@index[name] = @index[name.to_s.freeze] = attribute
|
237
|
+
end
|
238
|
+
|
239
|
+
end # class AttributeSet
|
240
|
+
end # module Virtus
|