activerecord 6.1.7 → 7.2.0

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 (332) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -1385
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  30. data/lib/active_record/associations/join_dependency.rb +23 -15
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +404 -509
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +2 -14
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +51 -49
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +18 -34
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +276 -251
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +56 -0
  165. data/lib/active_record/enum.rb +163 -63
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +56 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +143 -159
  199. data/lib/active_record/nested_attributes.rb +48 -21
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +19 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +234 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +325 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +198 -63
  216. data/lib/active_record/relation/calculations.rb +300 -111
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +842 -150
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +5 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +277 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +64 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/to_sql.rb +170 -36
  317. data/lib/arel/visitors/visitor.rb +2 -2
  318. data/lib/arel.rb +23 -4
  319. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  320. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  321. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  322. data/lib/rails/generators/active_record/migration.rb +3 -1
  323. data/lib/rails/generators/active_record/model/USAGE +113 -0
  324. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  325. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  326. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  328. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  329. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  330. metadata +100 -14
  331. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  332. data/lib/active_record/null_relation.rb +0 -67
@@ -1,42 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/enumerable"
4
- require "active_support/core_ext/hash/indifferent_access"
5
- require "active_support/core_ext/string/filters"
4
+ require "active_support/core_ext/module/delegation"
6
5
  require "active_support/parameter_filter"
7
6
  require "concurrent/map"
8
7
 
9
8
  module ActiveRecord
9
+ # = Active Record \Core
10
10
  module Core
11
11
  extend ActiveSupport::Concern
12
+ include ActiveModel::Access
12
13
 
13
14
  included do
14
15
  ##
15
16
  # :singleton-method:
16
17
  #
17
- # Accepts a logger conforming to the interface of Log4r which is then
18
- # passed on to any new database connections made and which can be
19
- # retrieved on both a class and instance level by calling +logger+.
20
- mattr_accessor :logger, instance_writer: false
21
-
22
- ##
23
- # :singleton-method:
24
- #
25
- # Specifies if the methods calling database queries should be logged below
26
- # their relevant queries. Defaults to false.
27
- mattr_accessor :verbose_query_logs, instance_writer: false, default: false
18
+ # Accepts a logger conforming to the interface of Log4r or the default
19
+ # Ruby +Logger+ class, which is then passed on to any new database
20
+ # connections made. You can retrieve this logger by calling +logger+ on
21
+ # either an Active Record model class or an Active Record model instance.
22
+ class_attribute :logger, instance_writer: false
23
+
24
+ class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
25
+
26
+ # The job class used to destroy associations in the background.
27
+ def self.destroy_association_async_job
28
+ if _destroy_association_async_job.is_a?(String)
29
+ self._destroy_association_async_job = _destroy_association_async_job.constantize
30
+ end
31
+ _destroy_association_async_job
32
+ rescue NameError => error
33
+ raise NameError, "Unable to load destroy_association_async_job: #{error.message}"
34
+ end
28
35
 
29
- ##
30
- # :singleton-method:
31
- #
32
- # Specifies the names of the queues used by background jobs.
33
- mattr_accessor :queues, instance_accessor: false, default: {}
36
+ singleton_class.alias_method :destroy_association_async_job=, :_destroy_association_async_job=
37
+ delegate :destroy_association_async_job, to: :class
34
38
 
35
39
  ##
36
40
  # :singleton-method:
37
41
  #
38
- # Specifies the job used to destroy associations in the background
39
- class_attribute :destroy_association_async_job, instance_writer: false, instance_predicate: false, default: false
42
+ # Specifies the maximum number of records that will be destroyed in a
43
+ # single background job by the <tt>dependent: :destroy_async</tt>
44
+ # association option. When +nil+ (default), all dependent records will be
45
+ # destroyed in a single background job. If specified, the records to be
46
+ # destroyed will be split into multiple background jobs.
47
+ class_attribute :destroy_association_async_batch_size, instance_writer: false, instance_predicate: false, default: nil
40
48
 
41
49
  ##
42
50
  # Contains the database configuration - as is typically stored in config/database.yml -
@@ -46,106 +54,45 @@ module ActiveRecord
46
54
  #
47
55
  # development:
48
56
  # adapter: sqlite3
