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.
Files changed (148) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +427 -522
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/builder/association.rb +16 -5
  7. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  8. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  9. data/lib/active_record/associations/builder/has_one.rb +1 -1
  10. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  11. data/lib/active_record/associations/collection_proxy.rb +22 -4
  12. data/lib/active_record/associations/deprecation.rb +88 -0
  13. data/lib/active_record/associations/errors.rb +3 -0
  14. data/lib/active_record/associations/join_dependency.rb +2 -0
  15. data/lib/active_record/associations/preloader/branch.rb +1 -0
  16. data/lib/active_record/associations.rb +159 -21
  17. data/lib/active_record/attribute_methods/serialization.rb +16 -3
  18. data/lib/active_record/attribute_methods.rb +1 -1
  19. data/lib/active_record/attributes.rb +3 -0
  20. data/lib/active_record/autosave_association.rb +1 -1
  21. data/lib/active_record/base.rb +2 -3
  22. data/lib/active_record/coders/json.rb +14 -5
  23. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
  24. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
  25. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +382 -51
  27. data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
  28. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -18
  29. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  30. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
  32. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  33. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -22
  34. data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
  35. data/lib/active_record/connection_adapters/abstract_adapter.rb +66 -14
  36. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
  37. data/lib/active_record/connection_adapters/column.rb +17 -4
  38. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  39. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  40. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  41. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
  42. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
  43. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  44. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -23
  45. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  46. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  47. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  48. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
  49. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
  50. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
  51. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
  52. data/lib/active_record/connection_adapters/postgresql_adapter.rb +10 -5
  53. data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
  54. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
  55. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  56. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
  57. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -30
  58. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
  59. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
  60. data/lib/active_record/connection_adapters.rb +1 -0
  61. data/lib/active_record/core.rb +5 -4
  62. data/lib/active_record/counter_cache.rb +33 -8
  63. data/lib/active_record/database_configurations/database_config.rb +5 -1
  64. data/lib/active_record/database_configurations/hash_config.rb +50 -9
  65. data/lib/active_record/database_configurations/url_config.rb +13 -3
  66. data/lib/active_record/database_configurations.rb +7 -3
  67. data/lib/active_record/delegated_type.rb +1 -1
  68. data/lib/active_record/dynamic_matchers.rb +54 -69
  69. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  70. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  71. data/lib/active_record/encryption/scheme.rb +1 -1
  72. data/lib/active_record/enum.rb +24 -8
  73. data/lib/active_record/errors.rb +23 -7
  74. data/lib/active_record/explain_registry.rb +0 -1
  75. data/lib/active_record/filter_attribute_handler.rb +73 -0
  76. data/lib/active_record/fixtures.rb +2 -2
  77. data/lib/active_record/gem_version.rb +3 -3
  78. data/lib/active_record/inheritance.rb +1 -1
  79. data/lib/active_record/insert_all.rb +12 -7
  80. data/lib/active_record/locking/optimistic.rb +7 -0
  81. data/lib/active_record/locking/pessimistic.rb +5 -0
  82. data/lib/active_record/log_subscriber.rb +1 -5
  83. data/lib/active_record/middleware/shard_selector.rb +34 -17
  84. data/lib/active_record/migration/command_recorder.rb +14 -1
  85. data/lib/active_record/migration/compatibility.rb +34 -24
  86. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  87. data/lib/active_record/migration.rb +26 -16
  88. data/lib/active_record/model_schema.rb +10 -7
  89. data/lib/active_record/nested_attributes.rb +2 -0
  90. data/lib/active_record/persistence.rb +34 -3
  91. data/lib/active_record/query_cache.rb +22 -15
  92. data/lib/active_record/query_logs.rb +3 -7
  93. data/lib/active_record/railtie.rb +32 -3
  94. data/lib/active_record/railties/databases.rake +16 -4
  95. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  96. data/lib/active_record/railties/job_runtime.rb +10 -11
  97. data/lib/active_record/reflection.rb +42 -3
  98. data/lib/active_record/relation/batches.rb +26 -12
  99. data/lib/active_record/relation/calculations.rb +20 -9
  100. data/lib/active_record/relation/delegation.rb +0 -1
  101. data/lib/active_record/relation/finder_methods.rb +27 -11
  102. data/lib/active_record/relation/merger.rb +2 -2
  103. data/lib/active_record/relation/predicate_builder.rb +2 -2
  104. data/lib/active_record/relation/query_attribute.rb +3 -1
  105. data/lib/active_record/relation/query_methods.rb +39 -29
  106. data/lib/active_record/relation/where_clause.rb +1 -10
  107. data/lib/active_record/relation.rb +25 -13
  108. data/lib/active_record/result.rb +44 -21
  109. data/lib/active_record/sanitization.rb +2 -0
  110. data/lib/active_record/schema_dumper.rb +12 -10
  111. data/lib/active_record/scoping.rb +0 -1
  112. data/lib/active_record/signed_id.rb +43 -15
  113. data/lib/active_record/statement_cache.rb +13 -9
  114. data/lib/active_record/store.rb +44 -19
  115. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  116. data/lib/active_record/tasks/database_tasks.rb +2 -21
  117. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
  118. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
  119. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  120. data/lib/active_record/test_databases.rb +10 -2
  121. data/lib/active_record/test_fixtures.rb +27 -2
  122. data/lib/active_record/testing/query_assertions.rb +8 -2
  123. data/lib/active_record/timestamp.rb +4 -2
  124. data/lib/active_record/transaction.rb +2 -5
  125. data/lib/active_record/transactions.rb +32 -10
  126. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  127. data/lib/active_record/type/internal/timezone.rb +7 -0
  128. data/lib/active_record/type/json.rb +15 -2
  129. data/lib/active_record/type/serialized.rb +11 -4
  130. data/lib/active_record/type/type_map.rb +1 -1
  131. data/lib/active_record/type_caster/connection.rb +2 -1
  132. data/lib/active_record/validations/associated.rb +1 -1
  133. data/lib/active_record.rb +65 -3
  134. data/lib/arel/alias_predication.rb +2 -0
  135. data/lib/arel/crud.rb +6 -11
  136. data/lib/arel/nodes/count.rb +2 -2
  137. data/lib/arel/nodes/function.rb +4 -10
  138. data/lib/arel/nodes/named_function.rb +2 -2
  139. data/lib/arel/nodes/node.rb +1 -1
  140. data/lib/arel/nodes.rb +0 -2
  141. data/lib/arel/select_manager.rb +7 -2
  142. data/lib/arel/visitors/dot.rb +0 -3
  143. data/lib/arel/visitors/postgresql.rb +55 -0
  144. data/lib/arel/visitors/sqlite.rb +55 -8
  145. data/lib/arel/visitors/to_sql.rb +3 -21
  146. data/lib/arel.rb +3 -1
  147. metadata +13 -9
  148. 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. This can be any other type
