activerecord 8.0.0.rc1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c87db8720964fea39e42e8eb013edb1fc3fd47e5964e5d6d6fdbe46295766f02
4
- data.tar.gz: c4bf7718e51c5accaad971f57faae94312d5cf81f5cfc4e58e8d4fe138027964
3
+ metadata.gz: fdca074c8ca76d0768b2989fb21aaafa5c31f3b80ce1d22275ccfd9d88dd6910
4
+ data.tar.gz: a282424df2605d4c5239f8eedbeb129fe4190e387e7da03292b6ad13e8b2e0ef
5
5
  SHA512:
6
- metadata.gz: 81389502abf74508e9eb58bcf7c91088bf4c44b858bd121128e801fee6a3a58309e0f455444bbe43faea18f7fe458610ab04ad8286d8e9faebf06ba5198bcba9
7
- data.tar.gz: 85de6837aff3ae8a24f177c53e2f3912c6660493bbb4138f75223a2dcb813c7f796444bd4d0196f606d16ce1bde6d3352d30d54279c72bb99910eee7f4183924
6
+ metadata.gz: f636c65b58ab040b9d820854f97f16df201d08142e50c609bb6acbd1f4817d55c2eeeb3859541c9b26ec47c48b20e01a7ec4e5736a68e6c8ce8d4ecb6342d39a
7
+ data.tar.gz: 7768bdbf0ff1b8dc3c369fc3168c0c66a56fcf21c00502043f846b1531a8e5962067987e33e43015172a8f346829362598838df197f5fa59f02ed44e9cb6a583
data/CHANGELOG.md CHANGED
@@ -1,3 +1,44 @@
1
+ ## Rails 8.0.0 (November 07, 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
+ ## Rails 8.0.0.rc2 (October 30, 2024) ##
10
+
11
+ * NULLS NOT DISTINCT works with UNIQUE CONSTRAINT as well as UNIQUE INDEX.
12
+
13
+ *Ryuta Kamizono*
14
+
15
+ * The `db:prepare` task no longer loads seeds when a non-primary database is created.
16
+
17
+ Previously, the `db:prepare` task would load seeds whenever a new database
18
+ is created, leading to potential loss of data if a database is added to an
19
+ existing environment.
20
+
21
+ Introduces a new database config property `seeds` to control whether seeds
22
+ are loaded during `db:prepare` which defaults to `true` for primary database
23
+ configs and `false` otherwise.
24
+
25
+ Fixes #53348.
26
+
27
+ *Mike Dalessio*
28
+
29
+ * `PG::UnableToSend: no connection to the server` is now retryable as a connection-related exception
30
+
31
+ *Kazuma Watanabe*
32
+
33
+ * Fix strict loading propagation even if statement cache is not used.
34
+
35
+ *Ryuta Kamizono*
36
+
37
+ * Allow `rename_enum` accepts two from/to name arguments as `rename_table` does so.
38
+
39
+ *Ryuta Kamizono*
40
+
41
+
1
42
  ## Rails 8.0.0.rc1 (October 19, 2024) ##
2
43
 
3
44
  * Remove deprecated support to setting `ENV["SCHEMA_CACHE"]`.
@@ -82,9 +123,9 @@
82
123
 
83
124
  *Ariel Rzezak*
84
125
 
85
- * When running `db:migrate` on a fresh database, load the database schema before running migrations.
126
+ * When running `db:migrate` on a fresh database, load the databases schemas before running migrations.
86
127
 
87
- *Andrew Novoselac*
128
+ *Andrew Novoselac*, *Marek Kasztelnik*
88
129
 
89
130
  * Fix an issue where `.left_outer_joins` used with multiple associations that have
90
131
  the same child association but different parents does not join all parents.
@@ -43,6 +43,7 @@ module ActiveRecord
43
43
  def exec_queries
44
44
  super do |record|
45
45
  @association.set_inverse_instance_from_queries(record)
46
+ @association.set_strict_loading(record)
46
47
  yield record if block_given?
47
48
  end
48
49
  end
@@ -120,6 +120,14 @@ module ActiveRecord
120
120
  @association_scope = nil
121
121
  end
122
122
 