49
- # database: db/development.sqlite3
57
+ # database: storage/development.sqlite3
50
58
  #
51
59
  # production:
52
60
  # adapter: sqlite3
53
- # database: db/production.sqlite3
61
+ # database: storage/production.sqlite3
54
62
  #
55
63
  # ...would result in ActiveRecord::Base.configurations to look like this:
56
64
  #
57
65
  # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
58
66
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
59
- # @name="primary", @config={adapter: "sqlite3", database: "db/development.sqlite3"}>,
67
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/development.sqlite3"}>,
60
68
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
61
- # @name="primary", @config={adapter: "sqlite3", database: "db/production.sqlite3"}>
69
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/production.sqlite3"}>
62
70
  # ]>
63
71
  def self.configurations=(config)
64
72
  @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
65
73
  end
66
74
  self.configurations = {}
67
75
 
68
- # Returns fully resolved ActiveRecord::DatabaseConfigurations object
76
+ # Returns a fully resolved ActiveRecord::DatabaseConfigurations object.
69
77
  def self.configurations
70
78
  @@configurations
71
79
  end
72
80
 
73
81
  ##
74
82
  # :singleton-method:
75
- # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
76
- # dates and times from the database. This is set to :utc by default.
77
- mattr_accessor :default_timezone, instance_writer: false, default: :utc
78
-
79
- ##
80
- # :singleton-method:
81
- # Specifies the format to use when dumping the database schema with Rails'
82
- # Rakefile. If :sql, the schema is dumped as (potentially database-
83
- # specific) SQL statements. If :ruby, the schema is dumped as an
84
- # ActiveRecord::Schema file which can be loaded into any database that
85
- # supports migrations. Use :ruby if you want to have different database
86
- # adapters for, e.g., your development and test environments.
87
- mattr_accessor :schema_format, instance_writer: false, default: :ruby
88
-
89
- ##
90
- # :singleton-method:
91
- # Specifies if an error should be raised if the query has an order being
92
- # ignored when doing batch queries. Useful in applications where the
93
- # scope being ignored is error-worthy, rather than a warning.
94
- mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
95
-
96
- ##
97
- # :singleton-method:
98
- # Specify whether or not to use timestamps for migration versions
99
- mattr_accessor :timestamped_migrations, instance_writer: false, default: true
100
-
101
- ##
102
- # :singleton-method:
103
- # Specify whether schema dump should happen at the end of the
104
- # db:migrate rails command. This is true by default, which is useful for the
105
- # development environment. This should ideally be false in the production
106
- # environment where dumping schema is rarely needed.
107
- mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
108
-
109
- ##
110
- # :singleton-method:
111
- # Specifies which database schemas to dump when calling db:schema:dump.
112
- # If the value is :schema_search_path (the default), any schemas listed in
113
- # schema_search_path are dumped. Use :all to dump all schemas regardless
114
- # of schema_search_path, or a string of comma separated schemas for a
115
- # custom list.
116
- mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path
117
-
118
- ##
119
- # :singleton-method:
120
- # Specify a threshold for the size of query result sets. If the number of
121
- # records in the set exceeds the threshold, a warning is logged. This can
122
- # be used to identify queries which load thousands of records and
123
- # potentially cause memory bloat.
124
- mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
125
-
126
- ##
127
- # :singleton-method:
128
- # Show a warning when Rails couldn't parse your database.yml
129
- # for multiple databases.
130
- mattr_accessor :suppress_multiple_database_warning, instance_writer: false, default: false
131
-
132
- mattr_accessor :maintain_test_schema, instance_accessor: false
83
+ # Force enumeration of all columns in SELECT statements.
84
+ # e.g. <tt>SELECT first_name, last_name FROM ...</tt> instead of <tt>SELECT * FROM ...</tt>
85
+ # This avoids +PreparedStatementCacheExpired+ errors when a column is added
86
+ # to the database while the app is running.
87
+ class_attribute :enumerate_columns_in_select_statements, instance_accessor: false, default: false
133
88
 
134
89
  class_attribute :belongs_to_required_by_default, instance_accessor: false
135
90
 