1212
- # of association, including other <tt>:through</tt> associations. Options for <tt>:class_name</tt>,
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 self, name, 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 a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
1415
- # <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
1416
- # source reflection. You can only use a <tt>:through</tt> query through a #has_one
1417
- # or #belongs_to association on the join model.
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 self, name, 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 self, name, 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.0]
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 name, self, options
2011
+ builder = Builder::HasAndBelongsToMany.new(name, self, options)
1874
2012
 
1875
2013
  join_model = builder.through_model
1876
2014
 
1877
- const_set join_model.name, join_model
1878
- private_constant join_model.name
2015
+ const_set(join_model.name, join_model)
2016
+ private_constant(join_model.name)
1879
2017
 
1880
- middle_reflection = builder.middle_reflection join_model
2018
+ middle_reflection = builder.middle_reflection(join_model)
1881
2019
 
1882
- Builder::HasMany.define_callbacks self, middle_reflection
1883
- Reflection.add_reflection self, middle_reflection.name, middle_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? k
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
- coder = Coders::JSON if coder == ::JSON
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") && !_has_attribute?("id_value")
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.polymorphic_name != class_name
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.
@@ -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:ActiveModel::AttributeAssignment#attributes=] method.
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:ActiveModel::AttributeAssignment#attributes=] method.
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
- module JSON # :nodoc:
6
- def self.dump(obj)
7
- ActiveSupport::JSON.encode(obj)
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 self.load(json)
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
- # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
11
- # +pool+. A reaper instantiated with a zero frequency will never reap
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
- # Configure the frequency by setting +reaping_frequency+ in your database
15
- # YAML file (default 60 seconds).
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].select! do |pool|
51
- pool.weakref_alive? && !pool.discarded?
52
- end
79
+ refs = @pools[frequency]
53
80
 
54
- @pools[frequency].each do |p|
55
- p.reap
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 @pools[frequency].empty?
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