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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +67 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +266 -0
  5. data/lib/active_model/access.rb +16 -0
  6. data/lib/active_model/api.rb +99 -0
  7. data/lib/active_model/attribute/user_provided_default.rb +55 -0
  8. data/lib/active_model/attribute.rb +277 -0
  9. data/lib/active_model/attribute_assignment.rb +78 -0
  10. data/lib/active_model/attribute_methods.rb +592 -0
  11. data/lib/active_model/attribute_mutation_tracker.rb +189 -0
  12. data/lib/active_model/attribute_registration.rb +117 -0
  13. data/lib/active_model/attribute_set/builder.rb +182 -0
  14. data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
  15. data/lib/active_model/attribute_set.rb +118 -0
  16. data/lib/active_model/attributes.rb +165 -0
  17. data/lib/active_model/callbacks.rb +155 -0
  18. data/lib/active_model/conversion.rb +121 -0
  19. data/lib/active_model/deprecator.rb +7 -0
  20. data/lib/active_model/dirty.rb +416 -0
  21. data/lib/active_model/error.rb +208 -0
  22. data/lib/active_model/errors.rb +547 -0
  23. data/lib/active_model/forbidden_attributes_protection.rb +33 -0
  24. data/lib/active_model/gem_version.rb +17 -0
  25. data/lib/active_model/lint.rb +118 -0
  26. data/lib/active_model/locale/en.yml +38 -0
  27. data/lib/active_model/model.rb +78 -0
  28. data/lib/active_model/naming.rb +359 -0
  29. data/lib/active_model/nested_error.rb +22 -0
  30. data/lib/active_model/railtie.rb +24 -0
  31. data/lib/active_model/secure_password.rb +231 -0
  32. data/lib/active_model/serialization.rb +198 -0
  33. data/lib/active_model/serializers/json.rb +154 -0
  34. data/lib/active_model/translation.rb +78 -0
  35. data/lib/active_model/type/big_integer.rb +36 -0
  36. data/lib/active_model/type/binary.rb +62 -0
  37. data/lib/active_model/type/boolean.rb +48 -0
  38. data/lib/active_model/type/date.rb +78 -0
  39. data/lib/active_model/type/date_time.rb +88 -0
  40. data/lib/active_model/type/decimal.rb +107 -0
  41. data/lib/active_model/type/float.rb +64 -0
  42. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +53 -0
  43. data/lib/active_model/type/helpers/mutable.rb +24 -0
  44. data/lib/active_model/type/helpers/numeric.rb +61 -0
  45. data/lib/active_model/type/helpers/time_value.rb +127 -0
  46. data/lib/active_model/type/helpers/timezone.rb +23 -0
  47. data/lib/active_model/type/helpers.rb +7 -0
  48. data/lib/active_model/type/immutable_string.rb +71 -0
  49. data/lib/active_model/type/integer.rb +113 -0
  50. data/lib/active_model/type/registry.rb +37 -0
  51. data/lib/active_model/type/serialize_cast_value.rb +47 -0
  52. data/lib/active_model/type/string.rb +43 -0
  53. data/lib/active_model/type/time.rb +87 -0
  54. data/lib/active_model/type/value.rb +157 -0
  55. data/lib/active_model/type.rb +55 -0
  56. data/lib/active_model/validations/absence.rb +33 -0
  57. data/lib/active_model/validations/acceptance.rb +113 -0
  58. data/lib/active_model/validations/callbacks.rb +119 -0
  59. data/lib/active_model/validations/clusivity.rb +54 -0
  60. data/lib/active_model/validations/comparability.rb +18 -0
  61. data/lib/active_model/validations/comparison.rb +90 -0
  62. data/lib/active_model/validations/confirmation.rb +80 -0
  63. data/lib/active_model/validations/exclusion.rb +49 -0
  64. data/lib/active_model/validations/format.rb +112 -0
  65. data/lib/active_model/validations/helper_methods.rb +15 -0
  66. data/lib/active_model/validations/inclusion.rb +47 -0
  67. data/lib/active_model/validations/length.rb +130 -0
  68. data/lib/active_model/validations/numericality.rb +222 -0
  69. data/lib/active_model/validations/presence.rb +39 -0
  70. data/lib/active_model/validations/resolve_value.rb +26 -0
  71. data/lib/active_model/validations/validates.rb +175 -0
  72. data/lib/active_model/validations/with.rb +154 -0
  73. data/lib/active_model/validations.rb +489 -0
  74. data/lib/active_model/validator.rb +190 -0
  75. data/lib/active_model/version.rb +10 -0
  76. data/lib/active_model.rb +84 -0
  77. 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