136
- ##
137
- # :singleton-method:
138
- # Set the application to log or raise when an association violates strict loading.
139
- # Defaults to :raise.
140
- mattr_accessor :action_on_strict_loading_violation, instance_accessor: false, default: :raise
141
-
142
91
  class_attribute :strict_loading_by_default, instance_accessor: false, default: false
143
92
 
144
- mattr_accessor :writing_role, instance_accessor: false, default: :writing
93
+ class_attribute :has_many_inversing, instance_accessor: false, default: false
145
94
 
146
- mattr_accessor :reading_role, instance_accessor: false, default: :reading
147
-
148
- mattr_accessor :has_many_inversing, instance_accessor: false, default: false
95
+ class_attribute :run_commit_callbacks_on_first_saved_instances_in_transaction, instance_accessor: false, default: true
149
96
 
150
97
  class_attribute :default_connection_handler, instance_writer: false
151
98
 
@@ -153,40 +100,38 @@ module ActiveRecord
153
100
 
154
101
  class_attribute :default_shard, instance_writer: false
155
102
 
156
- mattr_accessor :legacy_connection_handling, instance_writer: false, default: true
103
+ class_attribute :shard_selector, instance_accessor: false, default: nil
157
104
 
158
- # Application configurable boolean that instructs the YAML Coder to use
159
- # an unsafe load if set to true.
160
- mattr_accessor :use_yaml_unsafe_load, instance_writer: false, default: false
105
+ # Specifies the attributes that will be included in the output of the #inspect method
106
+ class_attribute :attributes_for_inspect, instance_accessor: false, default: [:id]
161
107
 
162
- # Application configurable array that provides additional permitted classes
163
- # to Psych safe_load in the YAML Coder
164
- mattr_accessor :yaml_column_permitted_classes, instance_writer: false, default: [Symbol]
108
+ def self.application_record_class? # :nodoc:
109
+ if ActiveRecord.application_record_class
110
+ self == ActiveRecord.application_record_class
111
+ else
112
+ if defined?(ApplicationRecord) && self == ApplicationRecord
113
+ true
114
+ end
115
+ end
116
+ end
165
117
 
166
118
  self.filter_attributes = []
167
119
 
168
120
  def self.connection_handler
169
- Thread.current.thread_variable_get(:ar_connection_handler) || default_connection_handler
121
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] || default_connection_handler
170
122
  end
171
123
 
172
124
  def self.connection_handler=(handler)
173
- Thread.current.thread_variable_set(:ar_connection_handler, handler)
125
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
174
126
  end
175
127
 
176
- def self.connection_handlers
177
- unless legacy_connection_handling
178
- raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
179
- end
180
-
181
- @@connection_handlers ||= {}
128
+ def self.asynchronous_queries_session # :nodoc:
129
+ asynchronous_queries_tracker.current_session
182
130
  end
183
131
 
184
- def self.connection_handlers=(handlers)
185
- unless legacy_connection_handling
186
- raise NotImplementedError, "The new connection handling does not setting support multiple connection handlers."
187
- end
188
-
189
- @@connection_handlers = handlers
132
+ def self.asynchronous_queries_tracker # :nodoc:
133
+ ActiveSupport::IsolatedExecutionState[:active_record_asynchronous_queries_tracker] ||= \
134
+ AsynchronousQueriesTracker.new
190
135
  end
191
136
 
192
137
  # Returns the symbol representing the current connected role.
@@ -199,16 +144,12 @@ module ActiveRecord
199
144
  # ActiveRecord::Base.current_role #=> :reading
200
145
  # end
201
146
  def self.current_role
202
- if ActiveRecord::Base.legacy_connection_handling
203
- connection_handlers.key(connection_handler) || default_role
204
- else
205
- connected_to_stack.reverse_each do |hash|
206
- return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
207
- return hash[:role] if hash[:role] && hash[:klasses].include?(connection_classes)
208
- end
209
-
210
- default_role
147
+ connected_to_stack.reverse_each do |hash|
148
+ return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
149
+ return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
211
150
  end
151
+
152
+ default_role
212
153
  end
213
154
 
214
155
  # Returns the symbol representing the current connected shard.
@@ -223,7 +164,7 @@ module ActiveRecord
223
164
  def self.current_shard