123
+ def set_strict_loading(record)
124
+ if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
125
+ record.strict_loading!
126
+ else
127
+ record.strict_loading!(false, mode: owner.strict_loading_mode)
128
+ end
129
+ end
130
+
123
131
  # Set the inverse association, if possible
124
132
  def set_inverse_instance(record)
125
133
  if inverse = inverse_association_for(record)
@@ -260,11 +268,7 @@ module ActiveRecord
260
268
  klass.with_connection do |c|
261
269
  sc.execute(binds, c, async: async) do |record|
262
270
  set_inverse_instance(record)
263
- if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
264
- record.strict_loading!
265
- else
266
- record.strict_loading!(false, mode: owner.strict_loading_mode)
267
- end
271
+ set_strict_loading(record)
268
272
  end
269
273
  end
270
274
  end
@@ -559,7 +559,7 @@ module ActiveRecord
559
559
  # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
560
560
  # @group.avatars.delete(@group.avatars.last) # so would this
561
561
  #
562
- # == Setting Inverses
562
+ # === Setting Inverses
563
563
  #
564
564
  # If you are using a #belongs_to on the join model, it is a good idea to set the
565
565
  # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
@@ -1221,8 +1221,11 @@ module ActiveRecord
1221
1221
  # If you are going to modify the association (rather than just read from it), then it is
1222
1222
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1223
1223
  # join model. This allows associated records to be built which will automatically create
1224
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1225
- # and 'Setting Inverses' sections above.)
1224
+ # the appropriate join model records when they are saved. See
1225
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1226
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1227
+ # more detail.
1228
+ #
1226
1229
  # [+:disable_joins+]
1227
1230
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1228
1231
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1251,7 +1254,8 @@ module ActiveRecord
1251
1254
  # [+:inverse_of+]
1252
1255
  # Specifies the name of the #belongs_to association on the associated object
1253
1256
  # that is the inverse of this #has_many association.
1254
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1257
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1258
+ # for more detail.
1255
1259
  # [+:extend+]
1256
1260
  # Specifies a module or array of modules that will be extended into the association object returned.
1257
1261
  # Useful for defining methods on associations, especially when they should be shared between multiple
@@ -1300,10 +1304,12 @@ module ActiveRecord
1300
1304
  Reflection.add_reflection self, name, reflection
1301
1305
  end
1302
1306
 
1303
- # Specifies a one-to-one association with another class. This method should only be used
1304
- # if the other class contains the foreign key. If the current class contains the foreign key,
1305
- # then you should use #belongs_to instead. See also ActiveRecord::Associations::ClassMethods's overview
1306
- # on when to use #has_one and when to use #belongs_to.
1307
+ # Specifies a one-to-one association with another class. This method
1308
+ # should only be used if the other class contains the foreign key. If
1309
+ # the current class contains the foreign key, then you should use
1310
+ # #belongs_to instead. See {Is it a belongs_to or has_one
1311
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1312
+ # for more detail on when to use #has_one and when to use #belongs_to.
1307
1313
  #
1308
1314
  # The following methods for retrieval and query of a single associated object will be added:
1309
1315
  #
@@ -1418,8 +1424,10 @@ module ActiveRecord
1418
1424
  # If you are going to modify the association (rather than just read from it), then it is
1419
1425
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1420
1426
  # join model. This allows associated records to be built which will automatically create
1421
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1422
- # and 'Setting Inverses' sections above.)
1427
+ # the appropriate join model records when they are saved. See
1428
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1429
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1430
+ # more detail.
1423
1431
  # [+:disable_joins+]
1424
1432
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1425
1433
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1457,7 +1465,8 @@ module ActiveRecord
1457
1465
  # [+:inverse_of+]
1458
1466
  # Specifies the name of the #belongs_to association on the associated object
1459
1467
  # that is the inverse of this #has_one association.
1460
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1468
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1469
+ # for more detail.
1461
1470
  # [+:required+]
1462
1471
  # When set to +true+, the association will also have its presence validated.
1463
1472
  # This will validate the association itself, not the id. You can use
@@ -1491,10 +1500,12 @@ module ActiveRecord
1491
1500
  Reflection.add_reflection self, name, reflection
1492
1501
  end
1493
1502
 
