virtus 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.pelusa.yml +7 -0
- data/.travis.yml +5 -3
- data/Changelog.md +17 -0
- data/Gemfile +4 -0
- data/README.md +35 -39
- data/TODO +12 -7
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/lib/virtus.rb +8 -0
- data/lib/virtus/attribute.rb +8 -29
- data/lib/virtus/attribute/boolean.rb +1 -1
- data/lib/virtus/attribute/default_value.rb +15 -45
- data/lib/virtus/attribute/default_value/from_callable.rb +33 -0
- data/lib/virtus/attribute/default_value/from_clonable.rb +40 -0
- data/lib/virtus/attribute/default_value/from_symbol.rb +35 -0
- data/lib/virtus/attribute/embedded_value.rb +3 -14
- data/lib/virtus/class_methods.rb +17 -0
- data/lib/virtus/coercion/hash.rb +0 -11
- data/lib/virtus/coercion/object.rb +98 -0
- data/lib/virtus/coercion/string.rb +9 -2
- data/lib/virtus/coercion/time_coercions.rb +2 -5
- data/lib/virtus/instance_methods.rb +16 -37
- data/lib/virtus/support/type_lookup.rb +1 -2
- data/lib/virtus/value_object.rb +31 -8
- data/lib/virtus/value_object/equalizer.rb +21 -26
- data/lib/virtus/version.rb +1 -1
- data/spec/integration/custom_attributes_spec.rb +1 -1
- data/spec/integration/default_values_spec.rb +15 -3
- data/spec/integration/defining_attributes_spec.rb +1 -1
- data/spec/integration/mass_assignment_with_accessors_spec.rb +44 -0
- data/spec/integration/virtus/value_object_spec.rb +2 -2
- data/spec/spec_helper.rb +8 -1
- data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +1 -1
- data/spec/unit/virtus/attribute/default_spec.rb +1 -1
- data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +25 -11
- data/spec/unit/virtus/attribute/embedded_value/class_methods/merge_options_spec.rb +1 -1
- data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +1 -1
- data/spec/unit/virtus/class_methods/allowed_writer_methods_spec.rb +25 -0
- data/spec/unit/virtus/coercion/object/class_methods/to_array_spec.rb +51 -0
- data/spec/unit/virtus/coercion/object/class_methods/to_hash_spec.rb +22 -0
- data/spec/unit/virtus/coercion/object/class_methods/to_integer_spec.rb +22 -0
- data/spec/unit/virtus/coercion/object/class_methods/to_string_spec.rb +22 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_constant_spec.rb +37 -1
- data/spec/unit/virtus/instance_methods/attributes_spec.rb +14 -2
- data/spec/unit/virtus/value_object/class_methods/allowed_writer_methods_spec.rb +15 -0
- data/spec/unit/virtus/value_object/class_methods/equalizer_spec.rb +1 -1
- data/spec/unit/virtus/value_object/initialize_spec.rb +1 -1
- data/spec/unit/virtus/value_object/with_spec.rb +1 -1
- metadata +17 -10
- data/spec/unit/virtus/attribute/default_value/instance_methods/evaluate_spec.rb +0 -30
- data/spec/unit/virtus/attribute/instance_variable_name_spec.rb +0 -12
- data/spec/unit/virtus/attribute/reader_visibility_spec.rb +0 -24
- data/spec/unit/virtus/attribute/writer_visibility_spec.rb +0 -24
- data/spec/unit/virtus/coercion/hash/class_methods/to_array_spec.rb +0 -12
@@ -0,0 +1,33 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
class DefaultValue
|
4
|
+
|
5
|
+
# Represents default value evaluated via a callable object
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class FromCallable < DefaultValue
|
9
|
+
|
10
|
+
# Return if the class can handle the value
|
11
|
+
#
|
12
|
+
# @return [Boolean]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.handle?(attribute, value)
|
16
|
+
value.respond_to?(:call)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Evaluates the value via value#call
|
20
|
+
#
|
21
|
+
# @param [Object]
|
22
|
+
#
|
23
|
+
# @return [Object] evaluated value
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def evaluate(instance)
|
27
|
+
@value.call(instance, @attribute)
|
28
|
+
end
|
29
|
+
|
30
|
+
end # class FromCallable
|
31
|
+
end # class DefaultValue
|
32
|
+
end # class Attribute
|
33
|
+
end # module Virtus
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
class DefaultValue
|
4
|
+
|
5
|
+
# Represents default value evaluated via a clonable object
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class FromClonable < DefaultValue
|
9
|
+
SINGLETON_CLASSES = [
|
10
|
+
::NilClass, ::TrueClass, ::FalseClass, ::Numeric, ::Symbol ].freeze
|
11
|
+
|
12
|
+
# Return if the class can handle the value
|
13
|
+
#
|
14
|
+
# @return [Boolean]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
def self.handle?(attribute, value)
|
18
|
+
case value
|
19
|
+
when *SINGLETON_CLASSES
|
20
|
+
false
|
21
|
+
else
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Evaluates the value via value#clone
|
27
|
+
#
|
28
|
+
# @param [Object]
|
29
|
+
#
|
30
|
+
# @return [Object] evaluated value
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def evaluate(instance)
|
34
|
+
@value.clone
|
35
|
+
end
|
36
|
+
|
37
|
+
end # class FromClonable
|
38
|
+
end # class DefaultValue
|
39
|
+
end # class Attribute
|
40
|
+
end # module Virtus
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
class DefaultValue
|
4
|
+
|
5
|
+
# Represents default value evaluated via a symbol
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class FromSymbol < DefaultValue
|
9
|
+
|
10
|
+
# Return if the class can handle the value
|
11
|
+
#
|
12
|
+
# @return [Boolean]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.handle?(attribute, value)
|
16
|
+
value.is_a?(::Symbol)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Evaluates the value via instance#__send__(value)
|
20
|
+
#
|
21
|
+
# Symbol value is returned if the instance doesn't respond to value
|
22
|
+
#
|
23
|
+
# @param [Object]
|
24
|
+
#
|
25
|
+
# @return [Object] evaluated value
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
def evaluate(instance)
|
29
|
+
instance.respond_to?(@value) ? instance.__send__(@value) : @value
|
30
|
+
end
|
31
|
+
|
32
|
+
end # class FromSymbol
|
33
|
+
end # class DefaultValue
|
34
|
+
end # class Attribute
|
35
|
+
end # module Virtus
|
@@ -25,6 +25,7 @@ module Virtus
|
|
25
25
|
# :street => 'Street 1/2', :zipcode => '12345', :city => 'NYC' })
|
26
26
|
#
|
27
27
|
class EmbeddedValue < Object
|
28
|
+
primitive ::OpenStruct
|
28
29
|
|
29
30
|
# @see Attribute.merge_options
|
30
31
|
#
|
@@ -33,19 +34,7 @@ module Virtus
|
|
33
34
|
#
|
34
35
|
# @api private
|
35
36
|
def self.merge_options(type, options)
|
36
|
-
options.merge(:
|
37
|
-
end
|
38
|
-
|
39
|
-
# Sets @model ivar
|
40
|
-
#
|
41
|
-
# @see Virtus::Attribute#initialize
|
42
|
-
#
|
43
|
-
# @return [undefined]
|
44
|
-
#
|
45
|
-
# @api private
|
46
|
-
def initialize(name, options = {})
|
47
|
-
super
|
48
|
-
@model = options.fetch(:model, OpenStruct)
|
37
|
+
options.merge(:primitive => type)
|
49
38
|
end
|
50
39
|
|
51
40
|
# Coerce attributes into a virtus object
|
@@ -57,7 +46,7 @@ module Virtus
|
|
57
46
|
# @api private
|
58
47
|
def coerce(attributes_or_object)
|
59
48
|
value = if attributes_or_object.kind_of?(::Hash)
|
60
|
-
@
|
49
|
+
@primitive.new(attributes_or_object)
|
61
50
|
else
|
62
51
|
attributes_or_object
|
63
52
|
end
|
data/lib/virtus/class_methods.rb
CHANGED
@@ -2,6 +2,8 @@ module Virtus
|
|
2
2
|
|
3
3
|
# Class methods that are added when you include Virtus
|
4
4
|
module ClassMethods
|
5
|
+
WRITER_METHOD_REGEXP = /=\z/.freeze
|
6
|
+
INVALID_WRITER_METHODS = %w[ == != === []= attributes= ].to_set.freeze
|
5
7
|
|
6
8
|
# Hook called when module is extended
|
7
9
|
#
|
@@ -78,6 +80,21 @@ module Virtus
|
|
78
80
|
@attributes = AttributeSet.new(parent)
|
79
81
|
end
|
80
82
|
|
83
|
+
# The list of writer methods that can be mass-assigned to in #attributes=
|
84
|
+
#
|
85
|
+
# @return [Set]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def allowed_writer_methods
|
89
|
+
@allowed_writer_methods ||=
|
90
|
+
begin
|
91
|
+
allowed_writer_methods = public_instance_methods.map(&:to_s)
|
92
|
+
allowed_writer_methods = allowed_writer_methods.grep(WRITER_METHOD_REGEXP).to_set
|
93
|
+
allowed_writer_methods -= INVALID_WRITER_METHODS
|
94
|
+
allowed_writer_methods.freeze
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
81
98
|
protected
|
82
99
|
|
83
100
|
# Set up the anonymous module which will host Attribute accessor methods
|
data/lib/virtus/coercion/hash.rb
CHANGED
@@ -7,17 +7,6 @@ module Virtus
|
|
7
7
|
|
8
8
|
TIME_SEGMENTS = [ :year, :month, :day, :hour, :min, :sec ].freeze
|
9
9
|
|
10
|
-
# Creates an Array instance from a Hash
|
11
|
-
#
|
12
|
-
# @param [Hash] value
|
13
|
-
#
|
14
|
-
# @return [Array]
|
15
|
-
#
|
16
|
-
# @api private
|
17
|
-
def self.to_array(value)
|
18
|
-
value.to_a
|
19
|
-
end
|
20
|
-
|
21
10
|
# Creates a Time instance from a Hash
|
22
11
|
#
|
23
12
|
# Valid keys are: :year, :month, :day, :hour, :min, :sec
|
@@ -7,6 +7,87 @@ module Virtus
|
|
7
7
|
|
8
8
|
COERCION_METHOD_REGEXP = /\Ato_/.freeze
|
9
9
|
|
10
|
+
# Create an Array from any Object
|
11
|
+
#
|
12
|
+
# @example with an object that does not respond to #to_a or #to_ary
|
13
|
+
# Virtus::Coercion::Object.to_array(value) # => [ value ]
|
14
|
+
#
|
15
|
+
# @example with an object that responds to #to_a
|
16
|
+
# Virtus::Coercion::Object.to_array(Set[ value ]) # => [ value ]
|
17
|
+
#
|
18
|
+
# @example with n object that responds to #to_ary
|
19
|
+
# Virtus::Coercion::Object.to_array([ value ]) # => [ value ]
|
20
|
+
#
|
21
|
+
# @param [#to_a,#to_ary,Object] value
|
22
|
+
# @param [#to_a,#to_ary,Object] value
|
23
|
+
#
|
24
|
+
# @return [Array]
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def self.to_array(value)
|
28
|
+
Array(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create a Hash from the Object if possible
|
32
|
+
#
|
33
|
+
# @example with a coercible object
|
34
|
+
# Virtus::Coercion::Object.to_hash(key => value) # => { key => value }
|
35
|
+
#
|
36
|
+
# @example with an object that is not coercible
|
37
|
+
# Virtus::Coercion::Object.to_hash(value) # => value
|
38
|
+
#
|
39
|
+
# @param [#to_hash, Object] value
|
40
|
+
#
|
41
|
+
# @return [Hash]
|
42
|
+
# returns a Hash when the object can be coerced
|
43
|
+
# @return [Object]
|
44
|
+
# returns the value when the object cannot be coerced
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def self.to_hash(value)
|
48
|
+
coerce_with_method(value, :to_hash)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a String from the Object if possible
|
52
|
+
#
|
53
|
+
# @example with a coercible object
|
54
|
+
# Virtus::Coercion::Object.to_string("string") # => "string"
|
55
|
+
#
|
56
|
+
# @example with an object that is not coercible
|
57
|
+
# Virtus::Coercion::Object.to_string(value) # => value
|
58
|
+
#
|
59
|
+
# @param [#to_str, Object] value
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
# returns a String when the object can be coerced
|
63
|
+
# @return [Object]
|
64
|
+
# returns the value when the object cannot be coerced
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def self.to_string(value)
|
68
|
+
coerce_with_method(value, :to_str)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create an Integer from the Object if possible
|
72
|
+
#
|
73
|
+
# @example with a coercible object
|
74
|
+
# Virtus::Coercion::Object.to_integer(1) # => 1
|
75
|
+
#
|
76
|
+
# @example with an object that is not coercible
|
77
|
+
# Virtus::Coercion::Object.to_integer(value) # => value
|
78
|
+
#
|
79
|
+
# @param [#to_int, Object] value
|
80
|
+
#
|
81
|
+
# @return [Integer]
|
82
|
+
# returns an Integer when the object can be coerced
|
83
|
+
# @return [Object]
|
84
|
+
# returns the value when the object cannot be coerced
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def self.to_integer(value)
|
88
|
+
coerce_with_method(value, :to_int)
|
89
|
+
end
|
90
|
+
|
10
91
|
# Passthrough given value
|
11
92
|
#
|
12
93
|
# @param [Object] value
|
@@ -22,6 +103,23 @@ module Virtus
|
|
22
103
|
end
|
23
104
|
end
|
24
105
|
|
106
|
+
private_class_method :method_missing
|
107
|
+
|
108
|
+
# Try to use native coercion method on the given value
|
109
|
+
#
|
110
|
+
# @param [Object] value
|
111
|
+
#
|
112
|
+
# @param [Symbol] method
|
113
|
+
#
|
114
|
+
# @return [Object]
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def self.coerce_with_method(value, method)
|
118
|
+
value.respond_to?(method) ? value.send(method) : value
|
119
|
+
end
|
120
|
+
|
121
|
+
private_class_method :coerce_with_method
|
122
|
+
|
25
123
|
end # class Object
|
26
124
|
end # class Coercion
|
27
125
|
end # module Virtus
|
@@ -25,8 +25,15 @@ module Virtus
|
|
25
25
|
#
|
26
26
|
# @api public
|
27
27
|
def self.to_constant(value)
|
28
|
-
|
29
|
-
|
28
|
+
names = value.split('::')
|
29
|
+
names.shift if names.first.empty?
|
30
|
+
names.inject(::Object) do |mod, name|
|
31
|
+
if mod.const_defined?(name, *EXTRA_CONST_ARGS)
|
32
|
+
mod.const_get(name, *EXTRA_CONST_ARGS)
|
33
|
+
else
|
34
|
+
mod.const_missing(name)
|
35
|
+
end
|
36
|
+
end
|
30
37
|
end
|
31
38
|
|
32
39
|
# Coerce give value to a symbol
|
@@ -73,11 +73,8 @@ module Virtus
|
|
73
73
|
#
|
74
74
|
# @api private
|
75
75
|
def coerce_with_method(value, method)
|
76
|
-
|
77
|
-
|
78
|
-
else
|
79
|
-
String.send(method, to_string(value))
|
80
|
-
end
|
76
|
+
coerced = super
|
77
|
+
coerced.equal?(value) ? String.send(method, to_string(value)) : coerced
|
81
78
|
end
|
82
79
|
|
83
80
|
end # module TimeCoercions
|
@@ -82,17 +82,14 @@ module Virtus
|
|
82
82
|
#
|
83
83
|
# @api public
|
84
84
|
def attributes
|
85
|
-
|
86
|
-
name = attribute.name
|
87
|
-
attributes[name] = self[name] if attribute.public_reader?
|
88
|
-
end
|
85
|
+
get_attributes(&:public_reader?)
|
89
86
|
end
|
90
87
|
|
91
88
|
# Mass-assign attribute values
|
92
89
|
#
|
93
90
|
# Keys in the +attribute_values+ param can be symbols or strings.
|
94
|
-
#
|
95
|
-
# Non-attribute setter methods on the receiver will
|
91
|
+
# All referenced Attribute writer methods *will* be called.
|
92
|
+
# Non-attribute setter methods on the receiver *will* be called.
|
96
93
|
#
|
97
94
|
# @example
|
98
95
|
# class User
|
@@ -112,11 +109,7 @@ module Virtus
|
|
112
109
|
#
|
113
110
|
# @api public
|
114
111
|
def attributes=(attribute_values)
|
115
|
-
|
116
|
-
set_attributes(attribute_values.select { |name,|
|
117
|
-
attribute = attributes[name]
|
118
|
-
attribute && attribute.public_writer?
|
119
|
-
})
|
112
|
+
set_attributes(attribute_values)
|
120
113
|
end
|
121
114
|
|
122
115
|
# Returns a hash of all publicly accessible attributes
|
@@ -141,42 +134,28 @@ module Virtus
|
|
141
134
|
|
142
135
|
private
|
143
136
|
|
144
|
-
#
|
145
|
-
#
|
146
|
-
# Keys in the +attribute_values+ param can be symbols or strings.
|
147
|
-
# All referenced Attribute writer methods *will* be called.
|
148
|
-
# Non-attribute setter methods on the receiver *will* be called.
|
149
|
-
#
|
150
|
-
# @example
|
151
|
-
# class User
|
152
|
-
# include Virtus
|
153
|
-
#
|
154
|
-
# attribute :name, String
|
155
|
-
# attribute :age, Integer
|
156
|
-
# end
|
157
|
-
#
|
158
|
-
# user = User.new
|
159
|
-
# user.attributes = { :name => 'John', 'age' => 28 }
|
160
|
-
#
|
161
|
-
# @param [#to_hash] attribute_values
|
162
|
-
# a hash of attribute names and values to set on the receiver
|
137
|
+
# Get values of all attributes defined for this class, ignoring privacy
|
163
138
|
#
|
164
139
|
# @return [Hash]
|
165
140
|
#
|
166
141
|
# @api private
|
167
|
-
def
|
168
|
-
|
142
|
+
def get_attributes
|
143
|
+
self.class.attributes.each_with_object({}) do |attribute, attributes|
|
144
|
+
name = attribute.name
|
145
|
+
attributes[name] = get_attribute(name) if yield(attribute)
|
146
|
+
end
|
169
147
|
end
|
170
148
|
|
171
|
-
#
|
149
|
+
# Mass-assign attribute values
|
150
|
+
#
|
151
|
+
# @see Virtus::InstanceMethods#attributes=
|
172
152
|
#
|
173
153
|
# @return [Hash]
|
174
154
|
#
|
175
155
|
# @api private
|
176
|
-
def
|
177
|
-
|
178
|
-
|
179
|
-
attributes[attribute_name] = get_attribute(attribute_name)
|
156
|
+
def set_attributes(attribute_values)
|
157
|
+
attribute_values.each do |name, value|
|
158
|
+
set_attribute(name, value) if self.class.allowed_writer_methods.include?("#{name}=")
|
180
159
|
end
|
181
160
|
end
|
182
161
|
|
@@ -3,8 +3,7 @@ module Virtus
|
|
3
3
|
# A module that adds type lookup to a class
|
4
4
|
module TypeLookup
|
5
5
|
|
6
|
-
TYPE_FORMAT
|
7
|
-
EXTRA_CONST_ARGS = RUBY_VERSION < '1.9' || RUBY_ENGINE == 'rbx' ? [] : [ false ]
|
6
|
+
TYPE_FORMAT = /\A[A-Z]\w*\z/.freeze
|
8
7
|
|
9
8
|
# Returns a descendant based on a name or class
|
10
9
|
#
|
data/lib/virtus/value_object.rb
CHANGED
@@ -4,6 +4,7 @@ require 'virtus'
|
|
4
4
|
require 'virtus/value_object/equalizer'
|
5
5
|
|
6
6
|
module Virtus
|
7
|
+
|
7
8
|
# Include this Module for Value Object semantics
|
8
9
|
#
|
9
10
|
# The idea is that instances should be immutable and compared based on state
|
@@ -22,6 +23,7 @@ module Virtus
|
|
22
23
|
# hash = { location => :foo }
|
23
24
|
# hash[same_location] #=> :foo
|
24
25
|
module ValueObject
|
26
|
+
|
25
27
|
# Callback to configure including Class as a Value Object
|
26
28
|
#
|
27
29
|
# Including Class will include Virtus and have additional
|
@@ -41,17 +43,25 @@ module Virtus
|
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
46
|
+
private_class_method :included
|
47
|
+
|
44
48
|
module InstanceMethods
|
49
|
+
# the #get_attributes method accept a Proc object that will filter
|
50
|
+
# out an attribute when the block returns false. the ValueObject
|
51
|
+
# needs all the attributes, so we allow every attribute.
|
52
|
+
FILTER_NONE = proc { true }
|
53
|
+
|
45
54
|
def initialize(attributes = {})
|
46
55
|
set_attributes(attributes)
|
47
56
|
end
|
48
57
|
|
49
58
|
def with(attribute_updates)
|
50
|
-
self.class.new(get_attributes.merge(attribute_updates))
|
59
|
+
self.class.new(get_attributes(&FILTER_NONE).merge(attribute_updates))
|
51
60
|
end
|
52
61
|
end
|
53
62
|
|
54
63
|
module ClassMethods
|
64
|
+
|
55
65
|
# Define an attribute on the receiver
|
56
66
|
#
|
57
67
|
# The Attribute will have private writer methods (eg., immutable instances)
|
@@ -73,7 +83,6 @@ module Virtus
|
|
73
83
|
def attribute(name, type, options = {})
|
74
84
|
equalizer << name
|
75
85
|
options[:writer] = :private
|
76
|
-
|
77
86
|
super
|
78
87
|
end
|
79
88
|
|
@@ -91,14 +100,28 @@ module Virtus
|
|
91
100
|
#
|
92
101
|
# @api public
|
93
102
|
def equalizer
|
94
|
-
|
103
|
+
@equalizer ||=
|
104
|
+
begin
|
105
|
+
equalizer = Equalizer.new(name || inspect)
|
106
|
+
include equalizer
|
107
|
+
equalizer
|
108
|
+
end
|
109
|
+
end
|
95
110
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
111
|
+
# The list of writer methods that can be mass-assigned to in #attributes=
|
112
|
+
#
|
113
|
+
# @return [Set]
|
114
|
+
#
|
115
|
+
# @api private
|
116
|
+
def allowed_writer_methods
|
117
|
+
@allowed_writer_methods ||=
|
118
|
+
begin
|
119
|
+
allowed_writer_methods = super
|
120
|
+
allowed_writer_methods += attributes.map{|attr| "#{attr.name}="}
|
121
|
+
allowed_writer_methods.to_set.freeze
|
122
|
+
end
|
101
123
|
end
|
124
|
+
|
102
125
|
end # module ClassMethods
|
103
126
|
end # module ValueObject
|
104
127
|
end # module Virtus
|