224
165
  connected_to_stack.reverse_each do |hash|
225
166
  return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
226
- return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_classes)
167
+ return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_class_for_self)
227
168
  end
228
169
 
229
170
  default_shard
@@ -240,24 +181,20 @@ module ActiveRecord
240
181
  # ActiveRecord::Base.current_preventing_writes #=> false
241
182
  # end
242
183
  def self.current_preventing_writes
243
- if legacy_connection_handling
244
- connection_handler.prevent_writes
245
- else
246
- connected_to_stack.reverse_each do |hash|
247
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
248
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_classes)
249
- end
250
-
251
- false
184
+ connected_to_stack.reverse_each do |hash|
185
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
186
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
252
187
  end
188
+
189
+ false
253
190
  end
254
191
 
255
192
  def self.connected_to_stack # :nodoc:
256
- if connected_to_stack = Thread.current.thread_variable_get(:ar_connected_to_stack)
193
+ if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
257
194
  connected_to_stack
258
195
  else
259
196
  connected_to_stack = Concurrent::Array.new
260
- Thread.current.thread_variable_set(:ar_connected_to_stack, connected_to_stack)
197
+ ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack] = connected_to_stack
261
198
  connected_to_stack
262
199
  end
263
200
  end
@@ -266,7 +203,7 @@ module ActiveRecord
266
203
  @connection_class = b
267
204
  end
268
205
 
269
- def self.connection_class # :nodoc
206
+ def self.connection_class # :nodoc:
270
207
  @connection_class ||= false
271
208
  end
272
209
 
@@ -274,7 +211,7 @@ module ActiveRecord
274
211
  self.connection_class
275
212
  end
276
213
 
277
- def self.connection_classes # :nodoc:
214
+ def self.connection_class_for_self # :nodoc:
278
215
  klass = self
279
216
 
280
217
  until klass == Base
@@ -285,22 +222,14 @@ module ActiveRecord
285
222
  klass
286
223
  end
287
224
 
288
- def self.allow_unsafe_raw_sql # :nodoc:
289
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql is deprecated and will be removed in Rails 7.0")
290
- end
291
-
292
- def self.allow_unsafe_raw_sql=(value) # :nodoc:
293
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql= is deprecated and will be removed in Rails 7.0")
294
- end
295
-
296
225
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
297
- self.default_role = writing_role
226
+ self.default_role = ActiveRecord.writing_role
298
227
  self.default_shard = :default
299
228
 
300
229
  def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
301
- case action_on_strict_loading_violation
230
+ case ActiveRecord.action_on_strict_loading_violation
302
231
  when :raise
303
- message = "`#{owner}` is marked for strict_loading. The `#{reflection.klass}` association named `:#{reflection.name}` cannot be lazily loaded."
232
+ message = reflection.strict_loading_violation_message(owner)
304
233
  raise ActiveRecord::StrictLoadingViolationError.new(message)
305
234
  when :log
306
235
  name = "strict_loading_violation.active_record"
@@ -314,19 +243,6 @@ module ActiveRecord
314
243
  @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
315
244
  end
316
245
 
317
- def inherited(child_class) # :nodoc:
318
- # initialize cache at class definition for thread safety
319
- child_class.initialize_find_by_cache
320
- unless child_class.base_class?
321
- klass = self
322
- until klass.base_class?
323
- klass.initialize_find_by_cache
324
- klass = klass.superclass
325
- end
326
- end
327
- super
328
- end
329
-
330
246
  def find(*ids) # :nodoc:
331
247
  # We don't have cache keys for this stuff yet
332
248
  return super unless ids.length == 1
@@ -336,14 +252,8 @@ module ActiveRecord
336
252
 
337
253
  return super if StatementCache.unsupported_value?(id)
338
254
 
339
- key = primary_key
340
-
341
- statement = cached_find_by_statement(key) { |params|
342
- where(key => params.bind).limit(1)
343
- }
344
-
345
- statement.execute([id], connection).first ||
346
- raise(RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id))
255
+ cached_find_by([primary_key], [id]) ||
256
+ raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
347
257
  end
348
258
 
349
259
  def find_by(*args) # :nodoc:
@@ -365,31 +275,36 @@ module ActiveRecord
365
275
  elsif reflection.belongs_to? && !reflection.polymorphic?