1494
- # Specifies a one-to-one association with another class. This method should only be used
1495
- # if this class contains the foreign key. If the other class contains the foreign key,
1496
- # then you should use #has_one instead. See also ActiveRecord::Associations::ClassMethods's overview
1497
- # on when to use #has_one and when to use #belongs_to.
1503
+ # Specifies a one-to-one association with another class. This method
1504
+ # should only be used if this class contains the foreign key. If the
1505
+ # other class contains the foreign key, then you should use #has_one
1506
+ # instead. See {Is it a belongs_to or has_one
1507
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1508
+ # for more detail on when to use #has_one and when to use #belongs_to.
1498
1509
  #
1499
1510
  # Methods will be added for retrieval and query for a single associated object, for which
1500
1511
  # this object holds an id:
@@ -1636,7 +1647,8 @@ module ActiveRecord
1636
1647
  # [+:inverse_of+]
1637
1648
  # Specifies the name of the #has_one or #has_many association on the associated
1638
1649
  # object that is the inverse of this #belongs_to association.
1639
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1650
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1651
+ # for more detail.
1640
1652
  # [+:optional+]
1641
1653
  # When set to +true+, the association will not have its presence validated.
1642
1654
  # [+:required+]
@@ -84,7 +84,7 @@ module ActiveRecord
84
84
  attribute_method_patterns_cache.clear
85
85
  end
86
86
 
87
- def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
87
+ def alias_attribute_method_definition(code_generator, pattern, new_name, old_name) # :nodoc:
88
88
  old_name = old_name.to_s
89
89
 
90
90
  if !abstract_class? && !has_attribute?(old_name)
@@ -674,7 +674,7 @@ module ActiveRecord
674
674
  raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
675
675
  end
676
676
 
677
- # We make sure to run query transformers on the orignal thread
677
+ # We make sure to run query transformers on the original thread
678
678
  sql = preprocess_query(sql)
679
679
  future_result = async.new(
680
680
  pool,
@@ -52,6 +52,7 @@ module ActiveRecord
52
52
  sql = ["CONSTRAINT"]
53
53
  sql << quote_column_name(o.name)
54
54
  sql << "UNIQUE"
55
+ sql << "NULLS NOT DISTINCT" if supports_nulls_not_distinct? && o.nulls_not_distinct
55
56
 
56
57
  if o.using_index
57
58
  sql << "USING INDEX #{quote_column_name(o.using_index)}"
@@ -224,6 +224,10 @@ module ActiveRecord
224
224
  options[:using_index]
225
225
  end
226
226
 
227
+ def nulls_not_distinct
228
+ options[:nulls_not_distinct]
229
+ end
230
+
227
231
  def export_name_on_schema_dump?
228
232
  !ActiveRecord::SchemaDumper.unique_ignore_pattern.match?(name) if name
229
233
  end
@@ -317,7 +321,7 @@ module ActiveRecord
317
321
 
318
322
  # Adds a unique constraint.
319
323
  #
320
- # t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred)
324
+ # t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred, nulls_not_distinct: true)
321
325
  #
322
326
  # See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
323
327
  def unique_constraint(*args)
@@ -68,6 +68,7 @@ module ActiveRecord
68
68
  "t.unique_constraint #{unique_constraint.column.inspect}"
69
69
  ]
70
70
 
71
+ parts << "nulls_not_distinct: #{unique_constraint.nulls_not_distinct.inspect}" if unique_constraint.nulls_not_distinct
71
72
  parts << "deferrable: #{unique_constraint.deferrable.inspect}" if unique_constraint.deferrable
72
73
 
73
74
  if unique_constraint.export_name_on_schema_dump?
@@ -698,7 +698,7 @@ module ActiveRecord
698
698
  scope = quoted_scope(table_name)
699
699
 
700
700
  unique_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
701
- SELECT c.conname, c.conrelid, c.conkey, c.condeferrable, c.condeferred
701
+ SELECT c.conname, c.conrelid, c.conkey, c.condeferrable, c.condeferred, pg_get_constraintdef(c.oid) AS constraintdef
702
702
  FROM pg_constraint c
703
703
  JOIN pg_class t ON c.conrelid = t.oid
704
704
  JOIN pg_namespace n ON n.oid = c.connamespace
