activerecord 7.2.1.2 → 7.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -0
  3. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  4. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  5. data/lib/active_record/associations/join_dependency.rb +4 -4
  6. data/lib/active_record/associations.rb +28 -16
  7. data/lib/active_record/attribute_assignment.rb +9 -1
  8. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  9. data/lib/active_record/attributes.rb +1 -2
  10. data/lib/active_record/callbacks.rb +1 -1
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -0
  12. data/lib/active_record/connection_adapters/abstract/query_cache.rb +4 -2
  13. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
  14. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +7 -3
  15. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  16. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -0
  17. data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -3
  18. data/lib/active_record/core.rb +39 -7
  19. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  20. data/lib/active_record/encryption/key_provider.rb +1 -1
  21. data/lib/active_record/encryption.rb +2 -0
  22. data/lib/active_record/enum.rb +7 -10
  23. data/lib/active_record/gem_version.rb +2 -2
  24. data/lib/active_record/marshalling.rb +4 -1
  25. data/lib/active_record/model_schema.rb +1 -2
  26. data/lib/active_record/nested_attributes.rb +13 -2
  27. data/lib/active_record/query_cache.rb +4 -5
  28. data/lib/active_record/query_logs.rb +1 -1
  29. data/lib/active_record/railtie.rb +7 -12
  30. data/lib/active_record/reflection.rb +5 -2
  31. data/lib/active_record/relation/calculations.rb +1 -1
  32. data/lib/active_record/relation/predicate_builder.rb +1 -1
  33. data/lib/active_record/relation/query_methods.rb +39 -24
  34. data/lib/active_record/tasks/database_tasks.rb +12 -1
  35. data/lib/active_record/testing/query_assertions.rb +2 -2
  36. data/lib/active_record/validations/uniqueness.rb +1 -0
  37. data/lib/arel/visitors/sqlite.rb +25 -0
  38. metadata +10 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 739cd17115b22c2ea13f9e9804df9669170c404bf83ba7c9b00ee45b79b04d81
4
- data.tar.gz: d25023b9882e1308c2d673491ff6677d4372febd451716f644b5f68e92285e09
3
+ metadata.gz: 0fa184c958d89b0bfbb95921e93d9dee4396b91ea57216f69cdaa0907fca900f
4
+ data.tar.gz: bab6a4f88acae3ad29b12d2d84229ad386acd8bca6e617804285b8d7534ea1ea
5
5
  SHA512:
