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
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2022 David Heinemeier Hansson
1
+ Copyright (c) David Heinemeier Hansson
2
2
 
3
3
  Arel originally copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
4
4
 
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = Active Record -- Object-relational mapping in Rails
1
+ = Active Record -- Object-relational mapping in \Rails
2
2
 
3
3
  Active Record connects classes to relational database tables to establish an
4
4
  almost zero-configuration persistence layer for applications. The library
@@ -13,29 +13,28 @@ columns. Although these mappings can be defined explicitly, it's recommended
13
13
  to follow naming conventions, especially when getting started with the
14
14
  library.
15
15
 
16
- You can read more about Active Record in the {Active Record Basics}[https://edgeguides.rubyonrails.org/active_record_basics.html] guide.
16
+ You can read more about Active Record in the {Active Record Basics}[https://guides.rubyonrails.org/active_record_basics.html] guide.
17
17
 
18
18
  A short rundown of some of the major features:
19
19
 
20
20
  * Automated mapping between classes and tables, attributes and columns.
21
21
 
22
- class Product < ActiveRecord::Base
23
- end
24
-
25
- {Learn more}[link:classes/ActiveRecord/Base.html]
22
+ class Product < ActiveRecord::Base
23
+ end
26
24
 
27
- The Product class is automatically mapped to the table named "products",
28
- which might look like this:
25
+ The Product class is automatically mapped to the table named "products",
26
+ which might look like this:
29
27
 
30
- CREATE TABLE products (
31
- id bigint NOT NULL auto_increment,
32
- name varchar(255),
33
- PRIMARY KEY (id)
34
- );
28
+ CREATE TABLE products (
29
+ id bigint NOT NULL auto_increment,
30
+ name varchar(255),
31
+ PRIMARY KEY (id)
32
+ );
35
33
 
36
- This would also define the following accessors: <tt>Product#name</tt> and
37
- <tt>Product#name=(new_name)</tt>.
34
+ This would also define the following accessors: <tt>Product#name</tt> and
35
+ <tt>Product#name=(new_name)</tt>.
38
36
 
37
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
39
38
 
40
39
  * Associations between objects defined by simple class methods.
41
40
 
@@ -45,7 +44,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
45
44
  belongs_to :conglomerate
46
45
  end
47
46
 
48
- {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html]
47
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html]
49
48
 
50
49
 
51
50
  * Aggregations of value objects.
@@ -57,7 +56,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
57
56
  mapping: [%w(address_street street), %w(address_city city)]
58
57
  end
59
58
 
60
- {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
59
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html]
61
60
 
62
61
 
63
62
  * Validation rules that can differ for new or existing objects.
@@ -69,7 +68,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
69
68
  validates :password, :email_address, confirmation: true, on: :create
70
69
  end
71
70
 
72
- {Learn more}[link:classes/ActiveRecord/Validations.html]
71
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Validations.html]
73
72
 
74
73
 
75
74
  * Callbacks available for the entire life cycle (instantiation, saving, destroying, validating, etc.).
@@ -79,7 +78,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
79
78
  # the `invalidate_payment_plan` method gets called just before Person#destroy
80
79
  end
81
80
 
82
- {Learn more}[link:classes/ActiveRecord/Callbacks.html]
81
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html]
83
82
 
84
83
 
85
84
  * Inheritance hierarchies.
@@ -89,7 +88,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
89
88
  class Client < Company; end
90
89
  class PriorityClient < Client; end
91
90
 
92
- {Learn more}[link:classes/ActiveRecord/Base.html]
91
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
93
92
 
94
93
 
95
94
  * Transactions.
@@ -100,7 +99,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
100
99
  mary.deposit(100)
101
100
  end
102
101
 
103
- {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html]
102
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html]
104
103
 
105
104
 
106
105
  * Reflections on columns, associations, and aggregations.
@@ -109,7 +108,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
109
108
  reflection.klass # => Client (class)
110
109
  Firm.columns # Returns an array of column descriptors for the firms table
111
110
 
112
- {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html]
111
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html]
113
112
 
114
113
 
115
114
  * Database abstraction through simple adapters.
@@ -126,13 +125,13 @@ This would also define the following accessors: <tt>Product#name</tt> and
126
125
  database: 'activerecord'
127
126
  )