@@ -711,10 +711,12 @@ module ActiveRecord
711
711
  conkey = row["conkey"].delete("{}").split(",").map(&:to_i)
712
712
  columns = column_names_from_column_numbers(row["conrelid"], conkey)
713
713
 
714
+ nulls_not_distinct = row["constraintdef"].start_with?("UNIQUE NULLS NOT DISTINCT")
714
715
  deferrable = extract_constraint_deferrable(row["condeferrable"], row["condeferred"])
715
716
 
716
717
  options = {
717
718
  name: row["conname"],
719
+ nulls_not_distinct: nulls_not_distinct,
718
720
  deferrable: deferrable
719
721
  }
720
722
 
@@ -771,7 +773,7 @@ module ActiveRecord
771
773
 
772
774
  # Adds a new unique constraint to the table.
773
775
  #
774
- # add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position"
776
+ # add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position", nulls_not_distinct: true
775
777
  #
776
778
  # generates:
777
779
  #
@@ -788,6 +790,9 @@ module ActiveRecord
788
790
  # Specify whether or not the unique constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
789
791
  # [<tt>:using_index</tt>]
790
792
  # To specify an existing unique index name. Defaults to +nil+.
793
+ # [<tt>:nulls_not_distinct</tt>]
794
+ # Create a unique constraint where NULLs are treated equally.
795
+ # Note: only supported by PostgreSQL version 15.0.0 and greater.
791
796
  def add_unique_constraint(table_name, column_name = nil, **options)
792
797
  options = unique_constraint_options(table_name, column_name, options)
793
798
  at = create_alter_table(table_name)
@@ -575,8 +575,10 @@ module ActiveRecord
575
575
  end
576
576
 
577
577
  # Rename an existing enum type to something else.
578
- def rename_enum(name, **options)
579
- new_name = options.fetch(:to) { raise ArgumentError, ":to is required" }
578
+ def rename_enum(name, new_name = nil, **options)
579
+ new_name ||= options.fetch(:to) do
580
+ raise ArgumentError, "rename_enum requires two from/to name positional arguments."
581
+ end
580
582
 
581
583
  exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}").tap { reload_type_map }
582
584
  end
@@ -796,7 +798,7 @@ module ActiveRecord
796
798
 
797
799
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
798
800
  when nil
799
- if exception.message.match?(/connection is closed/i)
801
+ if exception.message.match?(/connection is closed/i) || exception.message.match?(/no connection to the server/i)
800
802
  ConnectionNotEstablished.new(exception, connection_pool: @pool)
801
803
  elsif exception.is_a?(PG::ConnectionBad)
802
804
  # libpq message style always ends with a newline; the pg gem's internal
@@ -103,7 +103,19 @@ module ActiveRecord
103
103
 
104
104
  class_attribute :shard_selector, instance_accessor: false, default: nil
105
105
 
106
- # Specifies the attributes that will be included in the output of the #inspect method
106
+ ##
107
+ # :singleton-method:
108
+ #
109
+ # Specifies the attributes that will be included in the output of the
110
+ # #inspect method:
111
+ #
112
+ # Post.attributes_for_inspect = [:id, :title]
113
+ # Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
114
+ #
115
+ # When set to `:all` inspect will list all the record's attributes:
116
+ #
117
+ # Post.attributes_for_inspect = :all
118
+ # Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
107
119
  class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
108
120
 
109
121
  def self.application_record_class? # :nodoc:
@@ -728,12 +740,26 @@ module ActiveRecord
728
740
  self.class.connection_handler
729
741
  end
730
742
 
731
- # Returns the attributes specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string.
743
+ # Returns the attributes of the record as a nicely formatted string.
744
+ #
745
+ # Post.first.inspect
746
+ # #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
747
+ #
748
+ # The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
749
+ #
750
+ # Post.attributes_for_inspect = [:id, :title]
751
+ # Post.first.inspect
752
+ # #=> "#<Post id: 1, title: "Hello, World!">"
732
753
  def inspect
733
754
  inspect_with_attributes(attributes_for_inspect)
734
755
  end
735
756
 
