activemodel 7.2.2.2 → 8.0.3
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 +4 -4
- data/CHANGELOG.md +92 -34
- data/README.rdoc +1 -1
- data/lib/active_model/attribute.rb +1 -1
- data/lib/active_model/attribute_assignment.rb +23 -2
- data/lib/active_model/attribute_methods.rb +3 -3
- data/lib/active_model/attribute_set.rb +1 -1
- data/lib/active_model/conversion.rb +1 -1
- data/lib/active_model/dirty.rb +17 -10
- data/lib/active_model/gem_version.rb +4 -4
- data/lib/active_model/model.rb +2 -2
- data/lib/active_model/secure_password.rb +45 -4
- data/lib/active_model/serialization.rb +21 -21
- data/lib/active_model/translation.rb +22 -5
- data/lib/active_model/type/float.rb +8 -8
- data/lib/active_model/type/helpers/time_value.rb +20 -44
- data/lib/active_model/type/value.rb +3 -3
- data/lib/active_model/validations/acceptance.rb +1 -1
- data/lib/active_model/validations/validates.rb +7 -2
- data/lib/active_model/validations.rb +71 -32
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16b040fd5a62c0392cc172673913fcb87cdb1aa6314bc12a3b54c4b50a20ed94
|
|
4
|
+
data.tar.gz: d7e329b88a5c375b5e65c03e30b5469996b966ec3a6e8f15492c3c2f099205a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '04859dbfdb1a4e16f0e4d391ca49a149f588926e19b4175cd0338f047a98b88df2edca6eef049098e268a12f494877294441fb748d52ed576e9ab65f6ef64dc3'
|
|
7
|
+
data.tar.gz: 17a3fd0fd7d386aa3997bfdf247b0a386ec00b879fc0922422b82c25675cf021092f44e8c32bc142fc57db01c97524123d3cca6e7ddd8718a6cc747d7e27c6f9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,71 +1,129 @@
|
|
|
1
|
-
## Rails
|
|
1
|
+
## Rails 8.0.3 (September 22, 2025) ##
|
|
2
|
+
|
|
3
|
+
* Fix `has_secure_password` to perform confirmation validation of the password even when blank.
|
|
4
|
+
|
|
5
|
+
The validation was incorrectly skipped when the password only contained whitespace characters.
|
|
6
|
+
|
|
7
|
+
*Fabio Sangiovanni*
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Rails 8.0.2.1 (August 13, 2025) ##
|
|
2
11
|
|
|
3
12
|
* No changes.
|
|
4
13
|
|
|
5
14
|
|
|
6
|
-
## Rails
|
|
15
|
+
## Rails 8.0.2 (March 12, 2025) ##
|
|
7
16
|
|
|
8
17
|
* No changes.
|
|
9
18
|
|
|
10
19
|
|
|
11
|
-
## Rails
|
|
20
|
+
## Rails 8.0.1 (December 13, 2024) ##
|
|
12
21
|
|
|
13
|
-
*
|
|
22
|
+
* No changes.
|
|
14
23
|
|
|
15
|
-
`alias_attribute` would wrongly assume the attribute accessor was generated by Active Model.
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
## Rails 8.0.0.1 (December 10, 2024) ##
|
|
26
|
+
|
|
27
|
+
* No changes.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Rails 8.0.0 (November 07, 2024) ##
|
|
31
|
+
|
|
32
|
+
* No changes.
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Rails 8.0.0.rc2 (October 30, 2024) ##
|
|
36
|
+
|
|
37
|
+
* No changes.
|
|
20
38
|
|
|
21
|
-
define_attribute_methods :name
|
|
22
|
-
attr_accessor :name
|
|
23
39
|
|
|
24
|
-
|
|
40
|
+
## Rails 8.0.0.rc1 (October 19, 2024) ##
|
|
41
|
+
|
|
42
|
+
* Add `:except_on` option for validations. Grants the ability to _skip_ validations in specified contexts.
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
class User < ApplicationRecord
|
|
46
|
+
#...
|
|
47
|
+
validates :birthday, presence: { except_on: :admin }
|
|
48
|
+
#...
|
|
25
49
|
end
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
user = User.new(attributes except birthday)
|
|
52
|
+
user.save(context: :admin)
|
|
28
53
|
```
|
|
29
54
|
|
|
30
|
-
*
|
|
55
|
+
*Drew Bragg*
|
|
31
56
|
|
|
57
|
+
## Rails 8.0.0.beta1 (September 26, 2024) ##
|
|
32
58
|
|
|
33
|
-
|
|
59
|
+
* Make `ActiveModel::Serialization#read_attribute_for_serialization` public
|
|
34
60
|
|
|
35
|
-
*
|
|
61
|
+
*Sean Doyle*
|
|
36
62
|
|
|
63
|
+
* Add a default token generator for password reset tokens when using `has_secure_password`.
|
|
37
64
|
|
|
38
|
-
|
|
65
|
+
```ruby
|
|
66
|
+
class User < ApplicationRecord
|
|
67
|
+
has_secure_password
|
|
68
|
+
end
|
|
39
69
|
|
|
40
|
-
|
|
70
|
+
user = User.create!(name: "david", password: "123", password_confirmation: "123")
|
|
71
|
+
token = user.password_reset_token
|
|
72
|
+
User.find_by_password_reset_token(token) # returns user
|
|
41
73
|
|
|
74
|
+
# 16 minutes later...
|
|
75
|
+
User.find_by_password_reset_token(token) # returns nil
|
|
42
76
|
|
|
43
|
-
|
|
77
|
+
# raises ActiveSupport::MessageVerifier::InvalidSignature since the token is expired
|
|
78
|
+
User.find_by_password_reset_token!(token)
|
|
79
|
+
```
|
|
44
80
|
|
|
45
|
-
*
|
|
81
|
+
*DHH*
|
|
46
82
|
|
|
83
|
+
* Add a load hook `active_model_translation` for `ActiveModel::Translation`.
|
|
47
84
|
|
|
48
|
-
|
|
85
|
+
*Shouichi Kamiya*
|
|
49
86
|
|
|
50
|
-
*
|
|
51
|
-
|
|
87
|
+
* Add `raise_on_missing_translations` option to `ActiveModel::Translation`.
|
|
88
|
+
When the option is set, `human_attribute_name` raises an error if a translation of the given attribute is missing.
|
|
52
89
|
|
|
53
|
-
|
|
90
|
+
```ruby
|
|
91
|
+
# ActiveModel::Translation.raise_on_missing_translations = false
|
|
92
|
+
Post.human_attribute_name("title")
|
|
93
|
+
=> "Title"
|
|
94
|
+
|
|
95
|
+
# ActiveModel::Translation.raise_on_missing_translations = true
|
|
96
|
+
Post.human_attribute_name("title")
|
|
97
|
+
=> Translation missing. Options considered were: (I18n::MissingTranslationData)
|
|
98
|
+
- en.activerecord.attributes.post.title
|
|
99
|
+
- en.attributes.title
|
|
100
|
+
|
|
101
|
+
raise exception.respond_to?(:to_exception) ? exception.to_exception : exception
|
|
102
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
*Shouichi Kamiya*
|
|
106
|
+
|
|
107
|
+
* Introduce `ActiveModel::AttributeAssignment#attribute_writer_missing`
|
|
54
108
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
the same for Active Model as it does for Active Record.
|
|
109
|
+
Provide instances with an opportunity to gracefully handle assigning to an
|
|
110
|
+
unknown attribute:
|
|
58
111
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
112
|
+
```ruby
|
|
113
|
+
class Rectangle
|
|
114
|
+
include ActiveModel::AttributeAssignment
|
|
115
|
+
|
|
116
|
+
attr_accessor :length, :width
|
|
62
117
|
|
|
63
|
-
|
|
118
|
+
def attribute_writer_missing(name, value)
|
|
119
|
+
Rails.logger.warn "Tried to assign to unknown attribute #{name}"
|
|
64
120
|
end
|
|
121
|
+
end
|
|
65
122
|
|
|
66
|
-
|
|
67
|
-
|
|
123
|
+
rectangle = Rectangle.new
|
|
124
|
+
rectangle.assign_attributes(height: 10) # => Logs "Tried to assign to unknown attribute 'height'"
|
|
125
|
+
```
|
|
68
126
|
|
|
69
|
-
*
|
|
127
|
+
*Sean Doyle*
|
|
70
128
|
|
|
71
|
-
Please check [7-
|
|
129
|
+
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/activemodel/CHANGELOG.md) for previous changes.
|
data/README.rdoc
CHANGED
|
@@ -261,6 +261,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
|
261
261
|
|
|
262
262
|
* https://github.com/rails/rails/issues
|
|
263
263
|
|
|
264
|
-
Feature requests should be discussed on the
|
|
264
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
|
265
265
|
|
|
266
266
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -36,9 +36,30 @@ module ActiveModel
|
|
|
36
36
|
|
|
37
37
|
alias attributes= assign_attributes
|
|
38
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
|
+
|
|
39
60
|
private
|
|
40
61
|
def _assign_attributes(attributes)
|
|
41
|
-
attributes.
|
|
62
|
+
attributes.each_pair do |k, v|
|
|
42
63
|
_assign_attribute(k, v)
|
|
43
64
|
end
|
|
44
65
|
end
|
|
@@ -50,7 +71,7 @@ module ActiveModel
|
|
|
50
71
|
if respond_to?(setter)
|
|
51
72
|
raise
|
|
52
73
|
else
|
|
53
|
-
|
|
74
|
+
attribute_writer_missing(k.to_s, v)
|
|
54
75
|
end
|
|
55
76
|
end
|
|
56
77
|
end
|
|
@@ -214,7 +214,7 @@ module ActiveModel
|
|
|
214
214
|
end
|
|
215
215
|
end
|
|
216
216
|
|
|
217
|
-
def generate_alias_attribute_methods(code_generator, new_name, old_name)
|
|
217
|
+
def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
|
|
218
218
|
ActiveSupport::CodeGenerator.batch(code_generator, __FILE__, __LINE__) do |owner|
|
|
219
219
|
attribute_method_patterns.each do |pattern|
|
|
220
220
|
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
|
@@ -321,8 +321,8 @@ module ActiveModel
|
|
|
321
321
|
canonical_method_name = pattern.method_name(attr_name)
|
|
322
322
|
public_method_name = pattern.method_name(as)
|
|
323
323
|
|
|
324
|
-
# If defining a regular attribute method, we don't override methods that are
|
|
325
|
-
# defined in
|
|
324
|
+
# If defining a regular attribute method, we don't override methods that are explicitly
|
|
325
|
+
# defined in parent classes.
|
|
326
326
|
if instance_method_already_implemented?(public_method_name)
|
|
327
327
|
# However, for `alias_attribute`, we always define the method.
|
|
328
328
|
# We check for override second because `instance_method_already_implemented?`
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module ActiveModel
|
|
4
4
|
# = Active \Model \Conversion
|
|
5
5
|
#
|
|
6
|
-
# Handles default conversions: to_model, to_key, to_param, and to_partial_path.
|
|
6
|
+
# Handles default conversions: #to_model, #to_key, #to_param, and #to_partial_path.
|
|
7
7
|
#
|
|
8
8
|
# Let's take for example this non-persisted object.
|
|
9
9
|
#
|
data/lib/active_model/dirty.rb
CHANGED
|
@@ -108,7 +108,7 @@ module ActiveModel
|
|
|
108
108
|
# person.changes # => {"name" => ["Bill", "Bob"]}
|
|
109
109
|
#
|
|
110
110
|
# If an attribute is modified in-place then make use of
|
|
111
|
-
# {*_will_change!}[rdoc-
|
|
111
|
+
# {*_will_change!}[rdoc-ref:#*_will_change!] to mark that the attribute is changing.
|
|
112
112
|
# Otherwise \Active \Model can't track changes to in-place attributes. Note
|
|
113
113
|
# that Active Record can detect in-place modifications automatically. You do
|
|
114
114
|
# not need to call <tt>*_will_change!</tt> on Active Record models.
|
|
@@ -247,16 +247,23 @@ module ActiveModel
|
|
|
247
247
|
|
|
248
248
|
def initialize_dup(other) # :nodoc:
|
|
249
249
|
super
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
@mutations_from_database = nil
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def init_attributes(other) # :nodoc:
|
|
254
|
+
attrs = super
|
|
255
|
+
if other.persisted? && self.class.respond_to?(:_default_attributes)
|
|
256
|
+
self.class._default_attributes.map do |attr|
|
|
257
|
+
attr.with_value_from_user(attrs.fetch_value(attr.name))
|
|
253
258
|
end
|
|
259
|
+
else
|
|
260
|
+
attrs
|
|
254
261
|
end
|
|
255
|
-
@mutations_from_database = nil
|
|
256
262
|
end
|
|
257
263
|
|
|
258
264
|
def as_json(options = {}) # :nodoc:
|
|
259
|
-
|
|
265
|
+
except = [*options[:except], "mutations_from_database", "mutations_before_last_save"]
|
|
266
|
+
options = options.merge except: except
|
|
260
267
|
super(options)
|
|
261
268
|
end
|
|
262
269
|
|
|
@@ -289,22 +296,22 @@ module ActiveModel
|
|
|
289
296
|
mutations_from_database.changed_attribute_names
|
|
290
297
|
end
|
|
291
298
|
|
|
292
|
-
# Dispatch target for {*_changed?}[rdoc-
|
|
299
|
+
# Dispatch target for {*_changed?}[rdoc-ref:#*_changed?] attribute methods.
|
|
293
300
|
def attribute_changed?(attr_name, **options)
|
|
294
301
|
mutations_from_database.changed?(attr_name.to_s, **options)
|
|
295
302
|
end
|
|
296
303
|
|
|
297
|
-
# Dispatch target for {*_was}[rdoc-
|
|
304
|
+
# Dispatch target for {*_was}[rdoc-ref:#*_was] attribute methods.
|
|
298
305
|
def attribute_was(attr_name)
|
|
299
306
|
mutations_from_database.original_value(attr_name.to_s)
|
|
300
307
|
end
|
|
301
308
|
|
|
302
|
-
# Dispatch target for {*_previously_changed?}[rdoc-
|
|
309
|
+
# Dispatch target for {*_previously_changed?}[rdoc-ref:#*_previously_changed?] attribute methods.
|
|
303
310
|
def attribute_previously_changed?(attr_name, **options)
|
|
304
311
|
mutations_before_last_save.changed?(attr_name.to_s, **options)
|
|
305
312
|
end
|
|
306
313
|
|
|
307
|
-
# Dispatch target for {*_previously_was}[rdoc-
|
|
314
|
+
# Dispatch target for {*_previously_was}[rdoc-ref:#*_previously_was] attribute methods.
|
|
308
315
|
def attribute_previously_was(attr_name)
|
|
309
316
|
mutations_before_last_save.original_value(attr_name.to_s)
|
|
310
317
|
end
|
data/lib/active_model/model.rb
CHANGED
|
@@ -54,7 +54,7 @@ module ActiveModel
|
|
|
54
54
|
#
|
|
55
55
|
# person = Person.new(id: 1, name: "bob")
|
|
56
56
|
# person.slice(:id, :name)
|
|
57
|
-
# => { "id" => 1, "name" => "bob" }
|
|
57
|
+
# # => { "id" => 1, "name" => "bob" }
|
|
58
58
|
#
|
|
59
59
|
#--
|
|
60
60
|
# Implemented by ActiveModel::Access#slice.
|
|
@@ -68,7 +68,7 @@ module ActiveModel
|
|
|
68
68
|
#
|
|
69
69
|
# person = Person.new(id: 1, name: "bob")
|
|
70
70
|
# person.values_at(:id, :name)
|
|
71
|
-
# => [1, "bob"]
|
|
71
|
+
# # => [1, "bob"]
|
|
72
72
|
#
|
|
73
73
|
#--
|
|
74
74
|
# Implemented by ActiveModel::Access#values_at.
|
|
@@ -39,6 +39,10 @@ module ActiveModel
|
|
|
39
39
|
# <tt>validations: false</tt> as an argument. This allows complete
|
|
40
40
|
# customizability of validation behavior.
|
|
41
41
|
#
|
|
42
|
+
# Finally, a password reset token that's valid for 15 minutes after issue
|
|
43
|
+
# is automatically configured when +reset_token+ is set to true (which it is by default)
|
|
44
|
+
# and the object responds to +generates_token_for+ (which Active Records do).
|
|
45
|
+
#
|
|
42
46
|
# To use +has_secure_password+, add bcrypt (~> 3.1.7) to your Gemfile:
|
|
43
47
|
#
|
|
44
48
|
# gem "bcrypt", "~> 3.1.7"
|
|
@@ -98,7 +102,18 @@ module ActiveModel
|
|
|
98
102
|
# account.is_guest = true
|
|
99
103
|
# account.valid? # => true
|
|
100
104
|
#
|
|
101
|
-
|
|
105
|
+
# ===== Using the password reset token
|
|
106
|
+
#
|
|
107
|
+
# user = User.create!(name: "david", password: "123", password_confirmation: "123")
|
|
108
|
+
# token = user.password_reset_token
|
|
109
|
+
# User.find_by_password_reset_token(token) # returns user
|
|
110
|
+
#
|
|
111
|
+
# # 16 minutes later...
|
|
112
|
+
# User.find_by_password_reset_token(token) # returns nil
|
|
113
|
+
#
|
|
114
|
+
# # raises ActiveSupport::MessageVerifier::InvalidSignature since the token is expired
|
|
115
|
+
# User.find_by_password_reset_token!(token)
|
|
116
|
+
def has_secure_password(attribute = :password, validations: true, reset_token: true)
|
|
102
117
|
# Load bcrypt gem only when has_secure_password is used.
|
|
103
118
|
# This is to avoid ActiveModel (and by extension the entire framework)
|
|
104
119
|
# being dependent on a binary library.
|
|
@@ -109,7 +124,7 @@ module ActiveModel
|
|
|
109
124
|
raise
|
|
110
125
|
end
|
|
111
126
|
|
|
112
|
-
include InstanceMethodsOnActivation.new(attribute)
|
|
127
|
+
include InstanceMethodsOnActivation.new(attribute, reset_token: reset_token)
|
|
113
128
|
|
|
114
129
|
if validations
|
|
115
130
|
include ActiveModel::Validations
|
|
@@ -140,13 +155,32 @@ module ActiveModel
|
|
|
140
155
|
end
|
|
141
156
|
end
|
|
142
157
|
|
|
143
|
-
validates_confirmation_of attribute,
|
|
158
|
+
validates_confirmation_of attribute, allow_nil: true
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Only generate tokens for records that are capable of doing so (Active Records, not vanilla Active Models)
|
|
162
|
+
if reset_token && respond_to?(:generates_token_for)
|
|
163
|
+
generates_token_for :"#{attribute}_reset", expires_in: 15.minutes do
|
|
164
|
+
public_send(:"#{attribute}_salt")&.last(10)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
168
|
+
silence_redefinition_of_method :find_by_#{attribute}_reset_token
|
|
169
|
+
def self.find_by_#{attribute}_reset_token(token)
|
|
170
|
+
find_by_token_for(:#{attribute}_reset, token)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
silence_redefinition_of_method :find_by_#{attribute}_reset_token!
|
|
174
|
+
def self.find_by_#{attribute}_reset_token!(token)
|
|
175
|
+
find_by_token_for!(:#{attribute}_reset, token)
|
|
176
|
+
end
|
|
177
|
+
RUBY
|
|
144
178
|
end
|
|
145
179
|
end
|
|
146
180
|
end
|
|
147
181
|
|
|
148
182
|
class InstanceMethodsOnActivation < Module
|
|
149
|
-
def initialize(attribute)
|
|
183
|
+
def initialize(attribute, reset_token:)
|
|
150
184
|
attr_reader attribute
|
|
151
185
|
|
|
152
186
|
define_method("#{attribute}=") do |unencrypted_password|
|
|
@@ -184,6 +218,13 @@ module ActiveModel
|
|
|
184
218
|
end
|
|
185
219
|
|
|
186
220
|
alias_method :authenticate, :authenticate_password if attribute == :password
|
|
221
|
+
|
|
222
|
+
if reset_token
|
|
223
|
+
# Returns the class-level configured reset token for the password.
|
|
224
|
+
define_method("#{attribute}_reset_token") do
|
|
225
|
+
generate_token_for(:"#{attribute}_reset")
|
|
226
|
+
end
|
|
227
|
+
end
|
|
187
228
|
end
|
|
188
229
|
end
|
|
189
230
|
end
|
|
@@ -29,8 +29,8 @@ module ActiveModel
|
|
|
29
29
|
# An +attributes+ hash must be defined and should contain any attributes you
|
|
30
30
|
# need to be serialized. Attributes must be strings, not symbols.
|
|
31
31
|
# When called, serializable hash will use instance methods that match the name
|
|
32
|
-
# of the attributes hash's keys. In order to override this behavior,
|
|
33
|
-
#
|
|
32
|
+
# of the attributes hash's keys. In order to override this behavior, override
|
|
33
|
+
# the +read_attribute_for_serialization+ method.
|
|
34
34
|
#
|
|
35
35
|
# ActiveModel::Serializers::JSON module automatically includes
|
|
36
36
|
# the +ActiveModel::Serialization+ module, so there is no need to
|
|
@@ -128,7 +128,7 @@ module ActiveModel
|
|
|
128
128
|
return serializable_attributes(attribute_names) if options.blank?
|
|
129
129
|
|
|
130
130
|
if only = options[:only]
|
|
131
|
-
attribute_names
|
|
131
|
+
attribute_names = Array(only).map(&:to_s) & attribute_names
|
|
132
132
|
elsif except = options[:except]
|
|
133
133
|
attribute_names -= Array(except).map(&:to_s)
|
|
134
134
|
end
|
|
@@ -148,29 +148,29 @@ module ActiveModel
|
|
|
148
148
|
hash
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
# Hook method defining how an attribute value should be retrieved for
|
|
152
|
+
# serialization. By default this is assumed to be an instance named after
|
|
153
|
+
# the attribute. Override this method in subclasses should you need to
|
|
154
|
+
# retrieve the value for a given attribute differently:
|
|
155
|
+
#
|
|
156
|
+
# class MyClass
|
|
157
|
+
# include ActiveModel::Serialization
|
|
158
|
+
#
|
|
159
|
+
# def initialize(data = {})
|
|
160
|
+
# @data = data
|
|
161
|
+
# end
|
|
162
|
+
#
|
|
163
|
+
# def read_attribute_for_serialization(key)
|
|
164
|
+
# @data[key]
|
|
165
|
+
# end
|
|
166
|
+
# end
|
|
167
|
+
alias :read_attribute_for_serialization :send
|
|
168
|
+
|
|
151
169
|
private
|
|
152
170
|
def attribute_names_for_serialization
|
|
153
171
|
attributes.keys
|
|
154
172
|
end
|
|
155
173
|
|
|
156
|
-
# Hook method defining how an attribute value should be retrieved for
|
|
157
|
-
# serialization. By default this is assumed to be an instance named after
|
|
158
|
-
# the attribute. Override this method in subclasses should you need to
|
|
159
|
-
# retrieve the value for a given attribute differently:
|
|
160
|
-
#
|
|
161
|
-
# class MyClass
|
|
162
|
-
# include ActiveModel::Serialization
|
|
163
|
-
#
|
|
164
|
-
# def initialize(data = {})
|
|
165
|
-
# @data = data
|
|
166
|
-
# end
|
|
167
|
-
#
|
|
168
|
-
# def read_attribute_for_serialization(key)
|
|
169
|
-
# @data[key]
|
|
170
|
-
# end
|
|
171
|
-
# end
|
|
172
|
-
alias :read_attribute_for_serialization :send
|
|
173
|
-
|
|
174
174
|
def serializable_attributes(attribute_names)
|
|
175
175
|
attribute_names.index_with { |n| read_attribute_for_serialization(n) }
|
|
176
176
|
end
|
|
@@ -22,6 +22,8 @@ module ActiveModel
|
|
|
22
22
|
module Translation
|
|
23
23
|
include ActiveModel::Naming
|
|
24
24
|
|
|
25
|
+
singleton_class.attr_accessor :raise_on_missing_translations
|
|
26
|
+
|
|
25
27
|
# Returns the +i18n_scope+ for the class. Override if you want custom lookup.
|
|
26
28
|
def i18n_scope
|
|
27
29
|
:activemodel
|
|
@@ -50,23 +52,38 @@ module ActiveModel
|
|
|
50
52
|
namespace, _, attribute = attribute.rpartition(".")
|
|
51
53
|
namespace.tr!(".", "/")
|
|
52
54
|
|
|
55
|
+
if attribute.present?
|
|
56
|
+
key = "#{namespace}.#{attribute}"
|
|
57
|
+
separator = "/"
|
|
58
|
+
else
|
|
59
|
+
key = namespace
|
|
60
|
+
separator = "."
|
|
61
|
+
end
|
|
62
|
+
|
|
53
63
|
defaults = lookup_ancestors.map do |klass|
|
|
54
|
-
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}
|
|
64
|
+
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}#{separator}#{key}"
|
|
55
65
|
end
|
|
56
|
-
defaults << :"#{i18n_scope}.attributes.#{
|
|
66
|
+
defaults << :"#{i18n_scope}.attributes.#{key}"
|
|
67
|
+
defaults << :"attributes.#{key}"
|
|
57
68
|
else
|
|
58
69
|
defaults = lookup_ancestors.map do |klass|
|
|
59
70
|
:"#{i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
|
|
60
71
|
end
|
|
61
72
|
end
|
|
62
73
|
|
|
74
|
+
raise_on_missing = options.fetch(:raise, Translation.raise_on_missing_translations)
|
|
75
|
+
|
|
63
76
|
defaults << :"attributes.#{attribute}"
|
|
64
77
|
defaults << options[:default] if options[:default]
|
|
65
|
-
defaults << MISSING_TRANSLATION
|
|
78
|
+
defaults << MISSING_TRANSLATION unless raise_on_missing
|
|
66
79
|
|
|
67
|
-
translation = I18n.translate(defaults.shift, count: 1, **options, default: defaults)
|
|
68
|
-
|
|
80
|
+
translation = I18n.translate(defaults.shift, count: 1, raise: raise_on_missing, **options, default: defaults)
|
|
81
|
+
if translation == MISSING_TRANSLATION
|
|
82
|
+
translation = attribute.present? ? attribute.humanize : namespace.humanize
|
|
83
|
+
end
|
|
69
84
|
translation
|
|
70
85
|
end
|
|
71
86
|
end
|
|
87
|
+
|
|
88
|
+
ActiveSupport.run_load_hooks(:active_model_translation, Translation)
|
|
72
89
|
end
|
|
@@ -15,14 +15,6 @@ module ActiveModel
|
|
|
15
15
|
# attribute :weight, :float
|
|
16
16
|
# end
|
|
17
17
|
#
|
|
18
|
-
# Values are cast using their +to_f+ method, except for the following
|
|
19
|
-
# strings:
|
|
20
|
-
#
|
|
21
|
-
# - Blank strings are cast to +nil+.
|
|
22
|
-
# - <tt>"Infinity"</tt> is cast to +Float::INFINITY+.
|
|
23
|
-
# - <tt>"-Infinity"</tt> is cast to <tt>-Float::INFINITY</tt>.
|
|
24
|
-
# - <tt>"NaN"</tt> is cast to +Float::NAN+.
|
|
25
|
-
#
|
|
26
18
|
# bag = BagOfCoffee.new
|
|
27
19
|
#
|
|
28
20
|
# bag.weight = "0.25"
|
|
@@ -33,6 +25,14 @@ module ActiveModel
|
|
|
33
25
|
#
|
|
34
26
|
# bag.weight = "NaN"
|
|
35
27
|
# bag.weight # => Float::NAN
|
|
28
|
+
#
|
|
29
|
+
# Values are cast using their +to_f+ method, except for the following
|
|
30
|
+
# strings:
|
|
31
|
+
#
|
|
32
|
+
# - Blank strings are cast to +nil+.
|
|
33
|
+
# - <tt>"Infinity"</tt> is cast to +Float::INFINITY+.
|
|
34
|
+
# - <tt>"-Infinity"</tt> is cast to <tt>-Float::INFINITY</tt>.
|
|
35
|
+
# - <tt>"NaN"</tt> is cast to +Float::NAN+.
|
|
36
36
|
class Float < Value
|
|
37
37
|
include Helpers::Numeric
|
|
38
38
|
|
|
@@ -69,56 +69,32 @@ module ActiveModel
|
|
|
69
69
|
\z
|
|
70
70
|
/x
|
|
71
71
|
|
|
72
|
-
if
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
::Time.new(string)
|
|
84
|
-
end
|
|
85
|
-
rescue ArgumentError
|
|
86
|
-
nil
|
|
87
|
-
end
|
|
88
|
-
else
|
|
89
|
-
def fast_string_to_time(string)
|
|
90
|
-
return unless string.include?("-") # Time.new("1234") # => 1234-01-01 00:00:00
|
|
91
|
-
|
|
92
|
-
if is_utc?
|
|
93
|
-
::Time.new(string, in: "UTC")
|
|
94
|
-
else
|
|
95
|
-
::Time.new(string)
|
|
96
|
-
end
|
|
97
|
-
rescue ArgumentError
|
|
98
|
-
nil
|
|
72
|
+
if Time.new(2000, 1, 1, 0, 0, 0, "-00:00").yday != 1 # Early 3.2.x had a bug
|
|
73
|
+
# BUG: Wrapping the Time object with Time.at because Time.new with `in:` in Ruby 3.2.0
|
|
74
|
+
# used to return an invalid Time object
|
|
75
|
+
# see: https://bugs.ruby-lang.org/issues/19292
|
|
76
|
+
def fast_string_to_time(string)
|
|
77
|
+
return unless string.include?("-") # Time.new("1234") # => 1234-01-01 00:00:00
|
|
78
|
+
|
|
79
|
+
if is_utc?
|
|
80
|
+
::Time.at(::Time.new(string, in: "UTC"))
|
|
81
|
+
else
|
|
82
|
+
::Time.new(string)
|
|
99
83
|
end
|
|
84
|
+
rescue ArgumentError
|
|
85
|
+
nil
|
|
100
86
|
end
|
|
101
87
|
else
|
|
102
88
|
def fast_string_to_time(string)
|
|
103
|
-
return unless
|
|
89
|
+
return unless string.include?("-") # Time.new("1234") # => 1234-01-01 00:00:00
|
|
104
90
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
91
|
+
if is_utc?
|
|
92
|
+
::Time.new(string, in: "UTC")
|
|
93
|
+
else
|
|
94
|
+
::Time.new(string)
|
|
109
95
|
end
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
offset = \
|
|
113
|
-
if $8 == "Z"
|
|
114
|
-
0
|
|
115
|
-
else
|
|
116
|
-
offset_h, offset_m = $8.to_i, $9.to_i
|
|
117
|
-
offset_h.to_i * 3600 + (offset_h.negative? ? -1 : 1) * offset_m * 60
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
new_time($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, usec, offset)
|
|
96
|
+
rescue ArgumentError
|
|
97
|
+
nil
|
|
122
98
|
end
|
|
123
99
|
end
|
|
124
100
|
end
|
|
@@ -25,7 +25,7 @@ module ActiveModel
|
|
|
25
25
|
# by the database. For example a boolean type can return +true+ if the
|
|
26
26
|
# value parameter is a Ruby boolean, but may return +false+ if the value
|
|
27
27
|
# parameter is some other object.
|
|
28
|
-
def serializable?(value)
|
|
28
|
+
def serializable?(value, &_)
|
|
29
29
|
true
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -114,8 +114,8 @@ module ActiveModel
|
|
|
114
114
|
false
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
def map(value) # :nodoc:
|
|
118
|
-
|
|
117
|
+
def map(value, &) # :nodoc:
|
|
118
|
+
value
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def ==(other)
|
|
@@ -78,7 +78,12 @@ module ActiveModel
|
|
|
78
78
|
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
|
79
79
|
# <tt>on: :custom_validation_context</tt> or
|
|
80
80
|
# <tt>on: [:create, :custom_validation_context]</tt>)
|
|
81
|
-
# * <tt>:
|
|
81
|
+
# * <tt>:except_on</tt> - Specifies the contexts where this validation is not active.
|
|
82
|
+
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
|
83
|
+
# or an array of symbols. (e.g. <tt>except: :create</tt> or
|
|
84
|
+
# <tt>except_on: :custom_validation_context</tt> or
|
|
85
|
+
# <tt>except_on: [:create, :custom_validation_context]</tt>)
|
|
86
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
|
82
87
|
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
|
83
88
|
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
|
|
84
89
|
# proc or string should return or evaluate to a +true+ or +false+ value.
|
|
@@ -155,7 +160,7 @@ module ActiveModel
|
|
|
155
160
|
# When creating custom validators, it might be useful to be able to specify
|
|
156
161
|
# additional default keys. This can be done by overwriting this method.
|
|
157
162
|
def _validates_default_keys
|
|
158
|
-
[:if, :unless, :on, :allow_blank, :allow_nil, :strict]
|
|
163
|
+
[:if, :unless, :on, :allow_blank, :allow_nil, :strict, :except_on]
|
|
159
164
|
end
|
|
160
165
|
|
|
161
166
|
def _parse_validates_options(options)
|
|
@@ -45,27 +45,6 @@ module ActiveModel
|
|
|
45
45
|
extend HelperMethods
|
|
46
46
|
include HelperMethods
|
|
47
47
|
|
|
48
|
-
##
|
|
49
|
-
# :method: validation_context
|
|
50
|
-
# Returns the context when running validations.
|
|
51
|
-
#
|
|
52
|
-
# This is useful when running validations except a certain context (opposite to the +on+ option).
|
|
53
|
-
#
|
|
54
|
-
# class Person
|
|
55
|
-
# include ActiveModel::Validations
|
|
56
|
-
#
|
|
57
|
-
# attr_accessor :name
|
|
58
|
-
# validates :name, presence: true, if: -> { validation_context != :custom }
|
|
59
|
-
# end
|
|
60
|
-
#
|
|
61
|
-
# person = Person.new
|
|
62
|
-
# person.valid? #=> false
|
|
63
|
-
# person.valid?(:new) #=> false
|
|
64
|
-
# person.valid?(:custom) #=> true
|
|
65
|
-
|
|
66
|
-
##
|
|
67
|
-
attr_accessor :validation_context
|
|
68
|
-
private :validation_context=
|
|
69
48
|
define_callbacks :validate, scope: :name
|
|
70
49
|
|
|
71
50
|
class_attribute :_validators, instance_writer: false, default: Hash.new { |h, k| h[k] = [] }
|
|
@@ -84,12 +63,18 @@ module ActiveModel
|
|
|
84
63
|
# end
|
|
85
64
|
# end
|
|
86
65
|
#
|
|
87
|
-
# Options
|
|
66
|
+
# ==== Options
|
|
67
|
+
#
|
|
88
68
|
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
|
89
69
|
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
|
90
70
|
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
|
91
71
|
# <tt>on: :custom_validation_context</tt> or
|
|
92
72
|
# <tt>on: [:create, :custom_validation_context]</tt>)
|
|
73
|
+
# * <tt>:except_on</tt> - Specifies the contexts where this validation is not active.
|
|
74
|
+
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
|
75
|
+
# or an array of symbols. (e.g. <tt>except: :create</tt> or
|
|
76
|
+
# <tt>except_on: :custom_validation_context</tt> or
|
|
77
|
+
# <tt>except_on: [:create, :custom_validation_context]</tt>)
|
|
93
78
|
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
|
|
94
79
|
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
|
|
95
80
|
# * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
|
|
@@ -105,7 +90,7 @@ module ActiveModel
|
|
|
105
90
|
validates_with BlockValidator, _merge_attributes(attr_names), &block
|
|
106
91
|
end
|
|
107
92
|
|
|
108
|
-
VALID_OPTIONS_FOR_VALIDATE = [:on, :if, :unless, :prepend].freeze # :nodoc:
|
|
93
|
+
VALID_OPTIONS_FOR_VALIDATE = [:on, :if, :unless, :prepend, :except_on].freeze # :nodoc:
|
|
109
94
|
|
|
110
95
|
# Adds a validation method or block to the class. This is useful when
|
|
111
96
|
# overriding the +validate+ instance method becomes too unwieldy and
|
|
@@ -150,20 +135,26 @@ module ActiveModel
|
|
|
150
135
|
# Note that the return value of validation methods is not relevant.
|
|
151
136
|
# It's not possible to halt the validate callback chain.
|
|
152
137
|
#
|
|
153
|
-
# Options
|
|
138
|
+
# ==== Options
|
|
139
|
+
#
|
|
154
140
|
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
|
|
155
141
|
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
|
156
142
|
# or an array of symbols. (e.g. <tt>on: :create</tt> or
|
|
157
143
|
# <tt>on: :custom_validation_context</tt> or
|
|
158
144
|
# <tt>on: [:create, :custom_validation_context]</tt>)
|
|
159
|
-
# * <tt>:
|
|
145
|
+
# * <tt>:except_on</tt> - Specifies the contexts where this validation is not active.
|
|
146
|
+
# Runs in all validation contexts by default +nil+. You can pass a symbol
|
|
147
|
+
# or an array of symbols. (e.g. <tt>except: :create</tt> or
|
|
148
|
+
# <tt>except_on: :custom_validation_context</tt> or
|
|
149
|
+
# <tt>except_on: [:create, :custom_validation_context]</tt>)
|
|
150
|
+
# * <tt>:if</tt> - Specifies a method or proc to call to determine
|
|
160
151
|
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
|
161
|
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method
|
|
162
|
-
# proc
|
|
163
|
-
# * <tt>:unless</tt> - Specifies a method
|
|
152
|
+
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method or
|
|
153
|
+
# proc should return or evaluate to a +true+ or +false+ value.
|
|
154
|
+
# * <tt>:unless</tt> - Specifies a method or proc to call to
|
|
164
155
|
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
|
|
165
156
|
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
|
166
|
-
# method
|
|
157
|
+
# method or proc should return or evaluate to a +true+ or +false+
|
|
167
158
|
# value.
|
|
168
159
|
#
|
|
169
160
|
# NOTE: Calling +validate+ multiple times on the same method will overwrite previous definitions.
|
|
@@ -183,6 +174,15 @@ module ActiveModel
|
|
|
183
174
|
options = options.merge(if: [predicate_for_validation_context(options[:on]), *options[:if]])
|
|
184
175
|
end
|
|
185
176
|
|
|
177
|
+
if options.key?(:except_on)
|
|
178
|
+
options = options.dup
|
|
179
|
+
options[:except_on] = Array(options[:except_on])
|
|
180
|
+
options[:unless] = [
|
|
181
|
+
->(o) { options[:except_on].intersect?(Array(o.validation_context)) },
|
|
182
|
+
*options[:unless]
|
|
183
|
+
]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
186
|
set_callback(:validate, *args, options, &block)
|
|
187
187
|
end
|
|
188
188
|
|
|
@@ -361,15 +361,23 @@ module ActiveModel
|
|
|
361
361
|
# person.valid? # => true
|
|
362
362
|
# person.valid?(:new) # => false
|
|
363
363
|
def valid?(context = nil)
|
|
364
|
-
current_context
|
|
364
|
+
current_context = validation_context
|
|
365
|
+
context_for_validation.context = context
|
|
365
366
|
errors.clear
|
|
366
367
|
run_validations!
|
|
367
368
|
ensure
|
|
368
|
-
|
|
369
|
+
context_for_validation.context = current_context
|
|
369
370
|
end
|
|
370
371
|
|
|
371
372
|
alias_method :validate, :valid?
|
|
372
373
|
|
|
374
|
+
def freeze
|
|
375
|
+
errors
|
|
376
|
+
context_for_validation
|
|
377
|
+
|
|
378
|
+
super
|
|
379
|
+
end
|
|
380
|
+
|
|
373
381
|
# Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
|
|
374
382
|
# added, +false+ otherwise.
|
|
375
383
|
#
|
|
@@ -430,11 +438,38 @@ module ActiveModel
|
|
|
430
438
|
# end
|
|
431
439
|
alias :read_attribute_for_validation :send
|
|
432
440
|
|
|
441
|
+
# Returns the context when running validations.
|
|
442
|
+
#
|
|
443
|
+
# This is useful when running validations except a certain context (opposite to the +on+ option).
|
|
444
|
+
#
|
|
445
|
+
# class Person
|
|
446
|
+
# include ActiveModel::Validations
|
|
447
|
+
#
|
|
448
|
+
# attr_accessor :name
|
|
449
|
+
# validates :name, presence: true, if: -> { validation_context != :custom }
|
|
450
|
+
# end
|
|
451
|
+
#
|
|
452
|
+
# person = Person.new
|
|
453
|
+
# person.valid? #=> false
|
|
454
|
+
# person.valid?(:new) #=> false
|
|
455
|
+
# person.valid?(:custom) #=> true
|
|
456
|
+
def validation_context
|
|
457
|
+
context_for_validation.context
|
|
458
|
+
end
|
|
459
|
+
|
|
433
460
|
private
|
|
461
|
+
def validation_context=(context)
|
|
462
|
+
context_for_validation.context = context
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def context_for_validation
|
|
466
|
+
@context_for_validation ||= ValidationContext.new
|
|
467
|
+
end
|
|
468
|
+
|
|
434
469
|
def init_internals
|
|
435
470
|
super
|
|
436
471
|
@errors = nil
|
|
437
|
-
@
|
|
472
|
+
@context_for_validation = nil
|
|
438
473
|
end
|
|
439
474
|
|
|
440
475
|
def run_validations!
|
|
@@ -466,6 +501,10 @@ module ActiveModel
|
|
|
466
501
|
super(I18n.t(:"#{@model.class.i18n_scope}.errors.messages.model_invalid", errors: errors, default: :"errors.messages.model_invalid"))
|
|
467
502
|
end
|
|
468
503
|
end
|
|
504
|
+
|
|
505
|
+
class ValidationContext # :nodoc:
|
|
506
|
+
attr_accessor :context
|
|
507
|
+
end
|
|
469
508
|
end
|
|
470
509
|
|
|
471
510
|
Dir[File.expand_path("validations/*.rb", __dir__)].each { |file| require file }
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activemodel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 8.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Heinemeier Hansson
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 8.0.3
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: 8.0.3
|
|
26
26
|
description: A toolkit for building modeling frameworks like Active Record. Rich support
|
|
27
27
|
for attributes, callbacks, validations, serialization, internationalization, and
|
|
28
28
|
testing.
|
|
@@ -111,10 +111,10 @@ licenses:
|
|
|
111
111
|
- MIT
|
|
112
112
|
metadata:
|
|
113
113
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
|
114
|
-
changelog_uri: https://github.com/rails/rails/blob/
|
|
115
|
-
documentation_uri: https://api.rubyonrails.org/
|
|
114
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.0.3/activemodel/CHANGELOG.md
|
|
115
|
+
documentation_uri: https://api.rubyonrails.org/v8.0.3/
|
|
116
116
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
|
117
|
-
source_code_uri: https://github.com/rails/rails/tree/
|
|
117
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.0.3/activemodel
|
|
118
118
|
rubygems_mfa_required: 'true'
|
|
119
119
|
rdoc_options: []
|
|
120
120
|
require_paths:
|
|
@@ -123,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
123
123
|
requirements:
|
|
124
124
|
- - ">="
|
|
125
125
|
- !ruby/object:Gem::Version
|
|
126
|
-
version: 3.
|
|
126
|
+
version: 3.2.0
|
|
127
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
128
|
requirements:
|
|
129
129
|
- - ">="
|