omg-activemodel 8.0.0.alpha1
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/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
|