736
- # Returns the full contents of the record as a nicely formatted string.
757
+ # Returns all attributes of the record as a nicely formatted string,
758
+ # ignoring <tt>.attributes_for_inspect</tt>.
759
+ #
760
+ # Post.first.full_inspect
761
+ # #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
762
+ #
737
763
  def full_inspect
738
764
  inspect_with_attributes(all_attributes_for_inspect)
739
765
  end
@@ -99,6 +99,10 @@ module ActiveRecord
99
99
  def use_metadata_table?
100
100
  raise NotImplementedError
101
101
  end
102
+
103
+ def seeds?
104
+ raise NotImplementedError
105
+ end
102
106
  end
103
107
  end
104
108
  end
@@ -130,6 +130,14 @@ module ActiveRecord
130
130
  Base.configurations.primary?(name)
131
131
  end
132
132
 
133
+ # Determines whether the db:prepare task should seed the database from db/seeds.rb.
134
+ #
135
+ # If the `seeds` key is present in the config, `seeds?` will return its value. Otherwise, it
136
+ # will return `true` for the primary database and `false` for all other configs.
137
+ def seeds?
138
+ configuration_hash.fetch(:seeds, primary?)
139
+ end
140
+
133
141
  # Determines whether to dump the schema/structure files and the filename that
134
142
  # should be used.
135
143
  #
@@ -213,74 +213,79 @@ module ActiveRecord
213
213
  attr_reader :name, :mapping
214
214
  end
215
215
 
216
- def enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
217
- assert_valid_enum_definition_values(values)
218
- assert_valid_enum_options(options)
216
+ def enum(name, values = nil, **options)
217
+ values, options = options, {} unless values
218
+ _enum(name, values, **options)
219
+ end
219
220
 
220
- # statuses = { }
221
- enum_values = ActiveSupport::HashWithIndifferentAccess.new
222
- name = name.to_s
221
+ private
222
+ def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
223
+ assert_valid_enum_definition_values(values)
224
+ assert_valid_enum_options(options)
223
225
 
224
- # def self.statuses() statuses end
225
- detect_enum_conflict!(name, name.pluralize, true)
226
- singleton_class.define_method(name.pluralize) { enum_values }
227
- defined_enums[name] = enum_values
226
+ # statuses = { }
227
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
228
+ name = name.to_s
228
229
 
229
- detect_enum_conflict!(name, name)
230
- detect_enum_conflict!(name, "#{name}=")
230
+ # def self.statuses() statuses end
231
+ detect_enum_conflict!(name, name.pluralize, true)
232
+ singleton_class.define_method(name.pluralize) { enum_values }
233
+ defined_enums[name] = enum_values
231
234
 
232
- attribute(name, **options)
235
+ detect_enum_conflict!(name, name)
236
+ detect_enum_conflict!(name, "#{name}=")
233
237
 
234
- decorate_attributes([name]) do |_name, subtype|
235
- if subtype == ActiveModel::Type.default_value
236
- raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
237
- " backed by a database column or declared with an explicit type" \
238
- " via `attribute`."
239
- end
238
+ attribute(name, **options)
240
239
 
241
- subtype = subtype.subtype if EnumType === subtype
242
- EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
243
- end
240
+ decorate_attributes([name]) do |_name, subtype|
241
+ if subtype == ActiveModel::Type.default_value
242
+ raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
243
+ " backed by a database column or declared with an explicit type" \
244
+ " via `attribute`."
245
+ end
244
246
 
245
- value_method_names = []
246
- _enum_methods_module.module_eval do
247
- prefix = if prefix
248
- prefix == true ? "#{name}_" : "#{prefix}_"
247
+ subtype = subtype.subtype if EnumType === subtype
248
+ EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
249
249
  end
250
250
 
251
- suffix = if suffix
252
- suffix == true ? "_#{name}" : "_#{suffix}"
253
- end
251
+ value_method_names = []
252
+ _enum_methods_module.module_eval do
253
+ prefix = if prefix
254
+ prefix == true ? "#{name}_" : "#{prefix}_"
255
+ end
256
+
257
+ suffix = if suffix
258
+ suffix == true ? "_#{name}" : "_#{suffix}"
259
+ end
254
260
 