366
276
  key = reflection.join_foreign_key
367
277
  pkey = reflection.join_primary_key
368
- value = value.public_send(pkey) if value.respond_to?(pkey)
278
+
279
+ if pkey.is_a?(Array)
280
+ if pkey.all? { |attribute| value.respond_to?(attribute) }
281
+ value = pkey.map do |attribute|
282
+ if attribute == "id"
283
+ value.id_value
284
+ else
285
+ value.public_send(attribute)
286
+ end
287
+ end
288
+ composite_primary_key = true
289
+ end
290
+ else
291
+ value = value.public_send(pkey) if value.respond_to?(pkey)
292
+ end
369
293
  end
370
294
 
371
- if !columns_hash.key?(key) || StatementCache.unsupported_value?(value)
295
+ if !composite_primary_key &&
296
+ (!columns_hash.key?(key) || StatementCache.unsupported_value?(value))
372
297
  return super
373
298
  end
374
299
 
375
300
  h[key] = value
376
301
  end
377
302
 
378
- keys = hash.keys
379
- statement = cached_find_by_statement(keys) { |params|
380
- wheres = keys.index_with { params.bind }
381
- where(wheres).limit(1)
382
- }
383
-
384
- begin
385
- statement.execute(hash.values, connection).first
386
- rescue TypeError
387
- raise ActiveRecord::StatementInvalid
388
- end
303
+ cached_find_by(hash.keys, hash.values)
389
304
  end
390
305
 
391
306
  def find_by!(*args) # :nodoc:
392
- find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
307
+ find_by(*args) || where(*args).raise_record_not_found_exception!
393
308
  end
394
309
 
395
310
  def initialize_generated_modules # :nodoc:
@@ -408,10 +323,10 @@ module ActiveRecord
408
323
 
409
324
  # Returns columns which shouldn't be exposed while calling +#inspect+.
410
325
  def filter_attributes
411
- if defined?(@filter_attributes)
412
- @filter_attributes
413
- else
326
+ if @filter_attributes.nil?
414
327
  superclass.filter_attributes
328
+ else
329
+ @filter_attributes
415
330
  end
416
331
  end
417
332
 
@@ -422,13 +337,13 @@ module ActiveRecord
422
337
  end
423
338
 
424
339
  def inspection_filter # :nodoc:
425
- if defined?(@filter_attributes)
340
+ if @filter_attributes.nil?
341
+ superclass.inspection_filter
342
+ else
426
343
  @inspection_filter ||= begin
427
344
  mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
428
345
  ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
429
346
  end
430
- else
431
- superclass.inspection_filter
432
347
  end
433
348
  end
434
349
 
@@ -439,7 +354,7 @@ module ActiveRecord
439
354
  elsif abstract_class?
440
355
  "#{super}(abstract)"
441
356
  elsif !connected?
442
- "#{super} (call '#{super}.connection' to establish a connection)"
357
+ "#{super} (call '#{super}.lease_connection' to establish a connection)"
443
358
  elsif table_exists?
444
359
  attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
445
360
  "#{super}(#{attr_list})"
@@ -448,25 +363,11 @@ module ActiveRecord
448
363
  end
449
364
  end
450
365
 
451
- # Overwrite the default class equality method to provide support for decorated models.
452
- def ===(object) # :nodoc:
453
- object.is_a?(self)
454
- end
455
-
456
- # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
457
- #
458
- # class Post < ActiveRecord::Base
459
- # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
460
- # end
366
+ # Returns an instance of +Arel::Table+ loaded with the current table name.
461
367
  def arel_table # :nodoc:
462
368
  @arel_table ||= Arel::Table.new(table_name, klass: self)
463
369
  end
464
370
 
465
- def arel_attribute(name, table = arel_table) # :nodoc:
466
- table[name]
467
- end
468
- deprecate :arel_attribute
469
-
470
371
  def predicate_builder # :nodoc:
471
372
  @predicate_builder ||= PredicateBuilder.new(table_metadata)
472
373
  end
@@ -475,16 +376,34 @@ module ActiveRecord
475
376
  TypeCaster::Map.new(self)
476
377
  end
477
378
 