6
- metadata.gz: 1ffa0273c6e6e2997c2ae1053cf616e0b4352b2fbd50fdf2b62bfc6a48f47db46ed002ba07f520ce6f4ce7809bf28eddd9f8c21160003ce8e67ba1cc1e7326b2
7
- data.tar.gz: c1a72e4b291a4eef9731564cb4b1108afb353cbc7e8f2a47362adeead927e01c1d346841bad352c701c49c220793662cfad0a850a37320e5515df99c9f6f11a8
6
+ metadata.gz: 91cf7ccf875cbd0a5c687e354edd6f4338d9b7edcc900952f9b4fef3843c03488abcfba7c2fd4a001954c9891283c232cb9719ad5f3e9383f23656c04930ff2f
7
+ data.tar.gz: 4577e8f11d54b506f3d0fe841a25a1bbb7df529f22f51f08f0c70ec097098e80adab1d95f24f7048879ce5eb3aff6d330c124ad34c6667ebfdcc1c5823832821
data/CHANGELOG.md CHANGED
@@ -1,3 +1,147 @@
1
+ ## Rails 7.2.2.1 (December 10, 2024) ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 7.2.2 (October 30, 2024) ##
7
+
8
+ * Fix support for `query_cache: false` in `database.yml`.
9
+
10
+ `query_cache: false` would no longer entirely disable the Active Record query cache.
11
+
12
+ *zzak*
13
+
14
+ * Set `.attributes_for_inspect` to `:all` by default.
15
+
16
+ For new applications it is set to `[:id]` in config/environment/production.rb.
17
+
18
+ In the console all the attributes are always shown.
19
+
20
+ *Andrew Novoselac*
21
+
22
+ * `PG::UnableToSend: no connection to the server` is now retryable as a connection-related exception
23
+
24
+ *Kazuma Watanabe*
25
+
26
+ * Fix marshalling of unsaved associated records in 7.1 format.
27
+
28
+ The 7.1 format would only marshal associated records if the association was loaded.
29
+ But associations that would only contain unsaved records would be skipped.
30
+
31
+ *Jean Boussier*
32
+
33
+ * Fix incorrect SQL query when passing an empty hash to `ActiveRecord::Base.insert`.
34
+
35
+ *David Stosik*
36
+
37
+ * Allow to save records with polymorphic join tables that have `inverse_of`
38
+ specified.
39
+
40
+ *Markus Doits*
41
+
42
+ * Fix association scopes applying on the incorrect join when using a polymorphic `has_many through:`.
43
+
44
+ *Joshua Young*
45
+
46
+ * Fix `dependent: :destroy` for bi-directional has one through association.
47
+
48
+ Fixes #50948.
49
+
50
+ ```ruby
51
+ class Left < ActiveRecord::Base
52
+ has_one :middle, dependent: :destroy
53
+ has_one :right, through: :middle
54
+ end
55
+
56
+ class Middle < ActiveRecord::Base
57
+ belongs_to :left, dependent: :destroy
58
+ belongs_to :right, dependent: :destroy
59
+ end
60
+
61
+ class Right < ActiveRecord::Base
62
+ has_one :middle, dependent: :destroy
63
+ has_one :left, through: :middle
64
+ end
65
+ ```
66
+ In the above example `left.destroy` wouldn't destroy its associated `Right`
67
+ record.
68
+
69
+ *Andy Stewart*
70
+
71
+ * Properly handle lazily pinned connection pools.
72
+
73
+ Fixes #53147.
74
+
75
+ When using transactional fixtures with system tests to similar tools
76
+ such as capybara, it could happen that a connection end up pinned by the
77
+ server thread rather than the test thread, causing
78
+ `"Cannot expire connection, it is owned by a different thread"` errors.
79
+
80
+ *Jean Boussier*
81
+
82
+ * Fix `ActiveRecord::Base.with` to accept more than two sub queries.
83
+
84
+ Fixes #53110.
85
+
86
+ ```ruby
87
+ User.with(foo: [User.select(:id), User.select(:id), User.select(:id)]).to_sql
88
+ undefined method `union' for an instance of Arel::Nodes::UnionAll (NoMethodError)
89
+ ```
90
+
91
+ The above now works as expected.
92
+
93
+ *fatkodima*
94
+
95
+ * Properly release pinned connections with non joinable connections.
96
+
97
+ Fixes #52973
98
+
99
+ When running system tests with transactional fixtures on, it could happen that
100
+ the connection leased by the Puma thread wouldn't be properly released back to the pool,
101
+ causing "Cannot expire connection, it is owned by a different thread" errors in later tests.
102
+
103
+ *Jean Boussier*
104
+
105
+ * Make Float distinguish between `float4` and `float8` in PostgreSQL.
106
+
107
+ Fixes #52742
108
+
109
+ *Ryota Kitazawa*, *Takayuki Nagatomi*
110
+
111
+ * Fix an issue where `.left_outer_joins` used with multiple associations that have
112
+ the same child association but different parents does not join all parents.
113
+
114
+ Previously, using `.left_outer_joins` with the same child association would only join one of the parents.
115
+
116
+ Now it will correctly join both parents.
117
+
118
+ Fixes #41498.
119
+
120
+ *Garrett Blehm*
121
+
122
+ * Ensure `ActiveRecord::Encryption.config` is always ready before access.
123
+
124
+ Previously, `ActiveRecord::Encryption` configuration was deferred until `ActiveRecord::Base`
125
+ was loaded. Therefore, accessing `ActiveRecord::Encryption.config` properties before
126
+ `ActiveRecord::Base` was loaded would give incorrect results.
127
+
128
+ `ActiveRecord::Encryption` now has its own loading hook so that its configuration is set as
129
+ soon as needed.
130
+
131
+ When `ActiveRecord::Base` is loaded, even lazily, it in turn triggers the loading of
132
+ `ActiveRecord::Encryption`, thus preserving the original behavior of having its config ready
133
+ before any use of `ActiveRecord::Base`.
134
+
135
+ *Maxime Réty*
136
+
137
+ * Add `TimeZoneConverter#==` method, so objects will be properly compared by
138
+ their type, scale, limit & precision.
139
+
140
+ Address #52699.
141
+
142
+ *Ruy Rocha*
143
+
144
+
1
145
  ## Rails 7.2.1.2 (October 23, 2024) ##
2
146
 
3
147
  * No changes.
@@ -93,7 +93,13 @@ module ActiveRecord
93
93
  @through_scope = scope
94
94
  record = super
95
95
 
96
- inverse = source_reflection.inverse_of
96
+ inverse =
97
+ if source_reflection.polymorphic?
98
+ source_reflection.polymorphic_inverse_of(record.class)
99
+ else
100
+ source_reflection.inverse_of
101
+ end
102
+
97
103
  if inverse
98
104
  if inverse.collection?
99
105
  record.send(inverse.name) << build_through_record(record)
@@ -25,8 +25,9 @@ module ActiveRecord
25
25
  joins = []
26
26
  chain = []
27
27
 
28
- reflection.chain.each do |reflection|
29
- table, terminated = yield reflection
28
+ reflection_chain = reflection.chain
29
+ reflection_chain.each_with_index do |reflection, index|
30
+ table, terminated = yield reflection, reflection_chain[index..]
30
31
  @table ||= table
31
32
 
32
33
  if terminated
@@ -190,12 +190,12 @@ module ActiveRecord
190
190
  def make_constraints(parent, child, join_type)
191
191
  foreign_table = parent.table
192
192
  foreign_klass = parent.base_klass
193
- child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection|
194
- table, terminated = @joined_tables[reflection]
193
+ child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection, remaining_reflection_chain|
194
+ table, terminated = @joined_tables[remaining_reflection_chain]
195
195
  root = reflection == child.reflection
196
196
 
197
197
  if table && (!root || !terminated)
198
- @joined_tables[reflection] = [table, root] if root
198
+ @joined_tables[remaining_reflection_chain] = [table, root] if root
199
199
  next table, true
200
200
  end
201
201
 
@@ -206,7 +206,7 @@ module ActiveRecord
206
206
  root ? name : "#{name}_join"
207
207
  end
208
208
 
209
- @joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
209
+ @joined_tables[remaining_reflection_chain] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
210
210
  table
211
211
  end.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
212
212
  end
@@ -537,7 +537,7 @@ module ActiveRecord
537
537
  # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
538
538
  # @group.avatars.delete(@group.avatars.last) # so would this
539
539
  #
540
- # == Setting Inverses
540
+ # === Setting Inverses
541
541
  #