255
- pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
256
- pairs.each do |label, value|
257
- enum_values[label] = value
258
- label = label.to_s
261
+ pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
262
+ pairs.each do |label, value|
263
+ enum_values[label] = value
264
+ label = label.to_s
259
265
 
260
- value_method_name = "#{prefix}#{label}#{suffix}"
261
- value_method_names << value_method_name
262
- define_enum_methods(name, value_method_name, value, scopes, instance_methods)
266
+ value_method_name = "#{prefix}#{label}#{suffix}"
267
+ value_method_names << value_method_name
268
+ define_enum_methods(name, value_method_name, value, scopes, instance_methods)
263
269
 
264
- method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
265
- value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
270
+ method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
271
+ value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
266
272
 
267
- if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
268
- value_method_names << value_method_alias
269
- define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
273
+ if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
274
+ value_method_names << value_method_alias
275
+ define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
276
+ end
270
277
  end
271
278
  end
272
- end
273
- detect_negative_enum_conditions!(value_method_names) if scopes
279
+ detect_negative_enum_conditions!(value_method_names) if scopes
274
280
 
275
- if validate
276
- validate = {} unless Hash === validate
277
- validates_inclusion_of name, in: enum_values.keys, **validate
278
- end
281
+ if validate
282
+ validate = {} unless Hash === validate
283
+ validates_inclusion_of name, in: enum_values.keys, **validate
284
+ end
279
285
 
280
- enum_values.freeze
281
- end
286
+ enum_values.freeze
287
+ end
282
288
 
283
- private
284
289
  def inherited(base)
285
290
  base.defined_enums = defined_enums.deep_dup
286
291
  super
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  MAJOR = 8
11
11
  MINOR = 0
12
12
  TINY = 0
13
- PRE = "rc1"
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -240,7 +240,7 @@ module ActiveRecord
240
240
 
241
241
  values_list = insert_all.map_key_with_value do |key, value|
242
242
  next value if Arel::Nodes::SqlLiteral === value
243
- types[key].serialize(types[key].cast(value))
243
+ ActiveModel::Type::SerializeCastValue.serialize(type = types[key], type.cast(value))
244
244
  end
245
245
 
246
246
  connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
@@ -40,7 +40,7 @@ module ActiveRecord
40
40
  # * remove_reference
41
41
  # * remove_timestamps
42
42
  # * rename_column
43
- # * rename_enum (must supply a +:to+ option)
43
+ # * rename_enum
44
44
  # * rename_enum_value (must supply a +:from+ and +:to+ option)
45
45
  # * rename_index
46
46
  # * rename_table
@@ -364,13 +364,13 @@ module ActiveRecord
364
364
  end
365
365
 
366
366
  def invert_rename_enum(args)
367
- name, options = args
367
+ name, new_name, = args
368
368
 
369
- unless options.is_a?(Hash) && options.has_key?(:to)
370
- raise ActiveRecord::IrreversibleMigration, "rename_enum is only reversible if given a :to option."
369
+ if new_name.is_a?(Hash) && new_name.key?(:to)
370
+ new_name = new_name[:to]
371
371
  end
372
372
 
373
- [:rename_enum, [options[:to], to: name]]
373
+ [:rename_enum, [new_name, name]]
374
374
  end
375
375
 
376
376
  def invert_rename_enum_value(args)
@@ -930,7 +930,7 @@ module ActiveRecord
930
930
  )
931
931
 
932
932
  returning_columns.zip(returning_values).each do |column, value|
933
- _write_attribute(column, value) if !_read_attribute(column)
933
+ _write_attribute(column, type_for_attribute(column).deserialize(value)) if !_read_attribute(column)
934
934
  end if returning_values
935
935
  end
936
936
 
@@ -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)
@@ -69,7 +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 if Rails.env.production?
72
+ ActiveRecord::Base.attributes_for_inspect = :all
73
73
  end
74
74
 
75
75
  runner do
@@ -87,22 +87,7 @@ db_namespace = namespace :db do
87
87
 
88
88
  desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
89
89
  task migrate: :load_config do
