activerecord 8.0.0.rc1 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
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).