542
542
  # If you are using a #belongs_to on the join model, it is a good idea to set the
543
543
  # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
@@ -1199,8 +1199,11 @@ module ActiveRecord
1199
1199
  # If you are going to modify the association (rather than just read from it), then it is
1200
1200
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1201
1201
  # join model. This allows associated records to be built which will automatically create
1202
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1203
- # and 'Setting Inverses' sections above.)
1202
+ # the appropriate join model records when they are saved. See
1203
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1204
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1205
+ # more detail.
1206
+ #
1204
1207
  # [+:disable_joins+]
1205
1208
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1206
1209
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1229,7 +1232,8 @@ module ActiveRecord
1229
1232
  # [+:inverse_of+]
1230
1233
  # Specifies the name of the #belongs_to association on the associated object
1231
1234
  # that is the inverse of this #has_many association.
1232
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1235
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1236
+ # for more detail.
1233
1237
  # [+:extend+]
1234
1238
  # Specifies a module or array of modules that will be extended into the association object returned.
1235
1239
  # Useful for defining methods on associations, especially when they should be shared between multiple
@@ -1270,10 +1274,12 @@ module ActiveRecord
1270
1274
  Reflection.add_reflection self, name, reflection
1271
1275
  end
1272
1276
 
1273
- # Specifies a one-to-one association with another class. This method should only be used
1274
- # if the other class contains the foreign key. If the current class contains the foreign key,
1275
- # then you should use #belongs_to instead. See also ActiveRecord::Associations::ClassMethods's overview
1276
- # on when to use #has_one and when to use #belongs_to.
1277
+ # Specifies a one-to-one association with another class. This method
1278
+ # should only be used if the other class contains the foreign key. If
1279
+ # the current class contains the foreign key, then you should use
1280
+ # #belongs_to instead. See {Is it a belongs_to or has_one
1281
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1282
+ # for more detail on when to use #has_one and when to use #belongs_to.
1277
1283
  #
1278
1284
  # The following methods for retrieval and query of a single associated object will be added:
1279
1285
  #
@@ -1388,8 +1394,10 @@ module ActiveRecord
1388
1394
  # If you are going to modify the association (rather than just read from it), then it is
1389
1395
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1390
1396
  # join model. This allows associated records to be built which will automatically create
1391
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1392
- # and 'Setting Inverses' sections above.)
1397
+ # the appropriate join model records when they are saved. See
1398
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1399
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1400
+ # more detail.
1393
1401
  # [+:disable_joins+]
1394
1402
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1395
1403
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1427,7 +1435,8 @@ module ActiveRecord
1427
1435
  # [+:inverse_of+]
1428
1436
  # Specifies the name of the #belongs_to association on the associated object
1429
1437
  # that is the inverse of this #has_one association.
1430
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1438
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1439
+ # for more detail.
1431
1440
  # [+:required+]
1432
1441
  # When set to +true+, the association will also have its presence validated.
1433
1442
  # This will validate the association itself, not the id. You can use
@@ -1461,10 +1470,12 @@ module ActiveRecord
1461
1470
  Reflection.add_reflection self, name, reflection
1462
1471
  end
1463
1472
 
1464
- # Specifies a one-to-one association with another class. This method should only be used
1465
- # if this class contains the foreign key. If the other class contains the foreign key,
1466
- # then you should use #has_one instead. See also ActiveRecord::Associations::ClassMethods's overview
1467
- # on when to use #has_one and when to use #belongs_to.
1473
+ # Specifies a one-to-one association with another class. This method
1474
+ # should only be used if this class contains the foreign key. If the
1475
+ # other class contains the foreign key, then you should use #has_one
1476
+ # instead. See {Is it a belongs_to or has_one
1477
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1478
+ # for more detail on when to use #has_one and when to use #belongs_to.
1468
1479
  #
1469
1480
  # Methods will be added for retrieval and query for a single associated object, for which
1470
1481
  # this object holds an id:
@@ -1606,7 +1617,8 @@ module ActiveRecord
1606
1617
  # [+:inverse_of+]
1607
1618
  # Specifies the name of the #has_one or #has_many association on the associated
1608
1619
  # object that is the inverse of this #belongs_to association.
1609
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1620
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1621
+ # for more detail.
1610
1622
  # [+:optional+]
1611
1623
  # When set to +true+, the association will not have its presence validated.
1612
1624
  # [+:required+]
@@ -4,21 +4,29 @@ module ActiveRecord
4
4
  module AttributeAssignment
5
5
  private
6
6
  def _assign_attributes(attributes)
7
- multi_parameter_attributes = nil
7
+ multi_parameter_attributes = nested_parameter_attributes = nil
8
8
 
9
9
  attributes.each do |k, v|
10
10
  key = k.to_s
11
11
 
12
12
  if key.include?("(")
13
13
  (multi_parameter_attributes ||= {})[key] = v
14
+ elsif v.is_a?(Hash)
15
+ (nested_parameter_attributes ||= {})[key] = v
14
16
  else
15
17
  _assign_attribute(key, v)
16
18
  end
17
19
  end
18
20
 
21
+ assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
19
22
  assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
20
23
  end
21
24
 
25
+ # Assign any deferred nested attributes after the base attributes have been set.
26
+ def assign_nested_parameter_attributes(pairs)
27
+ pairs.each { |k, v| _assign_attribute(k, v) }
28
+ end
29
+
22
30
  # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
23
31
  # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
24
32
  # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
@@ -32,6 +32,10 @@ module ActiveRecord
32
32
  end