90
- db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
91
-
92
- if db_configs.size == 1 && db_configs.first.primary?
93
- ActiveRecord::Tasks::DatabaseTasks.migrate
94
- else
95
- mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
96
-
97
- mapped_versions.sort.each do |version, db_configs|
98
- db_configs.each do |db_config|
99
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
100
- ActiveRecord::Tasks::DatabaseTasks.migrate(version)
101
- end
102
- end
103
- end
104
- end
105
-
90
+ ActiveRecord::Tasks::DatabaseTasks.migrate_all
106
91
  db_namespace["_dump"].invoke
107
92
  end
108
93
 
@@ -498,7 +498,8 @@ module ActiveRecord
498
498
 
499
499
  # Like #with, but modifies relation in place.
500
500
  def with!(*args) # :nodoc:
501
- self.with_values += args
501
+ args = process_with_args(args)
502
+ self.with_values |= args
502
503
  self
503
504
  end
504
505
 
@@ -521,7 +522,8 @@ module ActiveRecord
521
522
 
522
523
  # Like #with_recursive but modifies the relation in place.
523
524
  def with_recursive!(*args) # :nodoc:
524
- self.with_values += args
525
+ args = process_with_args(args)
526
+ self.with_values |= args
525
527
  @with_is_recursive = true
526
528
  self
527
529
  end
@@ -1912,8 +1914,6 @@ module ActiveRecord
1912
1914
  return if with_values.empty?
1913
1915
 
1914
1916
  with_statements = with_values.map do |with_value|
1915
- raise ArgumentError, "Unsupported argument type: #{with_value} #{with_value.class}" unless with_value.is_a?(Hash)
1916
-
1917
1917
  build_with_value_from_hash(with_value)
1918
1918
  end
1919
1919
 
@@ -1964,10 +1964,10 @@ module ActiveRecord
1964
1964
  table_name = table_name.name if table_name.is_a?(Symbol)
1965
1965
  case columns
1966
1966
  when Symbol, String
1967
- arel_column_with_table(table_name, columns.to_s)
1967
+ arel_column_with_table(table_name, columns)
1968
1968
  when Array
1969
1969
  columns.map do |column|
1970
- arel_column_with_table(table_name, column.to_s)
1970
+ arel_column_with_table(table_name, column)
1971
1971
  end
1972
1972
  else
1973
1973
  raise TypeError, "Expected Symbol, String or Array, got: #{columns.class}"
@@ -1977,8 +1977,13 @@ module ActiveRecord
1977
1977
 
1978
1978
  def arel_column_with_table(table_name, column_name)
1979
1979
  self.references_values |= [Arel.sql(table_name, retryable: true)]
1980
- predicate_builder.resolve_arel_attribute(table_name, column_name) do
1981
- lookup_table_klass_from_join_dependencies(table_name)
1980
+
1981
+ if column_name.is_a?(Symbol) || !column_name.match?(/\W/)
1982
+ predicate_builder.resolve_arel_attribute(table_name, column_name) do
1983
+ lookup_table_klass_from_join_dependencies(table_name)
1984
+ end
1985
+ else
1986
+ Arel.sql("#{model.adapter_class.quote_table_name(table_name)}.#{column_name}")
1982
1987
  end
1983
1988
  end
1984
1989
 
@@ -1994,6 +1999,8 @@ module ActiveRecord
1994
1999
  arel_column_with_table(table, column)
1995
2000
  elsif block_given?
1996
2001
  yield field
2002
+ elsif Arel.arel_node?(field)
2003
+ field
1997
2004
  else
1998
2005
  Arel.sql(is_symbol ? model.adapter_class.quote_table_name(field) : field)
1999
2006
  end
@@ -2136,7 +2143,7 @@ module ActiveRecord
2136
2143
  arg.expr.relation.name
2137
2144
  end
2138
2145
  end
2139
- end.compact
2146
+ end.filter_map { |ref| Arel.sql(ref, retryable: true) if ref }
2140
2147
  end
2141
2148
 
2142
2149
  def extract_table_name_from(string)
@@ -2230,12 +2237,12 @@ module ActiveRecord
2230
2237
  case columns_aliases
2231
2238
  when Hash
2232
2239
  columns_aliases.map do |column, column_alias|
2233
- arel_column_with_table(table_name, column.to_s)
2240
+ arel_column_with_table(table_name, column)
2234
2241
  .as(model.adapter_class.quote_column_name(column_alias.to_s))