478
- def _internal? # :nodoc:
479
- false
480
- end
481
-
482
- def cached_find_by_statement(key, &block) # :nodoc:
379
+ def cached_find_by_statement(connection, key, &block) # :nodoc:
483
380
  cache = @find_by_statement_cache[connection.prepared_statements]
484
381
  cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
485
382
  end
486
383
 
487
384
  private
385
+ def inherited(subclass)
386
+ super
387
+
388
+ # initialize cache at class definition for thread safety
389
+ subclass.initialize_find_by_cache
390
+ unless subclass.base_class?
391
+ klass = self
392
+ until klass.base_class?
393
+ klass.initialize_find_by_cache
394
+ klass = klass.superclass
395
+ end
396
+ end
397
+
398
+ subclass.class_eval do
399
+ @arel_table = nil
400
+ @predicate_builder = nil
401
+ @inspection_filter = nil
402
+ @filter_attributes ||= nil
403
+ @generated_association_methods ||= nil
404
+ end
405
+ end
406
+
488
407
  def relation
489
408
  relation = Relation.create(self)
490
409
 
@@ -498,6 +417,27 @@ module ActiveRecord
498
417
  def table_metadata
499
418
  TableMetadata.new(self, arel_table)
500
419
  end
420
+
421
+ def cached_find_by(keys, values)
422
+ with_connection do |connection|
423
+ statement = cached_find_by_statement(connection, keys) { |params|
424
+ wheres = keys.index_with do |key|
425
+ if key.is_a?(Array)
426
+ [key.map { params.bind }]
427
+ else
428
+ params.bind
429
+ end
430
+ end
431
+ where(wheres).limit(1)
432
+ }
433
+
434
+ begin
435
+ statement.execute(values.flatten, connection, allow_retry: true).first
436
+ rescue TypeError
437
+ raise ActiveRecord::StatementInvalid
438
+ end
439
+ end
440
+ end
501
441
  end
502
442
 
503
443
  # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -505,7 +445,7 @@ module ActiveRecord
505
445
  # In both instances, valid attribute keys are determined by the column names of the associated table --
506
446
  # hence you can't have attributes that aren't part of the table columns.
507
447
  #
508
- # ==== Example:
448
+ # ==== Example
509
449
  # # Instantiates a single new object
510
450
  # User.new(first_name: 'Jamie')
511
451
  def initialize(attributes = nil)
@@ -515,7 +455,7 @@ module ActiveRecord
515
455
  init_internals
516
456
  initialize_internals_callback
517
457
 
518
- assign_attributes(attributes) if attributes
458
+ super
519
459
 
520
460
  yield self if block_given?
521
461
  _run_initialize_callbacks
@@ -536,7 +476,7 @@ module ActiveRecord
536
476
  # post.init_with(coder)
537
477
  # post.title # => 'hello world'
538
478
  def init_with(coder, &block)
539
- coder = LegacyYamlAdapter.convert(self.class, coder)
479
+ coder = LegacyYamlAdapter.convert(coder)
540
480
  attributes = self.class.yaml_encoder.decode(coder)
541
481
  init_with_attributes(attributes, coder["new_record"], &block)
542
482
  end
@@ -583,12 +523,17 @@ module ActiveRecord
583
523
  # only, not its associations. The extent of a "deep" copy is application
584
524
  # specific and is therefore left to the application to implement according
585
525
  # to its need.
586
- # The dup method does not preserve the timestamps (created|updated)_(at|on).
526
+ # The dup method does not preserve the timestamps (created|updated)_(at|on)
527
+ # and locking column.
587
528
 
588
529
  ##
589
530
  def initialize_dup(other) # :nodoc:
590
531
  @attributes = @attributes.deep_dup
591
- @attributes.reset(@primary_key)
532
+ if self.class.composite_primary_key?
533
+ @primary_key.each { |key| @attributes.reset(key) }
534
+ else
535
+ @attributes.reset(@primary_key)
536
+ end
592
537
 
593
538
  _run_initialize_callbacks
594
539
 
@@ -618,6 +563,35 @@ module ActiveRecord
618
563
  coder["active_record_yaml_version"] = 2
619
564
  end
620
565
 
