activerecord 8.0.2.1 → 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 (159) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +459 -421
  3. data/README.rdoc +2 -2
  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/belongs_to_association.rb +9 -1
  7. data/lib/active_record/associations/builder/association.rb +16 -5
  8. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  9. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  10. data/lib/active_record/associations/builder/has_one.rb +1 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  12. data/lib/active_record/associations/collection_association.rb +3 -3
  13. data/lib/active_record/associations/collection_proxy.rb +22 -4
  14. data/lib/active_record/associations/deprecation.rb +88 -0
  15. data/lib/active_record/associations/errors.rb +3 -0
  16. data/lib/active_record/associations/join_dependency.rb +2 -0
  17. data/lib/active_record/associations/preloader/branch.rb +1 -0
  18. data/lib/active_record/associations.rb +159 -21
  19. data/lib/active_record/attribute_methods/query.rb +34 -0
  20. data/lib/active_record/attribute_methods/serialization.rb +17 -4
  21. data/lib/active_record/attributes.rb +38 -24
  22. data/lib/active_record/base.rb +0 -1
  23. data/lib/active_record/coders/json.rb +14 -5
  24. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +2 -4
  25. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
  27. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +384 -49
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -1
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  31. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  32. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
  33. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  34. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +89 -23
  35. data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
  36. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -13
  37. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
  38. data/lib/active_record/connection_adapters/column.rb +17 -4
  39. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  40. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  41. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  42. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
  43. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
  44. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  45. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -16
  46. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  47. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  48. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  49. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
  50. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
  51. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
  52. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
  53. data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -7
  54. data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
  55. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -27
  56. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  57. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
  58. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +56 -32
  59. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
  60. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
  61. data/lib/active_record/connection_adapters.rb +1 -0
  62. data/lib/active_record/connection_handling.rb +1 -1
  63. data/lib/active_record/core.rb +12 -9
  64. data/lib/active_record/counter_cache.rb +33 -8
  65. data/lib/active_record/database_configurations/database_config.rb +5 -1
  66. data/lib/active_record/database_configurations/hash_config.rb +56 -9
  67. data/lib/active_record/database_configurations/url_config.rb +13 -3
  68. data/lib/active_record/database_configurations.rb +7 -3
  69. data/lib/active_record/delegated_type.rb +2 -2
  70. data/lib/active_record/dynamic_matchers.rb +54 -69
  71. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  72. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  73. data/lib/active_record/encryption/encryptor.rb +27 -25
  74. data/lib/active_record/encryption/scheme.rb +1 -1
  75. data/lib/active_record/enum.rb +37 -20
  76. data/lib/active_record/errors.rb +20 -4
  77. data/lib/active_record/explain_registry.rb +0 -1
  78. data/lib/active_record/filter_attribute_handler.rb +73 -0
  79. data/lib/active_record/fixture_set/table_row.rb +19 -2
  80. data/lib/active_record/fixtures.rb +2 -2
  81. data/lib/active_record/gem_version.rb +3 -3
  82. data/lib/active_record/inheritance.rb +1 -1
  83. data/lib/active_record/insert_all.rb +12 -7
  84. data/lib/active_record/locking/optimistic.rb +7 -0
  85. data/lib/active_record/locking/pessimistic.rb +5 -0
  86. data/lib/active_record/log_subscriber.rb +1 -5
  87. data/lib/active_record/middleware/shard_selector.rb +34 -17
  88. data/lib/active_record/migration/command_recorder.rb +14 -1
  89. data/lib/active_record/migration/compatibility.rb +34 -24
  90. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  91. data/lib/active_record/migration.rb +31 -21
  92. data/lib/active_record/model_schema.rb +10 -7
  93. data/lib/active_record/nested_attributes.rb +2 -0
  94. data/lib/active_record/persistence.rb +34 -3
  95. data/lib/active_record/query_cache.rb +22 -15
  96. data/lib/active_record/query_logs.rb +7 -7
  97. data/lib/active_record/querying.rb +4 -4
  98. data/lib/active_record/railtie.rb +34 -5
  99. data/lib/active_record/railties/databases.rake +23 -19
  100. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  101. data/lib/active_record/railties/job_runtime.rb +10 -11
  102. data/lib/active_record/reflection.rb +42 -3
  103. data/lib/active_record/relation/batches.rb +26 -12
  104. data/lib/active_record/relation/calculations.rb +35 -25
  105. data/lib/active_record/relation/delegation.rb +0 -1
  106. data/lib/active_record/relation/finder_methods.rb +37 -21
  107. data/lib/active_record/relation/merger.rb +2 -2
  108. data/lib/active_record/relation/predicate_builder.rb +2 -2
  109. data/lib/active_record/relation/query_attribute.rb +3 -1
  110. data/lib/active_record/relation/query_methods.rb +43 -33
  111. data/lib/active_record/relation/spawn_methods.rb +6 -6
  112. data/lib/active_record/relation/where_clause.rb +7 -10
  113. data/lib/active_record/relation.rb +37 -15
  114. data/lib/active_record/result.rb +44 -21
  115. data/lib/active_record/sanitization.rb +2 -0
  116. data/lib/active_record/schema_dumper.rb +12 -10
  117. data/lib/active_record/scoping.rb +0 -1
  118. data/lib/active_record/secure_token.rb +3 -3
  119. data/lib/active_record/signed_id.rb +46 -18
  120. data/lib/active_record/statement_cache.rb +13 -9
  121. data/lib/active_record/store.rb +44 -19
  122. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  123. data/lib/active_record/tasks/database_tasks.rb +24 -35
  124. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
  125. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  126. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  127. data/lib/active_record/test_databases.rb +11 -3
  128. data/lib/active_record/test_fixtures.rb +27 -2
  129. data/lib/active_record/testing/query_assertions.rb +8 -2
  130. data/lib/active_record/timestamp.rb +4 -2
  131. data/lib/active_record/transaction.rb +2 -5
  132. data/lib/active_record/transactions.rb +34 -10
  133. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  134. data/lib/active_record/type/internal/timezone.rb +7 -0
  135. data/lib/active_record/type/json.rb +15 -2
  136. data/lib/active_record/type/serialized.rb +11 -4
  137. data/lib/active_record/type/type_map.rb +1 -1
  138. data/lib/active_record/type_caster/connection.rb +2 -1
  139. data/lib/active_record/validations/associated.rb +1 -1
  140. data/lib/active_record.rb +68 -5
  141. data/lib/arel/alias_predication.rb +2 -0
  142. data/lib/arel/crud.rb +8 -11
  143. data/lib/arel/delete_manager.rb +5 -0
  144. data/lib/arel/nodes/count.rb +2 -2
  145. data/lib/arel/nodes/delete_statement.rb +4 -2
  146. data/lib/arel/nodes/function.rb +4 -10
  147. data/lib/arel/nodes/named_function.rb +2 -2
  148. data/lib/arel/nodes/node.rb +1 -1
  149. data/lib/arel/nodes/update_statement.rb +4 -2
  150. data/lib/arel/nodes.rb +0 -2
  151. data/lib/arel/select_manager.rb +13 -4
  152. data/lib/arel/update_manager.rb +5 -0
  153. data/lib/arel/visitors/dot.rb +2 -3
  154. data/lib/arel/visitors/postgresql.rb +55 -0
  155. data/lib/arel/visitors/sqlite.rb +55 -8
  156. data/lib/arel/visitors/to_sql.rb +5 -21
  157. data/lib/arel.rb +3 -1
  158. metadata +13 -9
  159. 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
