virtus2 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|