33
33
  end
34
34
 
35
+ def ==(other)
36
+ other.is_a?(self.class) && __getobj__ == other.__getobj__
37
+ end
38
+
35
39
  private
36
40
  def convert_time_to_time_zone(value)
37
41
  return if value.nil?
@@ -212,8 +212,7 @@ module ActiveRecord
212
212
  #--
213
213
  # Implemented by ActiveModel::AttributeRegistration#attribute.
214
214
 
215
- # This is the low level API which sits beneath +attribute+. It only
216
- # accepts type objects, and will do its work immediately instead of
215
+ # This API only accepts type objects, and will do its work immediately instead of
217
216
  # waiting for the schema to load. While this method
218
217
  # is provided so it can be used by plugin authors, application code
219
218
  # should probably use ClassMethods#attribute.
@@ -418,7 +418,7 @@ module ActiveRecord
418
418
 
419
419
  def destroy # :nodoc:
420
420
  @_destroy_callback_already_called ||= false
421
- return if @_destroy_callback_already_called
421
+ return true if @_destroy_callback_already_called
422
422
  @_destroy_callback_already_called = true
423
423
  _run_destroy_callbacks { super }
424
424
  rescue RecordNotDestroyed => e
@@ -184,6 +184,31 @@ module ActiveRecord
184
184
  end
185
185
  end
186
186
 
187
+ module ExecutorHooks # :nodoc:
188
+ class << self
189
+ def run
190
+ # noop
191
+ end
192
+
193
+ def complete(_)
194
+ ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
195
+ if (connection = pool.active_connection?)
196
+ transaction = connection.current_transaction
197
+ if transaction.closed? || !transaction.joinable?
198
+ pool.release_connection
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ class << self
207
+ def install_executor_hooks(executor = ActiveSupport::Executor)
208
+ executor.register_hook(ExecutorHooks)
209
+ end
210
+ end
211
+
187
212
  include MonitorMixin
188
213
  prepend QueryCache::ConnectionPoolConfiguration
189
214
  include ConnectionAdapters::AbstractPool
@@ -333,6 +358,7 @@ module ActiveRecord
333
358
  end
334
359
 
335
360
  if @pinned_connection.nil?
361
+ connection.steal!
336
362
  connection.lock_thread = nil
337
363
  checkin(connection)
338
364
  end
@@ -157,11 +157,13 @@ module ActiveRecord
157
157
  end
158
158
 
159
159
  def enable_query_cache!
160
- query_cache.enabled, query_cache.dirties = true, true
160
+ query_cache.enabled = true
161
+ query_cache.dirties = true
161
162
  end
162
163
 
163
164
  def disable_query_cache!
164
- query_cache.enabled, query_cache.dirties = false, true
165
+ query_cache.enabled = false
166
+ query_cache.dirties = true
165
167
  end
166
168
 
167
169
  def query_cache_enabled
@@ -682,7 +682,7 @@ module ActiveRecord
682
682
  #
683
683
  # If the options provided include an +if_exists+ key, it will be used to check if the
684
684
  # column does not exist. This will silently ignore the migration rather than raising
685
- # if the column was already used.
685
+ # if the column was already removed.
686
686
  #
687
687
  # remove_column(:suppliers, :qualification, if_exists: true)
688
688
  def remove_column(table_name, column_name, type = nil, **options)
@@ -639,7 +639,7 @@ module ActiveRecord
639
639
  end
640
640
 
641
641
  def build_insert_sql(insert) # :nodoc:
642
- no_op_column = quote_column_name(insert.keys.first)
642
+ no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
643
643
 
644
644
  # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
645
645
  # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
@@ -648,7 +648,9 @@ module ActiveRecord
648
648
  sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
649
649
 
650
650
  if insert.skip_duplicates?
651
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
651
+ if no_op_column
652
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
653
+ end
652
654
  elsif insert.update_duplicates?
653
655
  if insert.raw_update_sql?
654
656
  sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
@@ -662,7 +664,9 @@ module ActiveRecord
662
664
  sql = +"INSERT #{insert.into} #{insert.values_list}"
663
665
 
664
666
  if insert.skip_duplicates?
665
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
667
+ if no_op_column
668
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
669
+ end
666
670
  elsif insert.update_duplicates?
667
671
  sql << " ON DUPLICATE KEY UPDATE "
668
672
  if insert.raw_update_sql?
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # TODO: Remove when IPAddr#== compares IPAddr#prefix. See
32
32
  # https://github.com/ruby/ipaddr/issues/21
33
33
  def changed?(old_value, new_value, _new_value_before_type_cast)
34
- super || !old_value.nil? && old_value.prefix != new_value.prefix
34
+ !old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
35
35
  end
36
36
 
37
37
  def cast_value(value)
@@ -255,6 +255,8 @@ module ActiveRecord
255
255
 
256
256
  # Returns the sequence name for a table's primary key or some other specified key.
257
257
  def default_sequence_name(table_name, pk = "id") # :nodoc:
258
+ return nil if pk.is_a?(Array)
259
+
258
260
  result = serial_sequence(table_name, pk)
259
261
  return nil unless result
260
262
  Utils.extract_schema_qualified_name(result).to_s
@@ -652,8 +652,8 @@ module ActiveRecord
652
652
  m.register_type "int4", Type::Integer.new(limit: 4)
653
653
  m.register_type "int8", Type::Integer.new(limit: 8)
654
654
  m.register_type "oid", OID::Oid.new