@@ -3,6 +3,38 @@
3
3
  module ActiveRecord
4
4
  module AttributeMethods
5
5
  # = Active Record Attribute Methods \Query
6
+ #
7
+ # Adds query methods for attributes that return either +true+ or +false+
8
+ # depending on the attribute type and value.
9
+ #
10
+ # For Boolean attributes this will return +true+ if the value is present
11
+ # and return +false+ otherwise:
12
+ #
13
+ # class Product < ActiveRecord::Base
14
+ # end
15
+ #
16
+ # product = Product.new(archived: false)
17
+ # product.archived? # => false
18
+ # product.archived = true
19
+ # product.archived? # => true
20
+ #
21
+ # For Numeric attributes this will return +true+ if the value is a non-zero
22
+ # number and return +false+ otherwise:
23
+ #
24
+ # product.inventory_count = 0
25
+ # product.inventory_count? # => false
26
+ # product.inventory_count = 1
27
+ # product.inventory_count? # => true
28
+ #
29
+ # For other attributes it will return +true+ if the value is present
30
+ # and return +false+ otherwise:
31
+ #
32
+ # product.name = nil
33
+ # product.name? # => false
34
+ # product.name = " "
35
+ # product.name? # => false
36
+ # product.name = "Orange"
37
+ # product.name? # => true
6
38
  module Query
7
39
  extend ActiveSupport::Concern
8
40
 
@@ -10,6 +42,8 @@ module ActiveRecord
10
42
  attribute_method_suffix "?", parameters: false
11
43
  end
12
44
 
45
+ # Returns +true+ or +false+ for the attribute identified by +attr_name+,
46
+ # depending on the attribute type and value.
13
47
  def query_attribute(attr_name)
14
48
  value = self.public_send(attr_name)
15
49
 
@@ -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.
@@ -130,7 +140,7 @@ module ActiveRecord
130
140
  # silently cast unsupported types to +String+:
131
141
  #
132
142
  # >> JSON.parse(JSON.dump(Struct.new(:foo)))
133
- # => "#<Class:0x000000013090b4c0>"
143
+ # # => "#<Class:0x000000013090b4c0>"
134
144
  #
135
145
  # ==== Examples
