activerecord 7.2.1.1 → 7.2.2

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 (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 +9 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4d83507a149c9935cd951b39e4d422507f3d9d6712535b8ee78c6aaf078ce59
4
- data.tar.gz: f62a8fe46ab019e6b179980db6b3178b9fdb8dd98140ac0e91f595741fbc9baf
3
+ metadata.gz: 857ca134ca542d7fc1a0a8950ec0c24b8c66594c5a34298ad717da73ba5877d6
4
+ data.tar.gz: 0bf2140eb0df1bcdc98976389822b5d4978d8ffc2a8d7a237ea855c24ff329b1
5
5
  SHA512:
6
- metadata.gz: 303687dbb02622f0f9259c6cd09ef3657dfe799c0447f94fb8e3e740f0b8c9868c619b24e906bcae9a86ab99e8d49fce2e89fefe7873d5625b53fcf93ab1e93c
7
- data.tar.gz: 19087cc6d497f8901aa91faa3267fd369dc84bef1fc25717672ed0216ee53ed24ab798d928dfb5e1355001a998cd0a7179bdaf3b1efbc8c0955b1df46e598a1f
6
+ metadata.gz: 13574ac1dda8565be5e974d5799577feda4c5896aab0424afe49b05e9118a2aa9f07016e7fbc40c62360becc67ed9440e224e0db95dae93891fd5466c96f427e
7
+ data.tar.gz: 7741a92426fcd9be412d0279a7bd5b749ee316351e77c20f589cbd72798cac6e4c9645d96b12dab8f3d45f4fc6919a75272925147432ffc7a658ea5806979158
data/CHANGELOG.md CHANGED
@@ -1,3 +1,147 @@
1
+ ## Rails 7.2.2 (October 30, 2024) ##
2
+
3
+ * Fix support for `query_cache: false` in `database.yml`.
4
+
5
+ `query_cache: false` would no longer entirely disable the Active Record query cache.
6
+
7
+ *zzak*
8
+
9
+ * Set `.attributes_for_inspect` to `:all` by default.
10
+
11
+ For new applications it is set to `[:id]` in config/environment/production.rb.
12
+
13
+ In the console all the attributes are always shown.
14
+
15
+ *Andrew Novoselac*
16
+
17
+ * `PG::UnableToSend: no connection to the server` is now retryable as a connection-related exception
18
+
19
+ *Kazuma Watanabe*
20
+
21
+ * Fix marshalling of unsaved associated records in 7.1 format.
22
+
23
+ The 7.1 format would only marshal associated records if the association was loaded.
24
+ But associations that would only contain unsaved records would be skipped.
25
+
26
+ *Jean Boussier*
27
+
28
+ * Fix incorrect SQL query when passing an empty hash to `ActiveRecord::Base.insert`.
29
+
30
+ *David Stosik*
31
+
32
+ * Allow to save records with polymorphic join tables that have `inverse_of`
33
+ specified.
34
+
35
+ *Markus Doits*
36
+
37
+ * Fix association scopes applying on the incorrect join when using a polymorphic `has_many through:`.
38
+
39
+ *Joshua Young*
40
+
41
+ * Fix `dependent: :destroy` for bi-directional has one through association.
42
+
43
+ Fixes #50948.
44
+
45
+ ```ruby
46
+ class Left < ActiveRecord::Base
47
+ has_one :middle, dependent: :destroy
48
+ has_one :right, through: :middle
49
+ end
50
+
51
+ class Middle < ActiveRecord::Base
52
+ belongs_to :left, dependent: :destroy
53
+ belongs_to :right, dependent: :destroy
54
+ end
55
+
56
+ class Right < ActiveRecord::Base
57
+ has_one :middle, dependent: :destroy
58
+ has_one :left, through: :middle
59
+ end
60
+ ```
61
+ In the above example `left.destroy` wouldn't destroy its associated `Right`
62
+ record.
63
+
64
+ *Andy Stewart*
65
+
66
+ * Properly handle lazily pinned connection pools.
67
+
68
+ Fixes #53147.
69
+
70
+ When using transactional fixtures with system tests to similar tools
71
+ such as capybara, it could happen that a connection end up pinned by the
72
+ server thread rather than the test thread, causing
73
+ `"Cannot expire connection, it is owned by a different thread"` errors.
74
+
75
+ *Jean Boussier*
76
+
77
+ * Fix `ActiveRecord::Base.with` to accept more than two sub queries.
78
+
79
+ Fixes #53110.
80
+
81
+ ```ruby
82
+ User.with(foo: [User.select(:id), User.select(:id), User.select(:id)]).to_sql
83
+ undefined method `union' for an instance of Arel::Nodes::UnionAll (NoMethodError)
84
+ ```
85
+
86
+ The above now works as expected.
87
+
88
+ *fatkodima*
89
+
90
+ * Properly release pinned connections with non joinable connections.
91
+
92
+ Fixes #52973
93
+
94
+ When running system tests with transactional fixtures on, it could happen that
95
+ the connection leased by the Puma thread wouldn't be properly released back to the pool,
96
+ causing "Cannot expire connection, it is owned by a different thread" errors in later tests.
97
+
98
+ *Jean Boussier*
99
+
100
+ * Make Float distinguish between `float4` and `float8` in PostgreSQL.
101
+
102
+ Fixes #52742
103
+
104
+ *Ryota Kitazawa*, *Takayuki Nagatomi*
105
+
106
+ * Fix an issue where `.left_outer_joins` used with multiple associations that have
107
+ the same child association but different parents does not join all parents.
108
+
109
+ Previously, using `.left_outer_joins` with the same child association would only join one of the parents.
110
+
111
+ Now it will correctly join both parents.
112
+
113
+ Fixes #41498.
114
+
115
+ *Garrett Blehm*
116
+
117
+ * Ensure `ActiveRecord::Encryption.config` is always ready before access.
118
+
119
+ Previously, `ActiveRecord::Encryption` configuration was deferred until `ActiveRecord::Base`
120
+ was loaded. Therefore, accessing `ActiveRecord::Encryption.config` properties before
121
+ `ActiveRecord::Base` was loaded would give incorrect results.
122
+
123
+ `ActiveRecord::Encryption` now has its own loading hook so that its configuration is set as
124
+ soon as needed.
125
+
126
+ When `ActiveRecord::Base` is loaded, even lazily, it in turn triggers the loading of
127
+ `ActiveRecord::Encryption`, thus preserving the original behavior of having its config ready
128
+ before any use of `ActiveRecord::Base`.
129
+
130
+ *Maxime Réty*
131
+
132
+ * Add `TimeZoneConverter#==` method, so objects will be properly compared by
133
+ their type, scale, limit & precision.
134
+
135
+ Address #52699.
136
+
137
+ *Ruy Rocha*
138
+
139
+
140
+ ## Rails 7.2.1.2 (October 23, 2024) ##
141
+
142
+ * No changes.
143
+
144
+
1
145
  ## Rails 7.2.1.1 (October 15, 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 = "1"
12
+ TINY = 2
13
+ PRE = nil
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.1
4
+ version: 7.2.2
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-15 00:00:00.000000000 Z
11
+ date: 2024-10-31 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.1
19
+ version: 7.2.2
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.1
26
+ version: 7.2.2
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.1
33
+ version: 7.2.2
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.1
40
+ version: 7.2.2
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.1/activerecord/CHANGELOG.md
480
- documentation_uri: https://api.rubyonrails.org/v7.2.1.1/
479
+ changelog_uri: https://github.com/rails/rails/blob/v7.2.2/activerecord/CHANGELOG.md
480
+ documentation_uri: https://api.rubyonrails.org/v7.2.2/
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.1/activerecord
482
+ source_code_uri: https://github.com/rails/rails/tree/v7.2.2/activerecord
483
483
  rubygems_mfa_required: 'true'
484
484
  post_install_message:
485
485
  rdoc_options: