activerecord 8.0.3 → 8.1.0.beta1
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 +4 -4
- data/CHANGELOG.md +427 -522
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +382 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -18
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +66 -14
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -23
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +10 -5
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -30
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/core.rb +5 -4
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +50 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +24 -8
- data/lib/active_record/errors.rb +23 -7
- data/lib/active_record/explain_registry.rb +0 -1
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +1 -5
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +26 -16
- data/lib/active_record/model_schema.rb +10 -7
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +3 -7
- data/lib/active_record/railtie.rb +32 -3
- data/lib/active_record/railties/databases.rake +16 -4
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +42 -3
- data/lib/active_record/relation/batches.rb +26 -12
- data/lib/active_record/relation/calculations.rb +20 -9
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +27 -11
- data/lib/active_record/relation/merger.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +39 -29
- data/lib/active_record/relation/where_clause.rb +1 -10
- data/lib/active_record/relation.rb +25 -13
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +2 -21
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +10 -2
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +32 -10
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +65 -3
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- metadata +13 -9
- data/lib/active_record/normalization.rb +0 -163
@@ -39,6 +39,8 @@ module ActiveRecord
|
|
39
39
|
autoload :AssociationScope
|
40
40
|
autoload :DisableJoinsAssociationScope
|
41
41
|
autoload :AliasTracker
|
42
|
+
|
43
|
+
autoload :Deprecation
|
42
44
|
end
|
43
45
|
|
44
46
|
def self.eager_load!
|
@@ -79,7 +81,7 @@ module ActiveRecord
|
|
79
81
|
|
80
82
|
# Returns the specified association instance if it exists, +nil+ otherwise.
|
81
83
|
def association_instance_get(name)
|
82
|
-
@association_cache[name]
|
84
|
+
(@association_cache ||= {})[name]
|
83
85
|
end
|
84
86
|
|
85
87
|
# Set the specified association instance.
|
@@ -87,6 +89,14 @@ module ActiveRecord
|
|
87
89
|
@association_cache[name] = association
|
88
90
|
end
|
89
91
|
|
92
|
+
def deprecated_associations_api_guard(association, method_name)
|
93
|
+
Deprecation.guard(association.reflection) { "the method #{method_name} was invoked" }
|
94
|
+
end
|
95
|
+
|
96
|
+
def report_deprecated_association(reflection, context:)
|
97
|
+
Deprecation.report(reflection, context: context)
|
98
|
+
end
|
99
|
+
|
90
100
|
# = Active Record \Associations
|
91
101
|
#
|
92
102
|
# \Associations are a set of macro-like class methods for tying objects together through
|
@@ -1020,6 +1030,116 @@ module ActiveRecord
|
|
1020
1030
|
# associated records themselves, you can always do something along the lines of
|
1021
1031
|
# <tt>person.tasks.each(&:destroy)</tt>.
|
1022
1032
|
#
|
1033
|
+
# == Deprecated Associations
|
1034
|
+
#
|
1035
|
+
# Associations can be marked as deprecated by passing <tt>deprecated: true</tt>:
|
1036
|
+
#
|
1037
|
+
# has_many :posts, deprecated: true
|
1038
|
+
#
|
1039
|
+
# When a deprecated association is used, a warning is issued using the
|
1040
|
+
# Active Record logger, though more options are available via
|
1041
|
+
# configuration.
|
1042
|
+
#
|
1043
|
+
# The message includes some context that helps understand the deprecated
|
1044
|
+
# usage:
|
1045
|
+
#
|
1046
|
+
# The association Author#posts is deprecated, the method post_ids was invoked (...)
|
1047
|
+
# The association Author#posts is deprecated, referenced in query to preload records (...)
|
1048
|
+
#
|
1049
|
+
# The dots in the examples above would have the application-level spot
|
1050
|
+
# where usage occurred, to help locate what triggered the warning. That
|
1051
|
+
# location is computed using the Active Record backtrace cleaner.
|
1052
|
+
#
|
1053
|
+
# === What is considered to be usage?
|
1054
|
+
#
|
1055
|
+
# * Invocation of any association methods like +posts+, <tt>posts=</tt>,
|
1056
|
+
# etc.
|
1057
|
+
#
|
1058
|
+
# * If the association accepts nested attributes, assignment to those
|
1059
|
+
# attributes.
|
1060
|
+
#
|
1061
|
+
# * If the association is a through association and some of its nested
|
1062
|
+
# associations are deprecated, you'll get warnings for them whenever the
|
1063
|
+
# top-level through is used. This is so regardless of whether the
|
1064
|
+
# through itself is deprecated.
|
1065
|
+
#
|
1066
|
+
# * Execution of queries that refer to the association. Think execution of
|
1067
|
+
# <tt>eager_load(:posts)</tt>, <tt>joins(author: :posts)</tt>, etc.
|
1068
|
+
#
|
1069
|
+
# * If the association has a +:dependent+ option, destroying the
|
1070
|
+
# associated record issues warnings (because that has a side-effect that
|
1071
|
+
# would not happen if the association was removed).
|
1072
|
+
#
|
1073
|
+
# * If the association has a +:touch+ option, saving or destroying the
|
1074
|
+
# record issues a warning (because that has a side-effect that would not
|
1075
|
+
# happen if the association was removed).
|
1076
|
+
#
|
1077
|
+
# === Things that do NOT issue warnings
|
1078
|
+
#
|
1079
|
+
# The rationale behind most of the following edge cases is that Active
|
1080
|
+
# Record accesses associations lazily, when used. Before that, the
|
1081
|
+
# reference to the association is basically just a Ruby symbol.
|
1082
|
+
#
|
1083
|
+
# * If +posts+ is deprecated, <tt>has_many :comments, through: :posts</tt>
|
1084
|
+
# does not warn. Usage of the +comments+ association reports usage of
|
1085
|
+
# +posts+, as we explained above, but the definition of the +has_many+
|
1086
|
+
# itself does not.
|
1087
|
+
#
|
1088
|
+
# * Similarly, <tt>accepts_nested_attributes_for :posts</tt> does not
|
1089
|
+
# warn. Assignment to the posts attributes warns, as explained above,
|
1090
|
+
# but the +accepts_nested_attributes_for+ call itself does not.
|
1091
|
+
#
|
1092
|
+
# * Same if an association declares to be inverse of a deprecated one, the
|
1093
|
+
# macro itself does not warn.
|
1094
|
+
#
|
1095
|
+
# * In the same line, the declaration <tt>validates_associated :posts</tt>
|
1096
|
+
# does not warn by itself, though access is reported when the validation
|
1097
|
+
# runs.
|
1098
|
+
#
|
1099
|
+
# * Relation query methods like <tt>Author.includes(:posts)</tt> do not
|
1100
|
+
# warn by themselves. At that point, that is a relation that internally
|
1101
|
+
# stores a symbol for later use. As explained in the previous section,
|
1102
|
+
# you get a warning when/if the query is executed.
|
1103
|
+
#
|
1104
|
+
# * Access to the reflection object of the association as in
|
1105
|
+
# <tt>Author.reflect_on_association(:posts)</tt> or
|
1106
|
+
# <tt>Author.reflect_on_all_associations</tt> does not warn.
|
1107
|
+
#
|
1108
|
+
# === Configuration
|
1109
|
+
#
|
1110
|
+
# Reporting deprecated usage can be configured:
|
1111
|
+
#
|
1112
|
+
# config.active_record.deprecated_associations_options = { ... }
|
1113
|
+
#
|
1114
|
+
# If present, this has to be a hash with keys +:mode+ and/or +:backtrace+.
|
1115
|
+
#
|
1116
|
+
# ==== Mode
|
1117
|
+
#
|
1118
|
+
# * In +:warn+ mode, usage issues a warning that includes the
|
1119
|
+
# application-level place where the access happened, if any. This is the
|
1120
|
+
# default mode.
|
1121
|
+
#
|
1122
|
+
# * In +:raise+ mode, usage raises an
|
1123
|
+
# ActiveRecord::DeprecatedAssociationError with a similar message and a
|
1124
|
+
# clean backtrace in the exception object.
|
1125
|
+
#
|
1126
|
+
# * In +:notify+ mode, a <tt>deprecated_association.active_record</tt>
|
1127
|
+
# Active Support notification is published. The event payload has the
|
1128
|
+
# association reflection (+:reflection+), the application-level location
|
1129
|
+
# (+:location+) where the access happened (a Thread::Backtrace::Location
|
1130
|
+
# object, or +nil+), and a deprecation message (+:message+).
|
1131
|
+
#
|
1132
|
+
# ==== Backtrace
|
1133
|
+
#
|
1134
|
+
# If +:backtrace+ is true, warnings include a clean backtrace in the message
|
1135
|
+
# and notifications have a +:backtrace+ key in the payload with an array
|
1136
|
+
# of clean Thread::Backtrace::Location objects. Exceptions always get a
|
1137
|
+
# clean stack trace set.
|
1138
|
+
#
|
1139
|
+
# Clean backtraces are computed using the Active Record backtrace cleaner.
|
1140
|
+
# In Rails applications, that is by the default the same as
|
1141
|
+
# <tt>Rails.backtrace_cleaner</tt>.
|
1142
|
+
#
|
1023
1143
|
# == Type safety with ActiveRecord::AssociationTypeMismatch
|
1024
1144
|
#
|
1025
1145
|
# If you attempt to assign an object to an association that doesn't match the inferred
|
@@ -1208,8 +1328,10 @@ module ActiveRecord
|
|
1208
1328
|
# [+:as+]
|
1209
1329
|
# Specifies a polymorphic interface (See #belongs_to).
|
1210
1330
|
# [+:through+]
|
1211
|
-
# Specifies an association through which to perform the query.
|
1212
|
-
#
|
1331
|
+
# Specifies an association through which to perform the query.
|
1332
|
+
#
|
1333
|
+
# This can be any other type of association, including other <tt>:through</tt> associations,
|
1334
|
+
# but it cannot be a polymorphic association. Options for <tt>:class_name</tt>,
|
1213
1335
|
# <tt>:primary_key</tt> and <tt>:foreign_key</tt> are ignored, as the association uses the
|
1214
1336
|
# source reflection.
|
1215
1337
|
#
|
@@ -1285,6 +1407,9 @@ module ActiveRecord
|
|
1285
1407
|
# Defines an {association callback}[rdoc-ref:Associations::ClassMethods@Association+callbacks] that gets triggered <b>before an object is removed</b> from the association collection.
|
1286
1408
|
# [:after_remove]
|
1287
1409
|
# Defines an {association callback}[rdoc-ref:Associations::ClassMethods@Association+callbacks] that gets triggered <b>after an object is removed</b> from the association collection.
|
1410
|
+
# [+:deprecated+]
|
1411
|
+
# If true, marks the association as deprecated. Usage of deprecated associations is reported.
|
1412
|
+
# Please, check the class documentation above for details.
|
1288
1413
|
#
|
1289
1414
|
# Option examples:
|
1290
1415
|
# has_many :comments, -> { order("posted_on") }
|
@@ -1301,7 +1426,7 @@ module ActiveRecord
|
|
1301
1426
|
# has_many :comments, index_errors: :nested_attributes_order
|
1302
1427
|
def has_many(name, scope = nil, **options, &extension)
|
1303
1428
|
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
|
1304
|
-
Reflection.add_reflection
|
1429
|
+
Reflection.add_reflection(self, name, reflection)
|
1305
1430
|
end
|
1306
1431
|
|
1307
1432
|
# Specifies a one-to-one association with another class. This method
|
@@ -1411,10 +1536,12 @@ module ActiveRecord
|
|
1411
1536
|
# [+:as+]
|
1412
1537
|
# Specifies a polymorphic interface (See #belongs_to).
|
1413
1538
|
# [+:through+]
|
1414
|
-
# Specifies
|
1415
|
-
#
|
1416
|
-
#
|
1417
|
-
#
|
1539
|
+
# Specifies an association through which to perform the query.
|
1540
|
+
#
|
1541
|
+
# The through association must be a +has_one+, <tt>has_one :through</tt>, or non-polymorphic +belongs_to+.
|
1542
|
+
# That is, a non-polymorphic singular association. Options for <tt>:class_name</tt>, <tt>:primary_key</tt>,
|
1543
|
+
# and <tt>:foreign_key</tt> are ignored, as the association uses the source reflection. You can only
|
1544
|
+
# use a <tt>:through</tt> query through a #has_one or #belongs_to association on the join model.
|
1418
1545
|
#
|
1419
1546
|
# If the association on the join model is a #belongs_to, the collection can be modified
|
1420
1547
|
# and the records on the <tt>:through</tt> model will be automatically created and removed
|
@@ -1480,12 +1607,15 @@ module ActiveRecord
|
|
1480
1607
|
# Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
|
1481
1608
|
# This is an optional option. By default Rails will attempt to derive the value automatically.
|
1482
1609
|
# When the value is set the Array size must match associated model's primary key or +query_constraints+ size.
|
1610
|
+
# [+:deprecated+]
|
1611
|
+
# If true, marks the association as deprecated. Usage of deprecated associations is reported.
|
1612
|
+
# Please, check the class documentation above for details.
|
1483
1613
|
#
|
1484
1614
|
# Option examples:
|
1485
1615
|
# has_one :credit_card, dependent: :destroy # destroys the associated credit card
|
1486
1616
|
# has_one :credit_card, dependent: :nullify # updates the associated records foreign
|
1487
1617
|
# # key value to NULL rather than destroying it
|
1488
|
-
# has_one :last_comment, -> { order('posted_on') }, class_name: "Comment"
|
1618
|
+
# has_one :last_comment, -> { order('posted_on desc') }, class_name: "Comment"
|
1489
1619
|
# has_one :project_manager, -> { where(role: 'project_manager') }, class_name: "Person"
|
1490
1620
|
# has_one :attachment, as: :attachable
|
1491
1621
|
# has_one :boss, -> { readonly }
|
@@ -1497,7 +1627,7 @@ module ActiveRecord
|
|
1497
1627
|
# has_one :employment_record_book, query_constraints: [:organization_id, :employee_id]
|
1498
1628
|
def has_one(name, scope = nil, **options)
|
1499
1629
|
reflection = Builder::HasOne.build(self, name, scope, options)
|
1500
|
-
Reflection.add_reflection
|
1630
|
+
Reflection.add_reflection(self, name, reflection)
|
1501
1631
|
end
|
1502
1632
|
|
1503
1633
|
# Specifies a one-to-one association with another class. This method
|
@@ -1576,7 +1706,9 @@ module ActiveRecord
|
|
1576
1706
|
# [+:class_name+]
|
1577
1707
|
# Specify the class name of the association. Use it only if that name can't be inferred
|
1578
1708
|
# from the association name. So <tt>belongs_to :author</tt> will by default be linked to the Author class, but
|
1579
|
-
# if the real class name is Person, you'll have to specify it with this option.
|
1709
|
+
# if the real class name is Person, you'll have to specify it with this option. +:class_name+
|
1710
|
+
# is not supported in polymorphic associations, since in that case the class name of the
|
1711
|
+
# associated record is stored in the type column.
|
1580
1712
|
# [+:foreign_key+]
|
1581
1713
|
# Specify the foreign key used for the association. By default this is guessed to be the name
|
1582
1714
|
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
|
@@ -1670,6 +1802,9 @@ module ActiveRecord
|
|
1670
1802
|
# Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
|
1671
1803
|
# This is an optional option. By default Rails will attempt to derive the value automatically.
|
1672
1804
|
# When the value is set the Array size must match associated model's primary key or +query_constraints+ size.
|
1805
|
+
# [+:deprecated+]
|
1806
|
+
# If true, marks the association as deprecated. Usage of deprecated associations is reported.
|
1807
|
+
# Please, check the class documentation above for details.
|
1673
1808
|
#
|
1674
1809
|
# Option examples:
|
1675
1810
|
# belongs_to :firm, foreign_key: "client_of"
|
@@ -1688,7 +1823,7 @@ module ActiveRecord
|
|
1688
1823
|
# belongs_to :note, query_constraints: [:organization_id, :note_id]
|
1689
1824
|
def belongs_to(name, scope = nil, **options)
|
1690
1825
|
reflection = Builder::BelongsTo.build(self, name, scope, options)
|
1691
|
-
Reflection.add_reflection
|
1826
|
+
Reflection.add_reflection(self, name, reflection)
|
1692
1827
|
end
|
1693
1828
|
|
1694
1829
|
# Specifies a many-to-many relationship with another class. This associates two classes via an
|
@@ -1708,7 +1843,7 @@ module ActiveRecord
|
|
1708
1843
|
# The join table should not have a primary key or a model associated with it. You must manually generate the
|
1709
1844
|
# join table with a migration such as this:
|
1710
1845
|
#
|
1711
|
-
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[8.
|
1846
|
+
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[8.1]
|
1712
1847
|
# def change
|
1713
1848
|
# create_join_table :developers, :projects
|
1714
1849
|
# end
|
@@ -1859,6 +1994,9 @@ module ActiveRecord
|
|
1859
1994
|
# <tt>:autosave</tt> to <tt>true</tt>.
|
1860
1995
|
# [+:strict_loading+]
|
1861
1996
|
# Enforces strict loading every time an associated record is loaded through this association.
|
1997
|
+
# [+:deprecated+]
|
1998
|
+
# If true, marks the association as deprecated. Usage of deprecated associations is reported.
|
1999
|
+
# Please, check the class documentation above for details.
|
1862
2000
|
#
|
1863
2001
|
# Option examples:
|
1864
2002
|
# has_and_belongs_to_many :projects
|
@@ -1870,17 +2008,17 @@ module ActiveRecord
|
|
1870
2008
|
def has_and_belongs_to_many(name, scope = nil, **options, &extension)
|
1871
2009
|
habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
|
1872
2010
|
|
1873
|
-
builder = Builder::HasAndBelongsToMany.new
|
2011
|
+
builder = Builder::HasAndBelongsToMany.new(name, self, options)
|
1874
2012
|
|
1875
2013
|
join_model = builder.through_model
|
1876
2014
|
|
1877
|
-
const_set
|
1878
|
-
private_constant
|
2015
|
+
const_set(join_model.name, join_model)
|
2016
|
+
private_constant(join_model.name)
|
1879
2017
|
|
1880
|
-
middle_reflection = builder.middle_reflection
|
2018
|
+
middle_reflection = builder.middle_reflection(join_model)
|
1881
2019
|
|
1882
|
-
Builder::HasMany.define_callbacks
|
1883
|
-
Reflection.add_reflection
|
2020
|
+
Builder::HasMany.define_callbacks(self, middle_reflection)
|
2021
|
+
Reflection.add_reflection(self, middle_reflection.name, middle_reflection)
|
1884
2022
|
middle_reflection.parent_reflection = habtm_reflection
|
1885
2023
|
|
1886
2024
|
include Module.new {
|
@@ -1897,8 +2035,8 @@ module ActiveRecord
|
|
1897
2035
|
hm_options[:through] = middle_reflection.name
|
1898
2036
|
hm_options[:source] = join_model.right_reflection.name
|
1899
2037
|
|
1900
|
-
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading].each do |k|
|
1901
|
-
hm_options[k] = options[k] if options.key?
|
2038
|
+
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading, :deprecated].each do |k|
|
2039
|
+
hm_options[k] = options[k] if options.key?(k)
|
1902
2040
|
end
|
1903
2041
|
|
1904
2042
|
has_many name, scope, **hm_options, &extension
|
@@ -51,6 +51,16 @@ module ActiveRecord
|
|
51
51
|
# ActiveRecord::SerializationTypeMismatch error.
|
52
52
|
# * If the column is +NULL+ or starting from a new record, the default value
|
53
53
|
# will set to +type.new+
|
54
|
+
# * +comparable+ - Specify whether the deserialized object is safely comparable
|
55
|
+
# for the purpose of detecting changes. Defaults to +false+
|
56
|
+
# When set to +false+ the old and new values will be compared by their serialized
|
57
|
+
# representation (e.g. JSON or YAML), which can sometimes cause two objects that are
|
58
|
+
# semantically equal to be considered different.
|
59
|
+
# For instance two hashes with the same keys and values but a different order have a
|
60
|
+
# different serialized representation, but are semantically equal once deserialized.
|
61
|
+
# If set to +true+ the comparison will be done on the deserialized object.
|
62
|
+
# This options should only be enabled if the +type+ is known to have
|
63
|
+
# a proper <tt>==</tt> method that deeply compare the objects.
|
54
64
|
# * +yaml+ - Optional. Yaml specific options. The allowed config is:
|
55
65
|
# * +:permitted_classes+ - +Array+ with the permitted classes.
|
56
66
|
# * +:unsafe_load+ - Unsafely load YAML blobs, allow YAML to load any class.
|
@@ -180,7 +190,7 @@ module ActiveRecord
|
|
180
190
|
# serialize :preferences, coder: Rot13JSON
|
181
191
|
# end
|
182
192
|
#
|
183
|
-
def serialize(attr_name, coder: nil, type: Object, yaml: {}, **options)
|
193
|
+
def serialize(attr_name, coder: nil, type: Object, comparable: false, yaml: {}, **options)
|
184
194
|
coder ||= default_column_serializer
|
185
195
|
unless coder
|
186
196
|
raise ArgumentError, <<~MSG.squish
|
@@ -200,7 +210,7 @@ module ActiveRecord
|
|
200
210
|
end
|
201
211
|
|
202
212
|
cast_type = cast_type.subtype if Type::Serialized === cast_type
|
203
|
-
Type::Serialized.new(cast_type, column_serializer)
|
213
|
+
Type::Serialized.new(cast_type, column_serializer, comparable: comparable)
|
204
214
|
end
|
205
215
|
end
|
206
216
|
|
@@ -209,7 +219,10 @@ module ActiveRecord
|
|
209
219
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
210
220
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
211
221
|
# using the #as_json hook.
|
212
|
-
|
222
|
+
|
223
|
+
if coder == ::JSON || coder == Coders::JSON
|
224
|
+
coder = Coders::JSON.new
|
225
|
+
end
|
213
226
|
|
214
227
|
if coder == ::YAML || coder == Coders::YAMLColumn
|
215
228
|
Coders::YAMLColumn.new(attr_name, type, **(yaml || {}))
|
@@ -113,7 +113,7 @@ module ActiveRecord
|
|
113
113
|
unless abstract_class?
|
114
114
|
load_schema
|
115
115
|
super(attribute_names)
|
116
|
-
alias_attribute :id_value, :id if _has_attribute?("id")
|
116
|
+
alias_attribute :id_value, :id if _has_attribute?("id")
|
117
117
|
end
|
118
118
|
|
119
119
|
generate_alias_attributes
|
@@ -7,6 +7,7 @@ module ActiveRecord
|
|
7
7
|
module Attributes
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
include ActiveModel::AttributeRegistration
|
10
|
+
include ActiveModel::Attributes::Normalization
|
10
11
|
|
11
12
|
# = Active Record \Attributes
|
12
13
|
module ClassMethods
|
@@ -251,6 +252,7 @@ module ActiveRecord
|
|
251
252
|
|
252
253
|
def _default_attributes # :nodoc:
|
253
254
|
@default_attributes ||= begin
|
255
|
+
# TODO: Remove the need for a connection after we release 8.1.
|
254
256
|
attributes_hash = with_connection do |connection|
|
255
257
|
columns_hash.transform_values do |column|
|
256
258
|
ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
|
@@ -310,6 +312,7 @@ module ActiveRecord
|
|
310
312
|
end
|
311
313
|
|
312
314
|
def type_for_column(connection, column)
|
315
|
+
# TODO: Remove the need for a connection after we release 8.1.
|
313
316
|
hook_attribute_type(column.name, super)
|
314
317
|
end
|
315
318
|
end
|
@@ -527,7 +527,7 @@ module ActiveRecord
|
|
527
527
|
return false unless reflection.inverse_of&.polymorphic?
|
528
528
|
|
529
529
|
class_name = record._read_attribute(reflection.inverse_of.foreign_type)
|
530
|
-
reflection.active_record
|
530
|
+
reflection.active_record != record.class.polymorphic_class_for(class_name)
|
531
531
|
end
|
532
532
|
|
533
533
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
data/lib/active_record/base.rb
CHANGED
@@ -256,13 +256,13 @@ module ActiveRecord # :nodoc:
|
|
256
256
|
# * AssociationTypeMismatch - The object assigned to the association wasn't of the type
|
257
257
|
# specified in the association definition.
|
258
258
|
# * AttributeAssignmentError - An error occurred while doing a mass assignment through the
|
259
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:
|
259
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
260
260
|
# You can inspect the +attribute+ property of the exception object to determine which attribute
|
261
261
|
# triggered the error.
|
262
262
|
# * ConnectionNotEstablished - No connection has been established.
|
263
263
|
# Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
|
264
264
|
# * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
|
265
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:
|
265
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
266
266
|
# The +errors+ property of this exception contains an array of
|
267
267
|
# AttributeAssignmentError
|
268
268
|
# objects that should be inspected to determine which attributes triggered the errors.
|
@@ -328,7 +328,6 @@ module ActiveRecord # :nodoc:
|
|
328
328
|
include TokenFor
|
329
329
|
include SignedId
|
330
330
|
include Suppressor
|
331
|
-
include Normalization
|
332
331
|
include Marshalling::Methods
|
333
332
|
|
334
333
|
self.param_delimiter = "_"
|
@@ -1,14 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/json"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Coders # :nodoc:
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
class JSON # :nodoc:
|
8
|
+
DEFAULT_OPTIONS = { escape: false }.freeze
|
9
|
+
|
10
|
+
def initialize(options = nil)
|
11
|
+
@options = options ? DEFAULT_OPTIONS.merge(options) : DEFAULT_OPTIONS
|
12
|
+
@encoder = ActiveSupport::JSON::Encoding.json_encoder.new(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump(obj)
|
16
|
+
@encoder.encode(obj)
|
8
17
|
end
|
9
18
|
|
10
|
-
def
|
11
|
-
ActiveSupport::JSON.decode(json) unless json.blank?
|
19
|
+
def load(json)
|
20
|
+
ActiveSupport::JSON.decode(json, @options) unless json.blank?
|
12
21
|
end
|
13
22
|
end
|
14
23
|
end
|
@@ -158,9 +158,7 @@ module ActiveRecord
|
|
158
158
|
each_connection_pool(role).any?(&:active_connection?)
|
159
159
|
end
|
160
160
|
|
161
|
-
# Returns any connections in use by the current thread back to the pool
|
162
|
-
# and also returns connections to the pool cached by threads that are no
|
163
|
-
# longer alive.
|
161
|
+
# Returns any connections in use by the current thread back to the pool.
|
164
162
|
def clear_active_connections!(role = nil)
|
165
163
|
each_connection_pool(role).each do |pool|
|
166
164
|
pool.release_connection
|
@@ -40,6 +40,14 @@ module ActiveRecord
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
# Add +element+ to the back of the queue. Never blocks.
|
44
|
+
def add_back(element)
|
45
|
+
synchronize do
|
46
|
+
@queue.unshift element
|
47
|
+
@cond.signal
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
43
51
|
# If +element+ is in the queue, remove and return it, or +nil+.
|
44
52
|
def delete(element)
|
45
53
|
synchronize do
|
@@ -54,6 +62,13 @@ module ActiveRecord
|
|
54
62
|
end
|
55
63
|
end
|
56
64
|
|
65
|
+
# Number of elements in the queue.
|
66
|
+
def size
|
67
|
+
synchronize do
|
68
|
+
@queue.size
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
57
72
|
# Remove the head of the queue.
|
58
73
|
#
|
59
74
|
# If +timeout+ is not given, remove and return the head of the
|
@@ -7,12 +7,29 @@ module ActiveRecord
|
|
7
7
|
class ConnectionPool
|
8
8
|
# = Active Record Connection Pool \Reaper
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# the connection pool.
|
10
|
+
# The reaper is a singleton that exists in the background of the process
|
11
|
+
# and is responsible for general maintenance of all the connection pools.
|
13
12
|
#
|
14
|
-
#
|
15
|
-
#
|
13
|
+
# It will reclaim connections that are leased to now-dead threads,
|
14
|
+
# ensuring that a bad thread can't leak a pool slot forever. By definition,
|
15
|
+
# this involves touching currently-leased connections, but that is safe
|
16
|
+
# because the owning thread is known to be dead.
|
17
|
+
#
|
18
|
+
# Beyond that, it manages the health of available / unleased connections:
|
19
|
+
# * retiring connections that have been idle[1] for too long
|
20
|
+
# * creating occasional activity on inactive[1] connections
|
21
|
+
# * keeping the pool prepopulated up to its minimum size
|
22
|
+
# * proactively connecting to the target database from any pooled
|
23
|
+
# connections that had lazily deferred that step
|
24
|
+
# * resetting or replacing connections that are known to be broken
|
25
|
+
#
|
26
|
+
#
|
27
|
+
# [1]: "idle" and "inactive" here distinguish between connections that
|
28
|
+
# have not been requested by the application in a while (idle) and those
|
29
|
+
# that have not spoken to their remote server in a while (inactive). The
|
30
|
+
# former is a desirable opportunity to reduce our connection count
|
31
|
+
# (`idle_timeout`); the latter is a risk that the server or a firewall may
|
32
|
+
# drop a connection we still anticipate using (avoided by `keepalive`).
|
16
33
|
class Reaper
|
17
34
|
attr_reader :pool, :frequency
|
18
35
|
|
@@ -36,6 +53,15 @@ module ActiveRecord
|
|
36
53
|
end
|
37
54
|
end
|
38
55
|
|
56
|
+
def pools(refs = nil) # :nodoc:
|
57
|
+
refs ||= @mutex.synchronize { @pools.values.flatten(1) }
|
58
|
+
|
59
|
+
refs.filter_map do |ref|
|
60
|
+
ref.__getobj__ if ref.weakref_alive?
|
61
|
+
rescue WeakRef::RefError
|
62
|
+
end.select(&:maintainable?)
|
63
|
+
end
|
64
|
+
|
39
65
|
private
|
40
66
|
def spawn_thread(frequency)
|
41
67
|
Thread.new(frequency) do |t|
|
@@ -46,23 +72,36 @@ module ActiveRecord
|
|
46
72
|
running = true
|
47
73
|
while running
|
48
74
|
sleep t
|
75
|
+
|
76
|
+
refs = nil
|
77
|
+
|
49
78
|
@mutex.synchronize do
|
50
|
-
@pools[frequency]
|
51
|
-
pool.weakref_alive? && !pool.discarded?
|
52
|
-
end
|
79
|
+
refs = @pools[frequency]
|
53
80
|
|
54
|
-
|
55
|
-
|
56
|
-
p.flush
|
81
|
+
refs.select! do |pool|
|
82
|
+
pool.weakref_alive? && !pool.discarded?
|
57
83
|
rescue WeakRef::RefError
|
58
84
|
end
|
59
85
|
|
60
|
-
if
|
86
|
+
if refs.empty?
|
61
87
|
@pools.delete(frequency)
|
62
88
|
@threads.delete(frequency)
|
63
89
|
running = false
|
64
90
|
end
|
65
91
|
end
|
92
|
+
|
93
|
+
if running
|
94
|
+
pools(refs).each do |pool|
|
95
|
+
pool.reaper_lock do
|
96
|
+
pool.reap
|
97
|
+
pool.flush
|
98
|
+
pool.prepopulate
|
99
|
+
pool.retire_old_connections
|
100
|
+
pool.keep_alive
|
101
|
+
pool.preconnect
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
66
105
|
end
|
67
106
|
end
|
68
107
|
end
|