655
- m.register_type "float4", Type::Float.new
656
- m.alias_type "float8", "float4"
655
+ m.register_type "float4", Type::Float.new(limit: 24)
656
+ m.register_type "float8", Type::Float.new
657
657
  m.register_type "text", Type::Text.new
658
658
  register_class_with_limit m, "varchar", Type::String
659
659
  m.alias_type "char", "varchar"
@@ -777,7 +777,7 @@ module ActiveRecord
777
777
 
778
778
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
779
779
  when nil
780
- if exception.message.match?(/connection is closed/i)
780
+ if exception.message.match?(/connection is closed/i) || exception.message.match?(/no connection to the server/i)
781
781
  ConnectionNotEstablished.new(exception, connection_pool: @pool)
782
782
  elsif exception.is_a?(PG::ConnectionBad)
783
783
  # libpq message style always ends with a newline; the pg gem's internal
@@ -102,8 +102,20 @@ module ActiveRecord
102
102
 
103
103
  class_attribute :shard_selector, instance_accessor: false, default: nil
104
104
 
105
- # Specifies the attributes that will be included in the output of the #inspect method
106
- class_attribute :attributes_for_inspect, instance_accessor: false, default: [:id]
105
+ ##
106
+ # :singleton-method:
107
+ #
108
+ # Specifies the attributes that will be included in the output of the
109
+ # #inspect method:
110
+ #
111
+ # Post.attributes_for_inspect = [:id, :title]
112
+ # Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
113
+ #
114
+ # When set to `:all` inspect will list all the record's attributes:
115
+ #
116
+ # Post.attributes_for_inspect = :all
117
+ # Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
118
+ class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
107
119
 
108
120
  def self.application_record_class? # :nodoc:
109
121
  if ActiveRecord.application_record_class
@@ -349,7 +361,7 @@ module ActiveRecord
349
361
 
350
362
  # Returns a string like 'Post(id:integer, title:string, body:text)'
351
363
  def inspect # :nodoc:
352
- if self == Base
364
+ if self == Base || singleton_class?
353
365
  super
354
366
  elsif abstract_class?
355
367
  "#{super}(abstract)"
@@ -724,14 +736,28 @@ module ActiveRecord
724
736
  self.class.connection_handler
725
737
  end
726
738
 
727
- # Returns the attributes specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string.
739
+ # Returns the attributes of the record as a nicely formatted string.
740
+ #
741
+ # Post.first.inspect
742
+ # #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
743
+ #
744
+ # The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
745
+ #
746
+ # Post.attributes_for_inspect = [:id, :title]
747
+ # Post.first.inspect
748
+ # #=> "#<Post id: 1, title: "Hello, World!">"
728
749
  def inspect
729
750
  inspect_with_attributes(attributes_for_inspect)
730
751
  end
731
752
 
732
- # Returns the full contents of the record as a nicely formatted string.
753
+ # Returns all attributes of the record as a nicely formatted string,
754
+ # ignoring <tt>.attributes_for_inspect</tt>.
755
+ #
756
+ # Post.first.full_inspect
757
+ # #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
758
+ #
733
759
  def full_inspect
734
- inspect_with_attributes(attribute_names)
760
+ inspect_with_attributes(all_attributes_for_inspect)
735
761
  end
736
762
 
737
763
  # Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
@@ -823,7 +849,13 @@ module ActiveRecord
823
849
  end
824
850
 
825
851
  def attributes_for_inspect
826
- self.class.attributes_for_inspect == :all ? attribute_names : self.class.attributes_for_inspect
852
+ self.class.attributes_for_inspect == :all ? all_attributes_for_inspect : self.class.attributes_for_inspect
853
+ end
854
+
855
+ def all_attributes_for_inspect
856
+ return [] unless @attributes
857
+
858
+ attribute_names
827
859
  end
828
860
  end
829
861
  end
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  attr_reader :uri
46
46
 
47
47
  def uri_parser
48
- @uri_parser ||= URI::Parser.new
48
+ @uri_parser ||= URI::RFC2396_Parser.new
49
49
  end
50
50
 
51
51
  # Converts the query parameters of the URI into a hash.
@@ -12,7 +12,7 @@ module ActiveRecord
12
12
  @keys = Array(keys)
13
13
  end
14
14
 
15
- # Returns the first key in the list as the active key to perform encryptions
15
+ # Returns the last key in the list as the active key to perform encryptions
16
16
  #
17
17
  # When +ActiveRecord::Encryption.config.store_key_references+ is true, the key will include
18
18
  # a public tag referencing the key itself. That key will be stored in the public
@@ -53,4 +53,6 @@ module ActiveRecord
53
53
  Cipher.eager_load!
54
54
  end
55
55
  end
56
+
57
+ ActiveSupport.run_load_hooks :active_record_encryption, Encryption
56
58
  end
@@ -167,15 +167,6 @@ module ActiveRecord
167
167
  base.class_attribute(:defined_enums, instance_writer: false, default: {})
168
168
  end
169
169
 
170
- def load_schema! # :nodoc:
171
- defined_enums.each_key do |name|
172
- unless columns_hash.key?(resolve_attribute_name(name))
173
- raise "Unknown enum attribute '#{name}' for #{self.name}. Enums must be" \
174
- " backed by a database column."
175
- end
176
- end
177
- end
178
-
179
170
  class EnumType < Type::Value # :nodoc:
180
171
  delegate :type, to: :subtype
181
172
 
@@ -264,7 +255,13 @@ module ActiveRecord
264
255
 
265
256
  attribute(name, **options)
266
257
 