2235
2242
  end
2236
2243
  when Array
2237
2244
  columns_aliases.map do |column|
2238
- arel_column_with_table(table_name, column.to_s)
2245
+ arel_column_with_table(table_name, column)
2239
2246
  end
2240
2247
  when String, Symbol
2241
2248
  arel_column(key)
@@ -2244,6 +2251,13 @@ module ActiveRecord
2244
2251
  end
2245
2252
  end
2246
2253
 
2254
+ def process_with_args(args)
2255
+ args.flat_map do |arg|
2256
+ raise ArgumentError, "Unsupported argument type: #{arg} #{arg.class}" unless arg.is_a?(Hash)
2257
+ arg.map { |k, v| { k => v } }
2258
+ end
2259
+ end
2260
+
2247
2261
  STRUCTURAL_VALUE_METHODS = (
2248
2262
  Relation::VALUE_METHODS -
2249
2263
  [:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
@@ -180,7 +180,7 @@ module ActiveRecord
180
180
  each_current_configuration(env) do |db_config|
181
181
  database_initialized = initialize_database(db_config)
182
182
 
183
- seed = true if database_initialized
183
+ seed = true if database_initialized && db_config.seeds?
184
184
  end
185
185
 
186
186
  each_current_environment(env) do |environment|
@@ -240,13 +240,32 @@ module ActiveRecord
240
240
  end
241
241
  end
242
242
 
243
- def migrate(version = nil)
243
+ def migrate_all
244
+ db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
245
+ db_configs.each { |db_config| initialize_database(db_config) }
246
+
247
+ if db_configs.size == 1 && db_configs.first.primary?
248
+ ActiveRecord::Tasks::DatabaseTasks.migrate(skip_initialize: true)
249
+ else
250
+ mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
251
+
252
+ mapped_versions.sort.each do |version, db_configs|
253
+ db_configs.each do |db_config|
254
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
255
+ ActiveRecord::Tasks::DatabaseTasks.migrate(version, skip_initialize: true)
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ def migrate(version = nil, skip_initialize: false)
244
263
  scope = ENV["SCOPE"]
245
264
  verbose_was, Migration.verbose = Migration.verbose, verbose?
246
265
 
247
266
  check_target_version
248
267
 
249
- initialize_database(migration_connection_pool.db_config)
268
+ initialize_database(migration_connection_pool.db_config) unless skip_initialize
250
269
 
251
270
  migration_connection_pool.migration_context.migrate(target_version) do |migration|
252
271
  if version.blank?
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: 8.0.0.rc1
4
+ version: 8.0.0
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-19 00:00:00.000000000 Z
11
+ date: 2024-11-07 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: 8.0.0.rc1
19
+ version: 8.0.0
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: 8.0.0.rc1
26
+ version: 8.0.0
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: 8.0.0.rc1
33
+ version: 8.0.0
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: 8.0.0.rc1
40
+ version: 8.0.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: timeout
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -475,10 +475,10 @@ licenses:
475
475
  - MIT
476
476
  metadata:
477
477
  bug_tracker_uri: https://github.com/rails/rails/issues
478
- changelog_uri: https://github.com/rails/rails/blob/v8.0.0.rc1/activerecord/CHANGELOG.md
479
- documentation_uri: https://api.rubyonrails.org/v8.0.0.rc1/
478
+ changelog_uri: https://github.com/rails/rails/blob/v8.0.0/activerecord/CHANGELOG.md
479
+ documentation_uri: https://api.rubyonrails.org/v8.0.0/
480
480
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
481
- source_code_uri: https://github.com/rails/rails/tree/v8.0.0.rc1/activerecord
481
+ source_code_uri: https://github.com/rails/rails/tree/v8.0.0/activerecord
482
482
  rubygems_mfa_required: 'true'
483
483
  post_install_message:
484
484
  rdoc_options:
@@ -497,7 +497,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
497
497
  - !ruby/object:Gem::Version
498
498
  version: '0'
499
499
  requirements: []
500
- rubygems_version: 3.5.16
500
+ rubygems_version: 3.5.22
501
501
  signing_key:
502
502
  specification_version: 4
503
503
  summary: Object-relational mapper framework (part of Rails).