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 +4 -4
- data/CHANGELOG.md +43 -2
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/association.rb +9 -5
- data/lib/active_record/associations.rb +28 -16
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +7 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +5 -3
- data/lib/active_record/core.rb +29 -3
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +8 -0
- data/lib/active_record/enum.rb +55 -50
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +5 -5
- data/lib/active_record/persistence.rb +1 -1
- data/lib/active_record/query_cache.rb +4 -1
- data/lib/active_record/railtie.rb +1 -1
- data/lib/active_record/railties/databases.rake +1 -16
- data/lib/active_record/relation/query_methods.rb +25 -11
- data/lib/active_record/tasks/database_tasks.rb +22 -3
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fdca074c8ca76d0768b2989fb21aaafa5c31f3b80ce1d22275ccfd9d88dd6910
|
4
|
+
data.tar.gz: a282424df2605d4c5239f8eedbeb129fe4190e387e7da03292b6ad13e8b2e0ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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.
|
@@ -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
|
-
|
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
|
-
#
|
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.
|
1225
|
-
#
|
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
|
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
|
1304
|
-
# if the other class contains the foreign key. If
|
1305
|
-
#
|
1306
|
-
#
|
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.
|
1422
|
-
#
|
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
|
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
|
1495
|
-
# if this class contains the foreign key. If the
|
1496
|
-
# then you should use #has_one
|
1497
|
-
#
|
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
|
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
|
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
|
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
|
data/lib/active_record/core.rb
CHANGED
@@ -103,7 +103,19 @@ module ActiveRecord
|
|
103
103
|
|
104
104
|
class_attribute :shard_selector, instance_accessor: false, default: nil
|
105
105
|
|
106
|
-
|
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
|
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
|
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
|
@@ -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
|
#
|
data/lib/active_record/enum.rb
CHANGED
@@ -213,74 +213,79 @@ module ActiveRecord
|
|
213
213
|
attr_reader :name, :mapping
|
214
214
|
end
|
215
215
|
|
216
|
-
def enum(name, values
|
217
|
-
|
218
|
-
|
216
|
+
def enum(name, values = nil, **options)
|
217
|
+
values, options = options, {} unless values
|
218
|
+
_enum(name, values, **options)
|
219
|
+
end
|
219
220
|
|
220
|
-
|
221
|
-
|
222
|
-
|
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
|
-
|
225
|
-
|
226
|
-
|
227
|
-
defined_enums[name] = enum_values
|
226
|
+
# statuses = { }
|
227
|
+
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
228
|
+
name = name.to_s
|
228
229
|
|
229
|
-
|
230
|
-
|
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
|
-
|
235
|
+
detect_enum_conflict!(name, name)
|
236
|
+
detect_enum_conflict!(name, "#{name}=")
|
233
237
|
|
234
|
-
|
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
|
-
|
242
|
-
|
243
|
-
|
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
|
-
|
246
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
265
|
-
|
270
|
+
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
271
|
+
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
266
272
|
|
267
|
-
|
268
|
-
|
269
|
-
|
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
|
-
|
273
|
-
detect_negative_enum_conditions!(value_method_names) if scopes
|
279
|
+
detect_negative_enum_conditions!(value_method_names) if scopes
|
274
280
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
281
|
+
if validate
|
282
|
+
validate = {} unless Hash === validate
|
283
|
+
validates_inclusion_of name, in: enum_values.keys, **validate
|
284
|
+
end
|
279
285
|
|
280
|
-
|
281
|
-
|
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
|
@@ -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
|
-
|
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
|
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,
|
367
|
+
name, new_name, = args
|
368
368
|
|
369
|
-
|
370
|
-
|
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, [
|
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
|
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)
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
1981
|
-
|
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.
|
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
|
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
|
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
|
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
|
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-
|
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
|
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
|
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
|
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
|
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
|
479
|
-
documentation_uri: https://api.rubyonrails.org/v8.0.0
|
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
|
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.
|
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).
|