267
- decorate_attributes([name]) do |name, subtype|
258
+ decorate_attributes([name]) do |_name, subtype|
259
+ if subtype == ActiveModel::Type.default_value
260
+ raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
261
+ " backed by a database column or declared with an explicit type" \
262
+ " via `attribute`."
263
+ end
264
+
268
265
  subtype = subtype.subtype if EnumType === subtype
269
266
  EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
270
267
  end
@@ -9,8 +9,8 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 2
12
- TINY = 1
13
- PRE = "2"
12
+ TINY = 2
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -25,7 +25,10 @@ module ActiveRecord
25
25
  payload = [attributes_for_database, new_record?]
26
26
 
27
27
  cached_associations = self.class.reflect_on_all_associations.select do |reflection|
28
- association_cached?(reflection.name) && association(reflection.name).loaded?
28
+ if association_cached?(reflection.name)
29
+ association = association(reflection.name)
30
+ association.loaded? || association.target.present?
31
+ end
29
32
  end
30
33
 
31
34
  unless cached_associations.empty?
@@ -431,7 +431,6 @@ module ActiveRecord
431
431
  end
432
432
 
433
433
  def columns
434
- load_schema unless @columns
435
434
  @columns ||= columns_hash.values.freeze
436
435
  end
437
436
 
@@ -595,7 +594,7 @@ module ActiveRecord
595
594
  columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
596
595
  @columns_hash = columns_hash.freeze
597
596
 
598
- super
597
+ _default_attributes # Precompute to cache DB-dependent attribute types
599
598
  end
600
599
 
601
600
  # Guesses the table name, but does not decorate it with prefix and suffix information.
@@ -524,12 +524,12 @@ module ActiveRecord
524
524
  unless reject_new_record?(association_name, attributes)
525
525
  association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
526
526
  end
527
- elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
527
+ elsif existing_record = find_record_by_id(existing_records, attributes["id"])
528
528
  unless call_reject_if(association_name, attributes)
529
529
  # Make sure we are operating on the actual object which is in the association's
530
530
  # proxy_target array (either by finding it, or adding it if not found)
531
531
  # Take into account that the proxy_target may have changed due to callbacks
532
- target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
532
+ target_record = find_record_by_id(association.target, attributes["id"])
533
533
  if target_record
534
534
  existing_record = target_record
535
535
  else
@@ -620,5 +620,16 @@ module ActiveRecord
620
620
  raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
621
621
  model, "id", record_id)
622
622
  end
623
+
624
+ def find_record_by_id(records, id)
625
+ return if records.empty?
626
+
627
+ if records.first.class.composite_primary_key?
628
+ id = Array(id).map(&:to_s)
629
+ records.find { |record| Array(record.id).map(&:to_s) == id }
630
+ else
631
+ records.find { |record| record.id.to_s == id.to_s }
632
+ end
633
+ end
623
634
  end
624
635
  end
@@ -35,7 +35,10 @@ module ActiveRecord
35
35
  end
36
36
 
37
37
  def self.run
38
- ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each(&:enable_query_cache!)
38
+ ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
39
+ next if pool.db_config&.query_cache == false
40
+ pool.enable_query_cache!
41
+ end
39
42
  end
40
43
 
41
44
  def self.complete(pools)
@@ -43,10 +46,6 @@ module ActiveRecord
43
46
  pool.disable_query_cache!
44
47
  pool.clear_query_cache
45
48
  end
46
-
47
- ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
48
- pool.release_connection if pool.active_connection? && !pool.lease_connection.transaction_open?
49
- end
50
49
  end
51
50
 
52
51
  def self.install_executor_hooks(executor = ActiveSupport::Executor)
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  if Thread.respond_to?(:each_caller_location)
113
113
  def query_source_location # :nodoc:
114
114
  Thread.each_caller_location do |location|
115
- frame = LogSubscriber.backtrace_cleaner.clean_frame(location.path)
115
+ frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
116
116
  return frame if frame
117
117
  end
118
118
  nil
@@ -69,6 +69,7 @@ module ActiveRecord
69
69
  Rails.logger.broadcast_to(console)
70
70
  end
71
71
  ActiveRecord.verbose_query_logs = false
72
+ ActiveRecord::Base.attributes_for_inspect = :all
72
73
  end
73
74
 
74
75
  runner do
@@ -311,6 +312,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
311
312
  initializer "active_record.set_executor_hooks" do
312
313
  ActiveRecord::QueryCache.install_executor_hooks
313
314
  ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
315
+ ActiveRecord::ConnectionAdapters::ConnectionPool.install_executor_hooks
314
316
  end
315
317
 
316
318
  initializer "active_record.add_watchable_files" do |app|
@@ -356,16 +358,19 @@ To keep using the current cache store, you can turn off cache versioning entirel
356
358
  end
357
359
 
358
360
  initializer "active_record_encryption.configuration" do |app|
359
- ActiveSupport.on_load(:active_record) do
360
- ActiveRecord::Encryption.configure \
361
+ ActiveSupport.on_load(:active_record_encryption) do
362
+ ActiveRecord::Encryption.configure(
361
363
  primary_key: app.credentials.dig(:active_record_encryption, :primary_key),
362
364
  deterministic_key: app.credentials.dig(:active_record_encryption, :deterministic_key),
363
365
  key_derivation_salt: app.credentials.dig(:active_record_encryption, :key_derivation_salt),
364
366
  **app.config.active_record.encryption
367
+ )
365
368
 
366
369
  auto_filtered_parameters = ActiveRecord::Encryption::AutoFilteredParameters.new(app)