136
146
  #
@@ -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 || {}))
@@ -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
@@ -21,28 +22,34 @@ module ActiveRecord
21
22
  # your domain objects across much of Active Record, without having to
22
23
  # rely on implementation details or monkey patching.
23
24
  #
24
- # +name+ The name of the methods to define attribute methods for, and the
25
- # column which this will persist to.
25
+ # ==== Parameters
26
26
  #
27
- # +cast_type+ A symbol such as +:string+ or +:integer+, or a type object
28
- # to be used for this attribute. If this parameter is not passed, the previously
29
- # defined type (if any) will be used.
30
- # Otherwise, the type will be ActiveModel::Type::Value.
31
- # See the examples below for more information about providing custom type objects.
27
+ # [+name+]
28
+ # The name of the methods to define attribute methods for, and the
29
+ # column which this will persist to.
32
30
  #
33
- # ==== Options
31
+ # [+cast_type+]
32
+ # A symbol such as +:string+ or +:integer+, or a type object to be used
33
+ # for this attribute. If this parameter is not passed, the previously
34
+ # defined type (if any) will be used. Otherwise, the type will be
35
+ # ActiveModel::Type::Value. See the examples below for more information
36
+ # about providing custom type objects.
34
37
  #
35
- # The following options are accepted:
38
+ # ==== Options
36
39
  #
37
- # +default+ The default value to use when no value is provided. If this option
38
- # is not passed, the previously defined default value (if any) on the superclass or in the schema will be used.
39
- # Otherwise, the default will be +nil+.
40
+ # [+:default+]
41
+ # The default value to use when no value is provided. If this option is
42
+ # not passed, the previously defined default value (if any) on the
43
+ # superclass or in the schema will be used. Otherwise, the default will
44
+ # be +nil+.
40
45
  #
41
- # +array+ (PostgreSQL only) specifies that the type should be an array (see the
42
- # examples below).
46
+ # [+:array+]
47
+ # (PostgreSQL only) Specifies that the type should be an array. See the
48
+ # examples below.
43
49
  #
44
- # +range+ (PostgreSQL only) specifies that the type should be a range (see the
45
- # examples below).
50
+ # [+:range+]
51
+ # (PostgreSQL only) Specifies that the type should be a range. See the
52
+ # examples below.
46
53
  #
47
54
  # When using a symbol for +cast_type+, extra options are forwarded to the
48
55
  # constructor of the type object.
@@ -217,17 +224,22 @@ module ActiveRecord
217
224
  # is provided so it can be used by plugin authors, application code
218
225
  # should probably use ClassMethods#attribute.
219
226
  #
220
- # +name+ The name of the attribute being defined. Expected to be a +String+.
227
+ # ==== Parameters
228
+ #
229
+ # [+name+]
230
+ # The name of the attribute being defined. Expected to be a +String+.
221
231
  #
222
- # +cast_type+ The type object to use for this attribute.
232
+ # [+cast_type+]
233
+ # The type object to use for this attribute.
223
234
  #
224
- # +default+ The default value to use when no value is provided. If this option
225
- # is not passed, the previous default value (if any) will be used.
226
- # Otherwise, the default will be +nil+. A proc can also be passed, and
227
- # will be called once each time a new value is needed.
235
+ # [+default+]
236
+ # The default value to use when no value is provided. If this option
237
+ # is not passed, the previous default value (if any) will be used.
238
+ # Otherwise, the default will be +nil+. A proc can also be passed, and
239
+ # will be called once each time a new value is needed.
228
240
  #
229
- # +user_provided_default+ Whether the default value should be cast using
230
- # +cast+ or +deserialize+.
241
+ # [+user_provided_default+]
242
+ # Whether the default value should be cast using +cast+ or +deserialize+.
231
243
  def define_attribute(
232
244
  name,
233
245
  cast_type,
@@ -240,6 +252,7 @@ module ActiveRecord
240
252
 
241
253
  def _default_attributes # :nodoc:
242
254
  @default_attributes ||= begin
255
+ # TODO: Remove the need for a connection after we release 8.1.
243
256
  attributes_hash = with_connection do |connection|
244
257
  columns_hash.transform_values do |column|
245
258
  ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
@@ -299,6 +312,7 @@ module ActiveRecord
299
312
  end
300
313
 
301
314
  def type_for_column(connection, column)
315
+ # TODO: Remove the need for a connection after we release 8.1.
302
316
  hook_attribute_type(column.name, super)
303
317
  end
304
318
  end
@@ -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
@@ -168,7 +166,7 @@ module ActiveRecord
168
166
  end
169
167
  end
170
168
 
171
- # Clears the cache which maps classes.
169
+ # Clears reloadable connection caches in all connection pools.
172
170
  #
173
171
  # See ConnectionPool#clear_reloadable_connections! for details.
174
172
  def clear_reloadable_connections!(role = nil)
@@ -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