566
+ ##
567
+ # :method: slice
568
+ #
569
+ # :call-seq: slice(*methods)
570
+ #
571
+ # Returns a hash of the given methods with their names as keys and returned
572
+ # values as values.
573
+ #
574
+ # topic = Topic.new(title: "Budget", author_name: "Jason")
575
+ # topic.slice(:title, :author_name)
576
+ # => { "title" => "Budget", "author_name" => "Jason" }
577
+ #
578
+ #--
579
+ # Implemented by ActiveModel::Access#slice.
580
+
581
+ ##
582
+ # :method: values_at
583
+ #
584
+ # :call-seq: values_at(*methods)
585
+ #
586
+ # Returns an array of the values returned by the given methods.
587
+ #
588
+ # topic = Topic.new(title: "Budget", author_name: "Jason")
589
+ # topic.values_at(:title, :author_name)
590
+ # => ["Budget", "Jason"]
591
+ #
592
+ #--
593
+ # Implemented by ActiveModel::Access#values_at.
594
+
621
595
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
622
596
  # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
623
597
  #
@@ -630,7 +604,7 @@ module ActiveRecord
630
604
  def ==(comparison_object)
631
605
  super ||
632
606
  comparison_object.instance_of?(self.class) &&
633
- !id.nil? &&
607
+ primary_key_values_present? &&
634
608
  comparison_object.id == id
635
609
  end
636
610
  alias :eql? :==
@@ -638,7 +612,9 @@ module ActiveRecord
638
612
  # Delegates to id in order to allow two records of the same type and id to work with something like:
639
613
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
640
614
  def hash
641
- if id
615
+ id = self.id
616
+
617
+ if primary_key_values_present?
642
618
  self.class.hash ^ id.hash
643
619
  else
644
620
  super
@@ -689,14 +665,57 @@ module ActiveRecord
689
665
  # if the record tries to lazily load an association.
690
666
  #
691
667
  # user = User.first
692
- # user.strict_loading!
668
+ # user.strict_loading! # => true
669
+ # user.address.city
670
+ # => ActiveRecord::StrictLoadingViolationError
693
671
  # user.comments.to_a
694
672
  # => ActiveRecord::StrictLoadingViolationError
695
- def strict_loading!
696
- @strict_loading = true
673
+ #
674
+ # ==== Parameters
675
+ #
676
+ # * +value+ - Boolean specifying whether to enable or disable strict loading.
677
+ # * <tt>:mode</tt> - Symbol specifying strict loading mode. Defaults to :all. Using
678
+ # :n_plus_one_only mode will only raise an error if an association that
679
+ # will lead to an n plus one query is lazily loaded.
680
+ #
681
+ # ==== Examples
682
+ #
683
+ # user = User.first
684
+ # user.strict_loading!(false) # => false
685
+ # user.address.city # => "Tatooine"
686
+ # user.comments.to_a # => [#<Comment:0x00...]
687
+ #
688
+ # user.strict_loading!(mode: :n_plus_one_only)
689
+ # user.address.city # => "Tatooine"
690
+ # user.comments.to_a # => [#<Comment:0x00...]
691
+ # user.comments.first.ratings.to_a
692
+ # => ActiveRecord::StrictLoadingViolationError
693
+ def strict_loading!(value = true, mode: :all)
694
+ unless [:all, :n_plus_one_only].include?(mode)
695
+ raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
696
+ end
697
+
698
+ @strict_loading_mode = mode
699
+ @strict_loading = value
700
+ end
701
+
702
+ attr_reader :strict_loading_mode
703
+
704
+ # Returns +true+ if the record uses strict_loading with +:n_plus_one_only+ mode enabled.
705
+ def strict_loading_n_plus_one_only?
706
+ @strict_loading_mode == :n_plus_one_only
707
+ end
708
+
709
+ # Returns +true+ if the record uses strict_loading with +:all+ mode enabled.
710
+ def strict_loading_all?
711
+ @strict_loading_mode == :all
697
712
  end
698
713
 
699
714
  # Marks this record as read only.
715
+ #
716
+ # customer = Customer.first
717
+ # customer.readonly!
718
+ # customer.save # Raises an ActiveRecord::ReadOnlyRecord
700
719
  def readonly!