367
370
  auto_filtered_parameters.enable if ActiveRecord::Encryption.config.add_to_filter_parameters
371
+ end
368
372
 
373
+ ActiveSupport.on_load(:active_record) do
369
374
  # Support extended queries for deterministic attributes and validations
370
375
  if ActiveRecord::Encryption.config.extend_queries
371
376
  ActiveRecord::Encryption::ExtendedDeterministicQueries.install_support
@@ -432,15 +437,5 @@ To keep using the current cache store, you can turn off cache versioning entirel
432
437
  end
433
438
  end
434
439
  end
435
-
436
- initializer "active_record.attributes_for_inspect" do |app|
437
- ActiveSupport.on_load(:active_record) do
438
- if app.config.consider_all_requests_local
439
- if app.config.active_record.attributes_for_inspect.nil?
440
- ActiveRecord::Base.attributes_for_inspect = :all
441
- end
442
- end
443
- end
444
- end
445
440
  end
446
441
  end
@@ -79,7 +79,7 @@ module ActiveRecord
79
79
  normalized_reflections.stringify_keys
80
80
  end
81
81
 
82
- def normalized_reflections # :nodoc
82
+ def normalized_reflections # :nodoc:
83
83
  @__reflections ||= begin
84
84
  ref = {}
85
85
 
@@ -1239,7 +1239,10 @@ module ActiveRecord
1239
1239
  end
1240
1240
 
1241
1241
  def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1242
- scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
1242
+ scopes = super
1243
+ unless @previous_reflection.through_reflection?
1244
+ scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
1245
+ end
1243
1246
  scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
1244
1247
  end
1245
1248
 
@@ -307,8 +307,8 @@ module ActiveRecord
307
307
  relation.pluck(*column_names)
308
308
  else
309
309
  klass.disallow_raw_sql!(flattened_args(column_names))
310
- columns = arel_columns(column_names)
311
310
  relation = spawn
311
+ columns = relation.arel_columns(column_names)
312
312
  relation.select_values = columns
313
313
  result = skip_query_cache_if_necessary do
314
314
  if where_clause.contradiction?
@@ -142,7 +142,7 @@ module ActiveRecord
142
142
  queries.first
143
143
  else
144
144
  queries.map! { |query| query.reduce(&:and) }
145
- queries = queries.reduce { |result, query| Arel::Nodes::Or.new([result, query]) }
145
+ queries = Arel::Nodes::Or.new(queries)
146
146
  Arel::Nodes::Grouping.new(queries)
147
147
  end
148
148
  end
@@ -1636,6 +1636,26 @@ module ActiveRecord
1636
1636
  self
1637
1637
  end
1638
1638
 
1639
+ protected
1640
+ def arel_columns(columns)
1641
+ columns.flat_map do |field|
1642
+ case field
1643
+ when Symbol
1644
+ arel_column(field.to_s) do |attr_name|
1645
+ adapter_class.quote_table_name(attr_name)
1646
+ end
1647
+ when String
1648
+ arel_column(field, &:itself)
1649
+ when Proc
1650
+ field.call
1651
+ when Hash
1652
+ arel_columns_from_hash(field)
1653
+ else
1654
+ field
1655
+ end
1656
+ end
1657
+ end
1658
+
1639
1659
  private
1640
1660
  def async
1641
1661
  spawn.async!
@@ -1890,12 +1910,26 @@ module ActiveRecord
1890
1910
  end
1891
1911
  end
1892
1912
 
1893
- def build_with_expression_from_value(value)
1913
+ def build_with_expression_from_value(value, nested = false)
1894
1914
  case value
1895
1915
  when Arel::Nodes::SqlLiteral then Arel::Nodes::Grouping.new(value)
1896
- when ActiveRecord::Relation then value.arel
1916
+ when ActiveRecord::Relation
1917
+ if nested
1918
+ value.arel.ast
1919
+ else
1920
+ value.arel
1921
+ end
1897
1922
  when Arel::SelectManager then value
1898
- when Array then value.map { |q| build_with_expression_from_value(q) }.reduce { |result, value| result.union(:all, value) }
1923
+ when Array
1924
+ return build_with_expression_from_value(value.first, false) if value.size == 1
1925
+
1926
+ parts = value.map do |query|
1927
+ build_with_expression_from_value(query, true)
1928
+ end
1929
+
1930
+ parts.reduce do |result, value|
1931
+ Arel::Nodes::UnionAll.new(result, value)
1932
+ end
1899
1933
  else
1900
1934
  raise ArgumentError, "Unsupported argument type: `#{value}` #{value.class}"
1901
1935
  end
@@ -1909,33 +1943,14 @@ module ActiveRecord
1909
1943
  ).join_sources.first
1910
1944
  end
1911
1945
 
1912
- def arel_columns(columns)
1913
- columns.flat_map do |field|
1914
- case field
1915
- when Symbol
1916
- arel_column(field.to_s) do |attr_name|
1917
- adapter_class.quote_table_name(attr_name)
1918
- end
1919
- when String
1920
- arel_column(field, &:itself)
1921
- when Proc
1922
- field.call
1923
- when Hash
1924
- arel_columns_from_hash(field)
1925
- else
1926
- field
1927
- end
1928
- end
1929
- end
1930
-
1931
1946
  def arel_column(field)
1932
1947
  field = klass.attribute_aliases[field] || field
1933
1948
  from = from_clause.name || from_clause.value
1934
1949
 
1935
1950
  if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
1936
1951
  table[field]
