omg-activemodel 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +67 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +266 -0
- data/lib/active_model/access.rb +16 -0
- data/lib/active_model/api.rb +99 -0
- data/lib/active_model/attribute/user_provided_default.rb +55 -0
- data/lib/active_model/attribute.rb +277 -0
- data/lib/active_model/attribute_assignment.rb +78 -0
- data/lib/active_model/attribute_methods.rb +592 -0
- data/lib/active_model/attribute_mutation_tracker.rb +189 -0
- data/lib/active_model/attribute_registration.rb +117 -0
- data/lib/active_model/attribute_set/builder.rb +182 -0
- data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
- data/lib/active_model/attribute_set.rb +118 -0
- data/lib/active_model/attributes.rb +165 -0
- data/lib/active_model/callbacks.rb +155 -0
- data/lib/active_model/conversion.rb +121 -0
- data/lib/active_model/deprecator.rb +7 -0
- data/lib/active_model/dirty.rb +416 -0
- data/lib/active_model/error.rb +208 -0
- data/lib/active_model/errors.rb +547 -0
- data/lib/active_model/forbidden_attributes_protection.rb +33 -0
- data/lib/active_model/gem_version.rb +17 -0
- data/lib/active_model/lint.rb +118 -0
- data/lib/active_model/locale/en.yml +38 -0
- data/lib/active_model/model.rb +78 -0
- data/lib/active_model/naming.rb +359 -0
- data/lib/active_model/nested_error.rb +22 -0
- data/lib/active_model/railtie.rb +24 -0
- data/lib/active_model/secure_password.rb +231 -0
- data/lib/active_model/serialization.rb +198 -0
- data/lib/active_model/serializers/json.rb +154 -0
- data/lib/active_model/translation.rb +78 -0
- data/lib/active_model/type/big_integer.rb +36 -0
- data/lib/active_model/type/binary.rb +62 -0
- data/lib/active_model/type/boolean.rb +48 -0
- data/lib/active_model/type/date.rb +78 -0
- data/lib/active_model/type/date_time.rb +88 -0
- data/lib/active_model/type/decimal.rb +107 -0
- data/lib/active_model/type/float.rb +64 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +53 -0
- data/lib/active_model/type/helpers/mutable.rb +24 -0
- data/lib/active_model/type/helpers/numeric.rb +61 -0
- data/lib/active_model/type/helpers/time_value.rb +127 -0
- data/lib/active_model/type/helpers/timezone.rb +23 -0
- data/lib/active_model/type/helpers.rb +7 -0
- data/lib/active_model/type/immutable_string.rb +71 -0
- data/lib/active_model/type/integer.rb +113 -0
- data/lib/active_model/type/registry.rb +37 -0
- data/lib/active_model/type/serialize_cast_value.rb +47 -0
- data/lib/active_model/type/string.rb +43 -0
- data/lib/active_model/type/time.rb +87 -0
- data/lib/active_model/type/value.rb +157 -0
- data/lib/active_model/type.rb +55 -0
- data/lib/active_model/validations/absence.rb +33 -0
- data/lib/active_model/validations/acceptance.rb +113 -0
- data/lib/active_model/validations/callbacks.rb +119 -0
- data/lib/active_model/validations/clusivity.rb +54 -0
- data/lib/active_model/validations/comparability.rb +18 -0
- data/lib/active_model/validations/comparison.rb +90 -0
- data/lib/active_model/validations/confirmation.rb +80 -0
- data/lib/active_model/validations/exclusion.rb +49 -0
- data/lib/active_model/validations/format.rb +112 -0
- data/lib/active_model/validations/helper_methods.rb +15 -0
- data/lib/active_model/validations/inclusion.rb +47 -0
- data/lib/active_model/validations/length.rb +130 -0
- data/lib/active_model/validations/numericality.rb +222 -0
- data/lib/active_model/validations/presence.rb +39 -0
- data/lib/active_model/validations/resolve_value.rb +26 -0
- data/lib/active_model/validations/validates.rb +175 -0
- data/lib/active_model/validations/with.rb +154 -0
- data/lib/active_model/validations.rb +489 -0
- data/lib/active_model/validator.rb +190 -0
- data/lib/active_model/version.rb +10 -0
- data/lib/active_model.rb +84 -0
- metadata +139 -0
@@ -0,0 +1,277 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/duplicable"
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
class Attribute # :nodoc:
|
7
|
+
class << self
|
8
|
+
def from_database(name, value_before_type_cast, type, value = nil)
|
9
|
+
FromDatabase.new(name, value_before_type_cast, type, nil, value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_user(name, value_before_type_cast, type, original_attribute = nil)
|
13
|
+
FromUser.new(name, value_before_type_cast, type, original_attribute)
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_cast_value(name, value_before_type_cast, type)
|
17
|
+
WithCastValue.new(name, value_before_type_cast, type)
|
18
|
+
end
|
19
|
+
|
20
|
+
def null(name)
|
21
|
+
Null.new(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def uninitialized(name, type)
|
25
|
+
Uninitialized.new(name, type)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :name, :value_before_type_cast, :type
|
30
|
+
|
31
|
+
# This method should not be called directly.
|
32
|
+
# Use #from_database or #from_user
|
33
|
+
def initialize(name, value_before_type_cast, type, original_attribute = nil, value = nil)
|
34
|
+
@name = name
|
35
|
+
@value_before_type_cast = value_before_type_cast
|
36
|
+
@type = type
|
37
|
+
@original_attribute = original_attribute
|
38
|
+
@value = value unless value.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def value
|
42
|
+
# `defined?` is cheaper than `||=` when we get back falsy values
|
43
|
+
@value = type_cast(value_before_type_cast) unless defined?(@value)
|
44
|
+
@value
|
45
|
+
end
|
46
|
+
|
47
|
+
def original_value
|
48
|
+
if assigned?
|
49
|
+
original_attribute.original_value
|
50
|
+
else
|
51
|
+
type_cast(value_before_type_cast)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def value_for_database
|
56
|
+
if !defined?(@value_for_database) || type.changed_in_place?(@value_for_database, value)
|
57
|
+
@value_for_database = _value_for_database
|
58
|
+
end
|
59
|
+
@value_for_database
|
60
|
+
end
|
61
|
+
|
62
|
+
def serializable?(&block)
|
63
|
+
type.serializable?(value, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def changed?
|
67
|
+
changed_from_assignment? || changed_in_place?
|
68
|
+
end
|
69
|
+
|
70
|
+
def changed_in_place?
|
71
|
+
has_been_read? && type.changed_in_place?(original_value_for_database, value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def forgetting_assignment
|
75
|
+
with_value_from_database(value_for_database)
|
76
|
+
end
|
77
|
+
|
78
|
+
def with_value_from_user(value)
|
79
|
+
type.assert_valid_value(value)
|
80
|
+
self.class.from_user(name, value, type, original_attribute || self)
|
81
|
+
end
|
82
|
+
|
83
|
+
def with_value_from_database(value)
|
84
|
+
self.class.from_database(name, value, type)
|
85
|
+
end
|
86
|
+
|
87
|
+
def with_cast_value(value)
|
88
|
+
self.class.with_cast_value(name, value, type)
|
89
|
+
end
|
90
|
+
|
91
|
+
def with_type(type)
|
92
|
+
if changed_in_place?
|
93
|
+
with_value_from_user(value).with_type(type)
|
94
|
+
else
|
95
|
+
self.class.new(name, value_before_type_cast, type, original_attribute)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def type_cast(*)
|
100
|
+
raise NotImplementedError
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialized?
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
def came_from_user?
|
108
|
+
false
|
109
|
+
end
|
110
|
+
|
111
|
+
def has_been_read?
|
112
|
+
defined?(@value)
|
113
|
+
end
|
114
|
+
|
115
|
+
def ==(other)
|
116
|
+
self.class == other.class &&
|
117
|
+
name == other.name &&
|
118
|
+
value_before_type_cast == other.value_before_type_cast &&
|
119
|
+
type == other.type
|
120
|
+
end
|
121
|
+
alias eql? ==
|
122
|
+
|
123
|
+
def hash
|
124
|
+
[self.class, name, value_before_type_cast, type].hash
|
125
|
+
end
|
126
|
+
|
127
|
+
def init_with(coder)
|
128
|
+
@name = coder["name"]
|
129
|
+
@value_before_type_cast = coder["value_before_type_cast"]
|
130
|
+
@type = coder["type"]
|
131
|
+
@original_attribute = coder["original_attribute"]
|
132
|
+
@value = coder["value"] if coder.map.key?("value")
|
133
|
+
end
|
134
|
+
|
135
|
+
def encode_with(coder)
|
136
|
+
coder["name"] = name
|
137
|
+
coder["value_before_type_cast"] = value_before_type_cast unless value_before_type_cast.nil?
|
138
|
+
coder["type"] = type if type
|
139
|
+
coder["original_attribute"] = original_attribute if original_attribute
|
140
|
+
coder["value"] = value if defined?(@value)
|
141
|
+
end
|
142
|
+
|
143
|
+
def original_value_for_database
|
144
|
+
if assigned?
|
145
|
+
original_attribute.original_value_for_database
|
146
|
+
else
|
147
|
+
_original_value_for_database
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
attr_reader :original_attribute
|
153
|
+
alias :assigned? :original_attribute
|
154
|
+
|
155
|
+
def initialize_dup(other)
|
156
|
+
if @value&.duplicable?
|
157
|
+
@value = @value.dup
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def changed_from_assignment?
|
162
|
+
assigned? && type.changed?(original_value, value, value_before_type_cast)
|
163
|
+
end
|
164
|
+
|
165
|
+
def _value_for_database
|
166
|
+
type.serialize(value)
|
167
|
+
end
|
168
|
+
|
169
|
+
def _original_value_for_database
|
170
|
+
type.serialize(original_value)
|
171
|
+
end
|
172
|
+
|
173
|
+
class FromDatabase < Attribute # :nodoc:
|
174
|
+
def type_cast(value)
|
175
|
+
type.deserialize(value)
|
176
|
+
end
|
177
|
+
|
178
|
+
def forgetting_assignment
|
179
|
+
# If this attribute was not persisted (with a `value_for_database`
|
180
|
+
# that might differ from `value_before_type_cast`) and `value` has not
|
181
|
+
# changed in place, we can use the existing `value_before_type_cast`
|
182
|
+
# to avoid deserialize / cast / serialize calls from computing the new
|
183
|
+
# attribute's `value_before_type_cast`.
|
184
|
+
if !defined?(@value_for_database) && !changed_in_place?
|
185
|
+
with_value_from_database(value_before_type_cast)
|
186
|
+
else
|
187
|
+
super
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
def _original_value_for_database
|
193
|
+
value_before_type_cast
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class FromUser < Attribute # :nodoc:
|
198
|
+
def type_cast(value)
|
199
|
+
type.cast(value)
|
200
|
+
end
|
201
|
+
|
202
|
+
def came_from_user?
|
203
|
+
!type.value_constructed_by_mass_assignment?(value_before_type_cast)
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
def _value_for_database
|
208
|
+
Type::SerializeCastValue.serialize(type, value)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class WithCastValue < Attribute # :nodoc:
|
213
|
+
def type_cast(value)
|
214
|
+
value
|
215
|
+
end
|
216
|
+
|
217
|
+
def changed_in_place?
|
218
|
+
false
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Null < Attribute # :nodoc:
|
223
|
+
def initialize(name)
|
224
|
+
super(name, nil, Type.default_value)
|
225
|
+
end
|
226
|
+
|
227
|
+
def type_cast(*)
|
228
|
+
nil
|
229
|
+
end
|
230
|
+
|
231
|
+
def with_type(type)
|
232
|
+
self.class.with_cast_value(name, nil, type)
|
233
|
+
end
|
234
|
+
|
235
|
+
def with_value_from_database(value)
|
236
|
+
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
|
237
|
+
end
|
238
|
+
alias_method :with_value_from_user, :with_value_from_database
|
239
|
+
alias_method :with_cast_value, :with_value_from_database
|
240
|
+
end
|
241
|
+
|
242
|
+
class Uninitialized < Attribute # :nodoc:
|
243
|
+
UNINITIALIZED_ORIGINAL_VALUE = Object.new
|
244
|
+
|
245
|
+
def initialize(name, type)
|
246
|
+
super(name, nil, type)
|
247
|
+
end
|
248
|
+
|
249
|
+
def value
|
250
|
+
if block_given?
|
251
|
+
yield name
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def original_value
|
256
|
+
UNINITIALIZED_ORIGINAL_VALUE
|
257
|
+
end
|
258
|
+
|
259
|
+
def value_for_database
|
260
|
+
end
|
261
|
+
|
262
|
+
def initialized?
|
263
|
+
false
|
264
|
+
end
|
265
|
+
|
266
|
+
def forgetting_assignment
|
267
|
+
dup
|
268
|
+
end
|
269
|
+
|
270
|
+
def with_type(type)
|
271
|
+
self.class.new(name, type)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/keys"
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
module AttributeAssignment
|
7
|
+
include ActiveModel::ForbiddenAttributesProtection
|
8
|
+
|
9
|
+
# Allows you to set all the attributes by passing in a hash of attributes with
|
10
|
+
# keys matching the attribute names.
|
11
|
+
#
|
12
|
+
# If the passed hash responds to <tt>permitted?</tt> method and the return value
|
13
|
+
# of this method is +false+ an ActiveModel::ForbiddenAttributesError
|
14
|
+
# exception is raised.
|
15
|
+
#
|
16
|
+
# class Cat
|
17
|
+
# include ActiveModel::AttributeAssignment
|
18
|
+
# attr_accessor :name, :status
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# cat = Cat.new
|
22
|
+
# cat.assign_attributes(name: "Gorby", status: "yawning")
|
23
|
+
# cat.name # => 'Gorby'
|
24
|
+
# cat.status # => 'yawning'
|
25
|
+
# cat.assign_attributes(status: "sleeping")
|
26
|
+
# cat.name # => 'Gorby'
|
27
|
+
# cat.status # => 'sleeping'
|
28
|
+
def assign_attributes(new_attributes)
|
29
|
+
unless new_attributes.respond_to?(:each_pair)
|
30
|
+
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{new_attributes.class} passed."
|
31
|
+
end
|
32
|
+
return if new_attributes.empty?
|
33
|
+
|
34
|
+
_assign_attributes(sanitize_for_mass_assignment(new_attributes))
|
35
|
+
end
|
36
|
+
|
37
|
+
alias attributes= assign_attributes
|
38
|
+
|
39
|
+
# Like `BasicObject#method_missing`, `#attribute_writer_missing` is invoked
|
40
|
+
# when `#assign_attributes` is passed an unknown attribute name.
|
41
|
+
#
|
42
|
+
# By default, `#attribute_writer_missing` raises an UnknownAttributeError.
|
43
|
+
#
|
44
|
+
# class Rectangle
|
45
|
+
# include ActiveModel::AttributeAssignment
|
46
|
+
#
|
47
|
+
# attr_accessor :length, :width
|
48
|
+
#
|
49
|
+
# def attribute_writer_missing(name, value)
|
50
|
+
# Rails.logger.warn "Tried to assign to unknown attribute #{name}"
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# rectangle = Rectangle.new
|
55
|
+
# rectangle.assign_attributes(height: 10) # => Logs "Tried to assign to unknown attribute 'height'"
|
56
|
+
def attribute_writer_missing(name, value)
|
57
|
+
raise UnknownAttributeError.new(self, name)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def _assign_attributes(attributes)
|
62
|
+
attributes.each do |k, v|
|
63
|
+
_assign_attribute(k, v)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def _assign_attribute(k, v)
|
68
|
+
setter = :"#{k}="
|
69
|
+
public_send(setter, v)
|
70
|
+
rescue NoMethodError
|
71
|
+
if respond_to?(setter)
|
72
|
+
raise
|
73
|
+
else
|
74
|
+
attribute_writer_missing(k.to_s, v)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|