128
127
 
129
- {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
130
- MySQL[link:classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
131
- PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
132
- SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
128
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html] and read about the built-in support for
129
+ MySQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
130
+ PostgreSQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
131
+ SQLite3[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
133
132
 
134
133
 
135
- * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://ruby-doc.org/stdlib/libdoc/logger/rdoc/].
134
+ * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://docs.ruby-lang.org/en/master/Logger.html].
136
135
 
137
136
  ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
138
137
  ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
@@ -140,7 +139,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
140
139
 
141
140
  * Database agnostic schema management with Migrations.
142
141
 
143
- class AddSystemSettings < ActiveRecord::Migration[6.0]
142
+ class AddSystemSettings < ActiveRecord::Migration[7.2]
144
143
  def up
145
144
  create_table :system_settings do |t|
146
145
  t.string :name
@@ -158,7 +157,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
158
157
  end
159
158
  end
160
159
 
161
- {Learn more}[link:classes/ActiveRecord/Migration.html]
160
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Migration.html]
162
161
 
163
162
 
164
163
  == Philosophy
@@ -167,6 +166,7 @@ Active Record is an implementation of the object-relational mapping (ORM)
167
166
  pattern[https://www.martinfowler.com/eaaCatalog/activeRecord.html] by the same
168
167
  name described by Martin Fowler:
169
168
 
169
+ >>>
170
170
  "An object that wraps a row in a database table or view,
171
171
  encapsulates the database access, and adds domain logic on that data."
172
172
 
@@ -192,7 +192,7 @@ The latest version of Active Record can be installed with RubyGems:
192
192
 
193
193
  $ gem install activerecord
194
194
 
195
- Source code can be downloaded as part of the Rails project on GitHub:
195
+ Source code can be downloaded as part of the \Rails project on GitHub:
196
196
 
197
197
  * https://github.com/rails/rails/tree/main/activerecord
198
198
 
@@ -210,7 +210,7 @@ API documentation is at:
210
210
 
211
211
  * https://api.rubyonrails.org
212
212
 
213
- Bug reports for the Ruby on Rails project can be filed here:
213
+ Bug reports for the Ruby on \Rails project can be filed here:
214
214
 
215
215
  * https://github.com/rails/rails/issues
216
216
 
@@ -176,10 +176,10 @@ Benchmark.ips(TIME) do |x|
176
176
  end
177
177
 
178
178
  x.report "Model.log" do
179
- Exhibit.connection.send(:log, "hello", "world") { }
179
+ Exhibit.lease_connection.send(:log, "hello", "world") { }
180
180
  end
181
181
 
182
182
  x.report "AR.execute(query)" do
183
- ActiveRecord::Base.connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
183
+ ActiveRecord::Base.lease_connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
184
184
  end
185
185
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  # See ActiveRecord::Aggregations::ClassMethods for documentation
5
5
  module Aggregations
6
6
  def initialize_dup(*) # :nodoc:
7
- @aggregation_cache = {}
7
+ @aggregation_cache = @aggregation_cache.dup
8
8
  super
9
9
  end
10
10
 
@@ -19,10 +19,12 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def init_internals
22
- @aggregation_cache = {}
23
22
  super
23
+ @aggregation_cache = {}
24
24
  end
25
25
 
26
+ # = Active Record \Aggregations
27
+ #
26
28
  # Active Record implements aggregation through a macro-like class method called #composed_of
27
29
  # for representing attributes as value objects. It expresses relationships like "Account [is]
28
30
  # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
@@ -32,8 +34,8 @@ module ActiveRecord
32
34
  # the database).
33
35
  #
34
36
  # class Customer < ActiveRecord::Base
35
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
36
- # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
37
+ # composed_of :balance, class_name: "Money", mapping: { balance: :amount }
38
+ # composed_of :address, mapping: { address_street: :street, address_city: :city }
37
39
  # end
38
40
  #
39
41
  # The customer class now has the following methods to manipulate the value objects:
@@ -150,7 +152,7 @@ module ActiveRecord
150
152
  # class NetworkResource < ActiveRecord::Base
151
153
  # composed_of :cidr,
152
154
  # class_name: 'NetAddr::CIDR',
153
- # mapping: [ %w(network_address network), %w(cidr_range bits) ],
155
+ # mapping: { network_address: :network, cidr_range: :bits },
154
156
  # allow_nil: true,
155
157
  # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
156
158
  # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
@@ -188,10 +190,10 @@ module ActiveRecord
188
190
  # to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
189
191
  # with this option.
190
192
  # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
191
- # object. Each mapping is represented as an array where the first item is the name of the
192
- # entity attribute and the second item is the name of the attribute in the value object. The
193
+ # object. Each mapping is represented as a key-value pair where the key is the name of the
194
+ # entity attribute and the value is the name of the attribute in the value object. The
193
195
  # order in which mappings are defined determines the order in which attributes are sent to the
194
- # value class constructor.
196
+ # value class constructor. The mapping can be written as a hash or as an array of pairs.
195
197
  # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
196
198
  # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
197
199
  # mapped attributes.
@@ -208,14 +210,15 @@ module ActiveRecord
208
210
  # can return +nil+ to skip the assignment.
209
211
  #
210
212
  # Option examples:
211
- # composed_of :temperature, mapping: %w(reading celsius)
212
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
213
+ # composed_of :temperature, mapping: { reading: :celsius }
214
+ # composed_of :balance, class_name: "Money", mapping: { balance: :amount }
215
+ # composed_of :address, mapping: { address_street: :street, address_city: :city }
213
216
  # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
214
217
  # composed_of :gps_location
215
218
  # composed_of :gps_location, allow_nil: true
216
219
  # composed_of :ip_address,
217
220
  # class_name: 'IPAddr',
218
- # mapping: %w(ip to_i),
221
+ # mapping: { ip: :to_i },
219
222
  # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
220
223
  # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
221
224
  #
@@ -249,7 +252,7 @@ module ActiveRecord
249
252
  object = constructor.respond_to?(:call) ?
250
253
  constructor.call(*attrs) :
251
254
  class_name.constantize.send(constructor, *attrs)
252
- @aggregation_cache[name] = object
255
+ @aggregation_cache[name] = object.freeze
253
256
  end
254
257
  @aggregation_cache[name]
255
258
  end
@@ -264,7 +267,7 @@ module ActiveRecord
264
267
  end
265
268
 
266
269
  hash_from_multiparameter_assignment = part.is_a?(Hash) &&
267
- part.each_key.all? { |k| k.is_a?(Integer) }
270
+ part.keys.all?(Integer)
268
271
  if hash_from_multiparameter_assignment
269
272
  raise ArgumentError unless part.size == part.each_key.max
270
273
  part = klass.new(*part.sort.map(&:last))
@@ -275,7 +278,7 @@ module ActiveRecord
275
278
  @aggregation_cache[name] = nil
276
279
  else
277
280
  mapping.each { |key, value| write_attribute(key, part.send(value)) }
278
- @aggregation_cache[name] = part.freeze
281
+ @aggregation_cache[name] = part.dup.freeze
279
282
  end
280
283
  end
281
284
  end
@@ -16,27 +16,17 @@ module ActiveRecord
16
16
  end
17
17
 
18
18
  %w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
19
- class_eval <<~RUBY
19
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
20
20
  def #{method}(attributes, **kwargs)
21
21
  if @association.reflection.through_reflection?
22
22
  raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
23
23
  end
24
24
 
25
- scoping { klass.#{method}(attributes, **kwargs) }
25
+ super
26
26
  end
27
27
  RUBY
28
28
  end
29
29
 
30
- def build(attributes = nil, &block)
31
- if attributes.is_a?(Array)
32
- attributes.collect { |attr| build(attr, &block) }
33
- else
34
- block = current_scope_restoring_block(&block)
35
- scoping { _new(attributes, &block) }
36
- end
37
- end
38
- alias new build
39
-
40
30
  private
41
31
  def _new(attributes, &block)
42
32
  @association.build(attributes, &block)
@@ -6,21 +6,23 @@ module ActiveRecord
6
6
  module Associations
7
7
  # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
8
8
  class AliasTracker # :nodoc:
9
- def self.create(connection, initial_table, joins, aliases = nil)
10
- if joins.empty?
11
- aliases ||= Hash.new(0)
12
- elsif aliases
13
- default_proc = aliases.default_proc || proc { 0 }
14
- aliases.default_proc = proc { |h, k|
15
- h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
16
- }
17
- else
18
- aliases = Hash.new { |h, k|
19
- h[k] = initial_count_for(connection, k, joins)
20
- }
9
+ def self.create(pool, initial_table, joins, aliases = nil)
10
+ pool.with_connection do |connection|
11
+ if joins.empty?
12
+ aliases ||= Hash.new(0)
13
+ elsif aliases
14
+ default_proc = aliases.default_proc || proc { 0 }
15
+ aliases.default_proc = proc { |h, k|
16
+ h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
17
+ }
18
+ else
19
+ aliases = Hash.new { |h, k|
20
+ h[k] = initial_count_for(connection, k, joins)
21
+ }
22
+ end
23
+ aliases[initial_table] = 1
24
+ new(connection.table_alias_length, aliases)
21
25
  end
22
- aliases[initial_table] = 1
23
- new(connection, aliases)
24
26
  end
25
27
 
26
28
  def self.initial_count_for(connection, name, table_joins)
@@ -46,9 +48,9 @@ module ActiveRecord
46
48
  end
47
49
 
48
50
  # table_joins is an array of arel joins which might conflict with the aliases we assign here
49
- def initialize(connection, aliases)
50
- @aliases = aliases
51
- @connection = connection
51
+ def initialize(table_alias_length, aliases)
52
+ @aliases = aliases
53
+ @table_alias_length = table_alias_length
52
54
  end
53
55
 
54
56
  def aliased_table_for(arel_table, table_name = nil)
@@ -60,7 +62,7 @@ module ActiveRecord
60
62
  arel_table = arel_table.alias(table_name) if arel_table.name != table_name
61
63
  else
62
64
  # Otherwise, we need to use an alias
63
- aliased_name = @connection.table_alias_for(yield)
65
+ aliased_name = table_alias_for(yield)
64
66
 
65
67
  # Update the count
66
68
  count = aliases[aliased_name] += 1
@@ -76,8 +78,12 @@ module ActiveRecord
76
78
  attr_reader :aliases
77
79
 
78
80
  private
81
+ def table_alias_for(table_name)
82
+ table_name[0...@table_alias_length].tr(".", "_")
83
+ end
84
+
79
85
  def truncate(name)
80
- name.slice(0, @connection.table_alias_length - 2)
86
+ name.slice(0, @table_alias_length - 2)
81
87
  end
82
88
  end
83
89
  end
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  # Associations in Active Record are middlemen between the object that
20
20
  # holds the association, known as the <tt>owner</tt>, and the associated
21
21
  # result set, known as the <tt>target</tt>. Association metadata is available in
22
- # <tt>reflection</tt>, which is an instance of <tt>ActiveRecord::Reflection::AssociationReflection</tt>.
22
+ # <tt>reflection</tt>, which is an instance of +ActiveRecord::Reflection::AssociationReflection+.
23
23
  #
24
24
  # For example, given
25
25
  #
@@ -32,8 +32,9 @@ module ActiveRecord
32
32
  # The association of <tt>blog.posts</tt> has the object +blog+ as its
33
33
  # <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
34
34
  # the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
35
- class Association #:nodoc:
36
- attr_reader :owner, :target, :reflection
35
+ class Association # :nodoc:
36
+ attr_accessor :owner
37
+ attr_reader :target, :reflection, :disable_joins
37
38
 
38
39
  delegate :options, to: :reflection
39
40
 
@@ -41,17 +42,18 @@ module ActiveRecord
41
42
  reflection.check_validity!
42
43
 
43
44
  @owner, @reflection = owner, reflection
45
+ @disable_joins = @reflection.options[:disable_joins] || false
44
46
 
45
47
  reset
46
48
  reset_scope
49
+
50
+ @skip_strict_loading = nil
47
51
  end
48
52
 
49
53
  # Resets the \loaded flag to +false+ and sets the \target to +nil+.
50
54
  def reset
51
55
  @loaded = false
52
- @target = nil
53
56
  @stale_state = nil
54
- @inversed = false
55
57
  end
56
58
 
57
59
  def reset_negative_cache # :nodoc:
@@ -61,7 +63,7 @@ module ActiveRecord
61
63
  # Reloads the \target and returns +self+ on success.
62
64
  # The QueryCache is cleared if +force+ is true.
63
65
  def reload(force = false)
64
- klass.connection.clear_query_cache if force && klass
66
+ klass.connection_pool.clear_query_cache if force && klass
65
67
  reset
66
68
  reset_scope
67
69
  load_target
@@ -77,7 +79,6 @@ module ActiveRecord
77
79
  def loaded!
78
80
  @loaded = true
79
81
  @stale_state = stale_state
80
- @inversed = false
81
82
  end
82
83
 
83
84
  # The target is stale if the target no longer points to the record(s) that the
@@ -87,7 +88,7 @@ module ActiveRecord
87
88
  #
88
89
  # Note that if the target has not been loaded, it is not considered stale.
89
90
  def stale_target?
90
- !@inversed && loaded? && @stale_state != stale_state
91
+ loaded? && @stale_state != stale_state
91
92
  end
92
93
 
93
94
  # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
@@ -97,8 +98,12 @@ module ActiveRecord
97
98
  end
98
99
 
99
100
  def scope
100
- if (scope = klass.current_scope) && scope.try(:proxy_association) == self
101
+ if disable_joins
102
+ DisableJoinsAssociationScope.create.scope(self)
103
+ elsif (scope = klass.current_scope) && scope.try(:proxy_association) == self
101
104
  scope.spawn
105
+ elsif scope = klass.global_current_scope
106
+ target_scope.merge!(association_scope).merge!(scope)
102
107
  else
103
108
  target_scope.merge!(association_scope)
104
109
  end
@@ -132,15 +137,11 @@ module ActiveRecord
132
137
 
133
138
  def inversed_from(record)
134
139
  self.target = record
135
- @inversed = !!record
136
140
  end
137
141
 
138
142
  def inversed_from_queries(record)
139
143
  if inversable?(record)
140
144
  self.target = record
141
- @inversed = true
142
- else
143
- @inversed = false
144
145
  end
145
146
  end
146
147
 
@@ -191,7 +192,7 @@ module ActiveRecord
191
192
  @reflection = @owner.class._reflect_on_association(reflection_name)
192
193
  end
193
194
 
194
- def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
195
+ def initialize_attributes(record, except_from_scope_attributes = nil) # :nodoc:
195
196
  except_from_scope_attributes ||= {}
196
197
  skip_assign = [reflection.foreign_key, reflection.type].compact
197
198
  assigned_keys = record.changed_attribute_names_to_save
@@ -209,9 +210,21 @@ module ActiveRecord
209
210
  _create_record(attributes, true, &block)
210
211
  end
211
212
 
213
+ # Whether the association represent a single record
214
+ # or a collection of records.
215
+ def collection?
216
+ false
217
+ end
218
+
212
219
  private
220
+ # Reader and writer methods call this so that consistent errors are presented
221
+ # when the association target class does not exist.
222
+ def ensure_klass_exists!
223
+ klass
224
+ end
225
+
213
226
  def find_target
214
- if strict_loading? && owner.validation_context.nil?
227
+ if violates_strict_loading?
215
228
  Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
216
229
  end
217
230
 
@@ -224,13 +237,34 @@ module ActiveRecord
224
237
  end
225
238
 
226
239
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
227
- sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
240
+ klass.with_connection do |c|
241
+ sc.execute(binds, c) do |record|
242
+ set_inverse_instance(record)
243
+ if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
244
+ record.strict_loading!
245
+ else
246
+ record.strict_loading!(false, mode: owner.strict_loading_mode)
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ def skip_strict_loading(&block)
253
+ skip_strict_loading_was = @skip_strict_loading
254
+ @skip_strict_loading = true
255
+ yield
256
+ ensure
257
+ @skip_strict_loading = skip_strict_loading_was
228
258
  end
229
259
 
230
- def strict_loading?
260
+ def violates_strict_loading?
261
+ return if @skip_strict_loading
262
+
263
+ return unless owner.validation_context.nil?
264
+
231
265
  return reflection.strict_loading? if reflection.options.key?(:strict_loading)
232
266
 
233
- owner.strict_loading?
267
+ owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
234
268
  end
235
269
 
236
270
  # The scope for this association.
@@ -241,7 +275,11 @@ module ActiveRecord
241
275
  # actually gets built.
242
276
  def association_scope
243
277
  if klass
244
- @association_scope ||= AssociationScope.scope(self)
278
+ @association_scope ||= if disable_joins
279
+ DisableJoinsAssociationScope.scope(self)
280
+ else
281
+ AssociationScope.scope(self)
282
+ end
245
283
  end
246
284
  end
247
285
 
@@ -273,7 +311,7 @@ module ActiveRecord
273
311
 
274
312
  # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
275
313
  # the kind of the class of the associated objects. Meant to be used as
276
- # a sanity check when you are about to assign an associated record.
314
+ # a safety check when you are about to assign an associated record.
277
315
  def raise_on_type_mismatch!(record)
278
316
  unless record.is_a?(reflection.klass)
279
317
  fresh_class = reflection.class_name.safe_constantize
@@ -306,7 +344,8 @@ module ActiveRecord
306
344
 
307
345
  # Returns true if record contains the foreign_key
308
346
  def foreign_key_for?(record)
309
- record._has_attribute?(reflection.foreign_key)
347
+ foreign_key = Array(reflection.foreign_key)
348
+ foreign_key.all? { |key| record._has_attribute?(key) }
310
349
  end
311
350
 
312
351
  # This should be implemented to return the values of the relevant key(s) on the owner,
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
5
- class AssociationScope #:nodoc:
5
+ class AssociationScope # :nodoc:
6
6
  def self.scope(association)
7
7
  INSTANCE.scope(association)
8
8
  end
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  binds = []
36
36
  last_reflection = chain.last
37
37
 
38
- binds << last_reflection.join_id_for(owner)
38
+ binds.push(*last_reflection.join_id_for(owner))
39
39
  if last_reflection.type
40
40
  binds << owner.class.polymorphic_name
41
41
  end
@@ -56,12 +56,15 @@ module ActiveRecord
56
56
  end
57
57
 
58
58
  def last_chain_scope(scope, reflection, owner)
59
- primary_key = reflection.join_primary_key
60
- foreign_key = reflection.join_foreign_key
59
+ primary_key = Array(reflection.join_primary_key)
60
+ foreign_key = Array(reflection.join_foreign_key)
61
61
 
62
62
  table = reflection.aliased_table
63
- value = transform_value(owner[foreign_key])
64
- scope = apply_scope(scope, table, primary_key, value)
63
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
64
+ primary_key_foreign_key_pairs.each do |join_key, foreign_key|
65
+ value = transform_value(owner._read_attribute(foreign_key))
66
+ scope = apply_scope(scope, table, join_key, value)
67
+ end
65
68
 
66
69
  if reflection.type
67
70
  polymorphic_type = transform_value(owner.class.polymorphic_name)
@@ -76,19 +79,23 @@ module ActiveRecord
76
79
  end
77
80
 
78
81
  def next_chain_scope(scope, reflection, next_reflection)
79
- primary_key = reflection.join_primary_key
80
- foreign_key = reflection.join_foreign_key
82
+ primary_key = Array(reflection.join_primary_key)
83
+ foreign_key = Array(reflection.join_foreign_key)
81
84
 
82
85
  table = reflection.aliased_table
83
86
  foreign_table = next_reflection.aliased_table
84
- constraint = table[primary_key].eq(foreign_table[foreign_key])
87
+
88
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
89
+ constraints = primary_key_foreign_key_pairs.map do |join_primary_key, foreign_key|
90
+ table[join_primary_key].eq(foreign_table[foreign_key])
91
+ end.inject(&:and)
85
92
 
86
93
  if reflection.type
87
94
  value = transform_value(next_reflection.klass.polymorphic_name)
88
95
  scope = apply_scope(scope, table, reflection.type, value)
89
96
  end
90
97
 
91
- scope.joins!(join(foreign_table, constraint))
98
+ scope.joins!(join(foreign_table, constraints))
92
99
  end
93
100
 
94
101
  class ReflectionProxy < SimpleDelegator # :nodoc:
@@ -123,8 +130,6 @@ module ActiveRecord
123
130
 
124
131
  chain_head = chain.first
125
132
  chain.reverse_each do |reflection|
126
- # Exclude the scope of the association itself, because that
127
- # was already merged in the #scope method.
128
133
  reflection.constraints.each do |scope_chain_item|
129
134
  item = eval_scope(reflection, scope_chain_item, owner)
130
135