701
720
  @readonly = true
702
721
  end
@@ -705,21 +724,14 @@ module ActiveRecord
705
724
  self.class.connection_handler
706
725
  end
707
726
 
708
- # Returns the contents of the record as a nicely formatted string.
727
+ # Returns the attributes specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string.
709
728
  def inspect
710
- # We check defined?(@attributes) not to issue warnings if the object is
711
- # allocated but not initialized.
712
- inspection = if defined?(@attributes) && @attributes
713
- self.class.attribute_names.collect do |name|
714
- if _has_attribute?(name)
715
- "#{name}: #{attribute_for_inspect(name)}"
716
- end
717
- end.compact.join(", ")
718
- else
719
- "not initialized"
720
- end
729
+ inspect_with_attributes(attributes_for_inspect)
730
+ end
721
731
 
722
- "#<#{self.class} #{inspection}>"
732
+ # Returns the full contents of the record as a nicely formatted string.
733
+ def full_inspect
734
+ inspect_with_attributes(attribute_names)
723
735
  end
724
736
 
725
737
  # Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
@@ -727,17 +739,17 @@ module ActiveRecord
727
739
  def pretty_print(pp)
728
740
  return super if custom_inspect_method_defined?
729
741
  pp.object_address_group(self) do
730
- if defined?(@attributes) && @attributes
731
- attr_names = self.class.attribute_names.select { |name| _has_attribute?(name) }
742
+ if @attributes
743
+ attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
732
744
  pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
745
+ attr_name = attr_name.to_s
733
746
  pp.breakable " "
734
747
  pp.group(1) do
735
748
  pp.text attr_name
736
749
  pp.text ":"
737
750
  pp.breakable
738
- value = _read_attribute(attr_name)
739
- value = inspection_filter.filter_param(attr_name, value) unless value.nil?
740
- pp.pp value
751
+ value = attribute_for_inspect(attr_name)
752
+ pp.text value
741
753
  end
742
754
  end
743
755
  else
@@ -747,16 +759,6 @@ module ActiveRecord
747
759
  end
748
760
  end
749
761
 
750
- # Returns a hash of the given methods with their names as keys and returned values as values.
751
- def slice(*methods)
752
- methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
753
- end
754
-
755
- # Returns an array of the values returned by the given methods.
756
- def values_at(*methods)
757
- methods.flatten.map! { |method| public_send(method) }
758
- end
759
-
760
762
  private
761
763
  # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
762
764
  # the array, and then rescues from the possible +NoMethodError+. If those elements are
@@ -771,16 +773,20 @@ module ActiveRecord
771
773
  end
772
774
 
773
775
  def init_internals
774
- @primary_key = self.class.primary_key
775
776
  @readonly = false
776
777
  @previously_new_record = false
777
778
  @destroyed = false
778
779
  @marked_for_destruction = false
779
780
  @destroyed_by_association = nil
780
781
  @_start_transaction_state = nil
781
- @strict_loading = self.class.strict_loading_by_default
782
782
 
783
- self.class.define_attribute_methods
783
+ klass = self.class
784
+
785
+ @primary_key = klass.primary_key
786
+ @strict_loading = klass.strict_loading_by_default
787
+ @strict_loading_mode = :all
788
+
789
+ klass.define_attribute_methods
784
790
  end
785
791
 
786
792
  def initialize_internals_callback
@@ -800,5 +806,24 @@ module ActiveRecord
800
806
  def inspection_filter
801
807
  self.class.inspection_filter
802
808
  end
809
+
810
+ def inspect_with_attributes(attributes_to_list)
811
+ inspection = if @attributes
812
+ attributes_to_list.filter_map do |name|
813
+ name = name.to_s
814
+ if _has_attribute?(name)
815
+ "#{name}: #{attribute_for_inspect(name)}"
816
+ end
817
+ end.join(", ")
818
+ else
819
+ "not initialized"
820
+ end
821
+
822
+ "#<#{self.class} #{inspection}>"
823
+ end
824
+
825
+ def attributes_for_inspect
826
+ self.class.attributes_for_inspect == :all ? attribute_names : self.class.attributes_for_inspect
827
+ end
803
828
  end
804
829
  end