1937
- elsif field.match?(/\A\w+\.\w+\z/)
1938
- table, column = field.split(".")
1952
+ elsif /\A(?<table>(?:\w+\.)?\w+)\.(?<column>\w+)\z/ =~ field
1953
+ self.references_values |= [Arel.sql(table, retryable: true)]
1939
1954
  predicate_builder.resolve_arel_attribute(table, column) do
1940
1955
  lookup_table_klass_from_join_dependencies(table)
1941
1956
  end
@@ -175,6 +175,7 @@ module ActiveRecord
175
175
 
176
176
  def prepare_all
177
177
  seed = false
178
+ dump_db_configs = []
178
179
 
179
180
  each_current_configuration(env) do |db_config|
180
181
  with_temporary_pool(db_config) do
@@ -197,15 +198,25 @@ module ActiveRecord
197
198
 
198
199
  each_current_environment(env) do |environment|
199
200
  db_configs_with_versions(environment).sort.each do |version, db_configs|
201
+ dump_db_configs |= db_configs
202
+
200
203
  db_configs.each do |db_config|
201
204
  with_temporary_pool(db_config) do
202
205
  migrate(version)
203
- dump_schema(db_config) if ActiveRecord.dump_schema_after_migration
204
206
  end
205
207
  end
206
208
  end
207
209
  end
208
210
 
211
+ # Dump schema for databases that were migrated.
212
+ if ActiveRecord.dump_schema_after_migration
213
+ dump_db_configs.each do |db_config|
214
+ with_temporary_pool(db_config) do
215
+ dump_schema(db_config)
216
+ end
217
+ end
218
+ end
219
+
209
220
  load_seed if seed
210
221
  end
211
222
 
@@ -52,7 +52,7 @@ module ActiveRecord
52
52
  # assert_queries_match(/LIMIT \?/) { Post.first }
53
53
  #
54
54
  # If the +:include_schema+ option is provided, any queries (including schema related)
55
- # that match the matcher are considered.
55
+ # that match the matcher are considered.
56
56
  #
57
57
  # assert_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
58
58
  #
@@ -80,7 +80,7 @@ module ActiveRecord
80
80
  # assert_no_queries_match(/SELECT/i) { post.comments }
81
81
  #
82
82
  # If the +:include_schema+ option is provided, any queries (including schema related)
83
- # that match the matcher are counted.
83
+ # that match the matcher are counted.
84
84
  #
85
85
  # assert_no_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
86
86
  #
@@ -14,6 +14,7 @@ module ActiveRecord
14
14
  end
15
15
  super
16
16
  @klass = options[:class]
17
+ @klass = @klass.superclass if @klass.singleton_class?
17
18
  end
18
19
 
19
20
  def validate_each(record, attribute, value)
@@ -33,6 +33,31 @@ module Arel # :nodoc: all
33
33
  collector << " IS NOT "
34
34
  visit o.right, collector
35
35
  end
36
+
37
+ # Queries used in UNION should not be wrapped by parentheses,
38
+ # because it is an invalid syntax in SQLite.
39
+ def infix_value_with_paren(o, collector, value, suppress_parens = false)
40
+ collector << "( " unless suppress_parens
41
+
42
+ left = o.left.is_a?(Nodes::Grouping) ? o.left.expr : o.left
43
+ collector = if left.class == o.class
44
+ infix_value_with_paren(left, collector, value, true)
45
+ else
46
+ grouping_parentheses left, collector, false
47
+ end
48
+
49
+ collector << value
50
+
51
+ right = o.right.is_a?(Nodes::Grouping) ? o.right.expr : o.right
52
+ collector = if right.class == o.class
53
+ infix_value_with_paren(right, collector, value, true)
54
+ else
55
+ grouping_parentheses right, collector, false
56
+ end
57
+
58
+ collector << " )" unless suppress_parens
59
+ collector
60
+ end
36
61
  end
37
62
  end
38
63
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.1.2
4
+ version: 7.2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-23 00:00:00.000000000 Z
11
+ date: 2024-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 7.2.1.2
19
+ version: 7.2.2.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 7.2.1.2
26
+ version: 7.2.2.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 7.2.1.2
33
+ version: 7.2.2.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 7.2.1.2
40
+ version: 7.2.2.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: timeout
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -476,10 +476,10 @@ licenses:
476
476
  - MIT
477
477
  metadata:
478
478
  bug_tracker_uri: https://github.com/rails/rails/issues
479
- changelog_uri: https://github.com/rails/rails/blob/v7.2.1.2/activerecord/CHANGELOG.md
480
- documentation_uri: https://api.rubyonrails.org/v7.2.1.2/
479
+ changelog_uri: https://github.com/rails/rails/blob/v7.2.2.1/activerecord/CHANGELOG.md
480
+ documentation_uri: https://api.rubyonrails.org/v7.2.2.1/
481
481
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
482
- source_code_uri: https://github.com/rails/rails/tree/v7.2.1.2/activerecord
482
+ source_code_uri: https://github.com/rails/rails/tree/v7.2.2.1/activerecord
483
483
  rubygems_mfa_required: 'true'
484
484
  post_install_message:
485
485
  rdoc_options:
@@ -498,7 +498,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
498
498
  - !ruby/object:Gem::Version
499
499
  version: '0'
500
500
  requirements: []
501
- rubygems_version: 3.5.16
501
+ rubygems_version: 3.5.22
502
502
  signing_key:
503
503
  specification_version: 4
504
504
  summary: Object-relational mapper framework (part of Rails).