activerecord 5.2.8 → 7.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (364) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1393 -587
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +10 -9
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +122 -47
  10. data/lib/active_record/associations/association_scope.rb +24 -24
  11. data/lib/active_record/associations/belongs_to_association.rb +67 -49
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
  13. data/lib/active_record/associations/builder/association.rb +52 -23
  14. data/lib/active_record/associations/builder/belongs_to.rb +44 -61
  15. data/lib/active_record/associations/builder/collection_association.rb +17 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +10 -3
  18. data/lib/active_record/associations/builder/has_one.rb +35 -3
  19. data/lib/active_record/associations/builder/singular_association.rb +5 -3
  20. data/lib/active_record/associations/collection_association.rb +59 -50
  21. data/lib/active_record/associations/collection_proxy.rb +32 -23
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +27 -14
  25. data/lib/active_record/associations/has_many_through_association.rb +26 -19
  26. data/lib/active_record/associations/has_one_association.rb +52 -37
  27. data/lib/active_record/associations/has_one_through_association.rb +6 -6
  28. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +97 -62
  31. data/lib/active_record/associations/preloader/association.rb +220 -60
  32. data/lib/active_record/associations/preloader/batch.rb +48 -0
  33. data/lib/active_record/associations/preloader/branch.rb +147 -0
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -40
  35. data/lib/active_record/associations/preloader.rb +44 -105
  36. data/lib/active_record/associations/singular_association.rb +9 -17
  37. data/lib/active_record/associations/through_association.rb +4 -4
  38. data/lib/active_record/associations.rb +207 -66
  39. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  40. data/lib/active_record/attribute_assignment.rb +17 -19
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
  42. data/lib/active_record/attribute_methods/dirty.rb +141 -47
  43. data/lib/active_record/attribute_methods/primary_key.rb +22 -27
  44. data/lib/active_record/attribute_methods/query.rb +6 -10
  45. data/lib/active_record/attribute_methods/read.rb +15 -55
  46. data/lib/active_record/attribute_methods/serialization.rb +77 -18
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
  48. data/lib/active_record/attribute_methods/write.rb +18 -37
  49. data/lib/active_record/attribute_methods.rb +90 -153
  50. data/lib/active_record/attributes.rb +38 -12
  51. data/lib/active_record/autosave_association.rb +50 -50
  52. data/lib/active_record/base.rb +23 -18
  53. data/lib/active_record/callbacks.rb +159 -44
  54. data/lib/active_record/coders/yaml_column.rb +12 -3
  55. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
  63. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
  71. data/lib/active_record/connection_adapters/column.rb +33 -11
  72. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  73. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  74. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  75. data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
  76. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  77. data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
  78. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  80. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
  81. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
  82. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  83. data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
  84. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  85. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
  95. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
  116. data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
  122. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
  123. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  124. data/lib/active_record/connection_adapters.rb +53 -0
  125. data/lib/active_record/connection_handling.rb +292 -38
  126. data/lib/active_record/core.rb +385 -158
  127. data/lib/active_record/counter_cache.rb +8 -30
  128. data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
  129. data/lib/active_record/database_configurations/database_config.rb +83 -0
  130. data/lib/active_record/database_configurations/hash_config.rb +154 -0
  131. data/lib/active_record/database_configurations/url_config.rb +53 -0
  132. data/lib/active_record/database_configurations.rb +256 -0
  133. data/lib/active_record/delegated_type.rb +250 -0
  134. data/lib/active_record/destroy_association_async_job.rb +36 -0
  135. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  136. data/lib/active_record/dynamic_matchers.rb +4 -5
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +44 -0
  140. data/lib/active_record/encryption/configurable.rb +61 -0
  141. data/lib/active_record/encryption/context.rb +35 -0
  142. data/lib/active_record/encryption/contexts.rb +72 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -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 +155 -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 +160 -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 +42 -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_serializer.rb +90 -0
  159. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  160. data/lib/active_record/encryption/properties.rb +76 -0
  161. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  162. data/lib/active_record/encryption/scheme.rb +99 -0
  163. data/lib/active_record/encryption.rb +55 -0
  164. data/lib/active_record/enum.rb +130 -51
  165. data/lib/active_record/errors.rb +129 -23
  166. data/lib/active_record/explain.rb +10 -6
  167. data/lib/active_record/explain_registry.rb +11 -6
  168. data/lib/active_record/explain_subscriber.rb +1 -1
  169. data/lib/active_record/fixture_set/file.rb +22 -15
  170. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  171. data/lib/active_record/fixture_set/render_context.rb +17 -0
  172. data/lib/active_record/fixture_set/table_row.rb +187 -0
  173. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  174. data/lib/active_record/fixtures.rb +206 -490
  175. data/lib/active_record/future_result.rb +139 -0
  176. data/lib/active_record/gem_version.rb +3 -3
  177. data/lib/active_record/inheritance.rb +104 -37
  178. data/lib/active_record/insert_all.rb +278 -0
  179. data/lib/active_record/integration.rb +69 -18
  180. data/lib/active_record/internal_metadata.rb +24 -9
  181. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  182. data/lib/active_record/locking/optimistic.rb +41 -26
  183. data/lib/active_record/locking/pessimistic.rb +18 -8
  184. data/lib/active_record/log_subscriber.rb +46 -35
  185. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  186. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  187. data/lib/active_record/middleware/database_selector.rb +82 -0
  188. data/lib/active_record/middleware/shard_selector.rb +60 -0
  189. data/lib/active_record/migration/command_recorder.rb +96 -44
  190. data/lib/active_record/migration/compatibility.rb +246 -64
  191. data/lib/active_record/migration/join_table.rb +1 -2
  192. data/lib/active_record/migration.rb +266 -187
  193. data/lib/active_record/model_schema.rb +165 -52
  194. data/lib/active_record/nested_attributes.rb +17 -19
  195. data/lib/active_record/no_touching.rb +11 -4
  196. data/lib/active_record/null_relation.rb +2 -7
  197. data/lib/active_record/persistence.rb +467 -92
  198. data/lib/active_record/query_cache.rb +21 -4
  199. data/lib/active_record/query_logs.rb +138 -0
  200. data/lib/active_record/querying.rb +51 -24
  201. data/lib/active_record/railtie.rb +224 -57
  202. data/lib/active_record/railties/console_sandbox.rb +2 -4
  203. data/lib/active_record/railties/controller_runtime.rb +31 -36
  204. data/lib/active_record/railties/databases.rake +369 -101
  205. data/lib/active_record/readonly_attributes.rb +15 -0
  206. data/lib/active_record/reflection.rb +170 -137
  207. data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
  208. data/lib/active_record/relation/batches.rb +46 -37
  209. data/lib/active_record/relation/calculations.rb +168 -96
  210. data/lib/active_record/relation/delegation.rb +37 -52
  211. data/lib/active_record/relation/finder_methods.rb +79 -58
  212. data/lib/active_record/relation/from_clause.rb +5 -1
  213. data/lib/active_record/relation/merger.rb +50 -51
  214. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  215. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  216. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  217. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  218. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  219. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  220. data/lib/active_record/relation/predicate_builder.rb +58 -46
  221. data/lib/active_record/relation/query_attribute.rb +9 -10
  222. data/lib/active_record/relation/query_methods.rb +685 -208
  223. data/lib/active_record/relation/record_fetch_warning.rb +9 -11
  224. data/lib/active_record/relation/spawn_methods.rb +10 -10
  225. data/lib/active_record/relation/where_clause.rb +108 -64
  226. data/lib/active_record/relation.rb +515 -151
  227. data/lib/active_record/result.rb +78 -42
  228. data/lib/active_record/runtime_registry.rb +9 -13
  229. data/lib/active_record/sanitization.rb +29 -44
  230. data/lib/active_record/schema.rb +37 -31
  231. data/lib/active_record/schema_dumper.rb +74 -23
  232. data/lib/active_record/schema_migration.rb +7 -9
  233. data/lib/active_record/scoping/default.rb +62 -17
  234. data/lib/active_record/scoping/named.rb +17 -32
  235. data/lib/active_record/scoping.rb +70 -41
  236. data/lib/active_record/secure_token.rb +16 -8
  237. data/lib/active_record/serialization.rb +6 -4
  238. data/lib/active_record/signed_id.rb +116 -0
  239. data/lib/active_record/statement_cache.rb +49 -6
  240. data/lib/active_record/store.rb +88 -9
  241. data/lib/active_record/suppressor.rb +13 -17
  242. data/lib/active_record/table_metadata.rb +42 -43
  243. data/lib/active_record/tasks/database_tasks.rb +352 -94
  244. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  245. data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
  246. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  247. data/lib/active_record/test_databases.rb +24 -0
  248. data/lib/active_record/test_fixtures.rb +287 -0
  249. data/lib/active_record/timestamp.rb +44 -34
  250. data/lib/active_record/touch_later.rb +23 -22
  251. data/lib/active_record/transactions.rb +67 -128
  252. data/lib/active_record/translation.rb +3 -3
  253. data/lib/active_record/type/adapter_specific_registry.rb +34 -19
  254. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  255. data/lib/active_record/type/internal/timezone.rb +2 -2
  256. data/lib/active_record/type/serialized.rb +7 -4
  257. data/lib/active_record/type/time.rb +10 -0
  258. data/lib/active_record/type/type_map.rb +17 -21
  259. data/lib/active_record/type/unsigned_integer.rb +0 -1
  260. data/lib/active_record/type.rb +9 -5
  261. data/lib/active_record/type_caster/connection.rb +15 -15
  262. data/lib/active_record/type_caster/map.rb +8 -8
  263. data/lib/active_record/validations/associated.rb +2 -3
  264. data/lib/active_record/validations/numericality.rb +35 -0
  265. data/lib/active_record/validations/uniqueness.rb +39 -31
  266. data/lib/active_record/validations.rb +4 -3
  267. data/lib/active_record.rb +209 -32
  268. data/lib/arel/alias_predication.rb +9 -0
  269. data/lib/arel/attributes/attribute.rb +33 -0
  270. data/lib/arel/collectors/bind.rb +29 -0
  271. data/lib/arel/collectors/composite.rb +39 -0
  272. data/lib/arel/collectors/plain_string.rb +20 -0
  273. data/lib/arel/collectors/sql_string.rb +27 -0
  274. data/lib/arel/collectors/substitute_binds.rb +35 -0
  275. data/lib/arel/crud.rb +48 -0
  276. data/lib/arel/delete_manager.rb +32 -0
  277. data/lib/arel/errors.rb +9 -0
  278. data/lib/arel/expressions.rb +29 -0
  279. data/lib/arel/factory_methods.rb +49 -0
  280. data/lib/arel/filter_predications.rb +9 -0
  281. data/lib/arel/insert_manager.rb +48 -0
  282. data/lib/arel/math.rb +45 -0
  283. data/lib/arel/nodes/and.rb +32 -0
  284. data/lib/arel/nodes/ascending.rb +23 -0
  285. data/lib/arel/nodes/binary.rb +126 -0
  286. data/lib/arel/nodes/bind_param.rb +44 -0
  287. data/lib/arel/nodes/case.rb +55 -0
  288. data/lib/arel/nodes/casted.rb +62 -0
  289. data/lib/arel/nodes/comment.rb +29 -0
  290. data/lib/arel/nodes/count.rb +12 -0
  291. data/lib/arel/nodes/delete_statement.rb +44 -0
  292. data/lib/arel/nodes/descending.rb +23 -0
  293. data/lib/arel/nodes/equality.rb +15 -0
  294. data/lib/arel/nodes/extract.rb +24 -0
  295. data/lib/arel/nodes/false.rb +16 -0
  296. data/lib/arel/nodes/filter.rb +10 -0
  297. data/lib/arel/nodes/full_outer_join.rb +8 -0
  298. data/lib/arel/nodes/function.rb +45 -0
  299. data/lib/arel/nodes/grouping.rb +11 -0
  300. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  301. data/lib/arel/nodes/in.rb +15 -0
  302. data/lib/arel/nodes/infix_operation.rb +92 -0
  303. data/lib/arel/nodes/inner_join.rb +8 -0
  304. data/lib/arel/nodes/insert_statement.rb +37 -0
  305. data/lib/arel/nodes/join_source.rb +20 -0
  306. data/lib/arel/nodes/matches.rb +18 -0
  307. data/lib/arel/nodes/named_function.rb +23 -0
  308. data/lib/arel/nodes/node.rb +51 -0
  309. data/lib/arel/nodes/node_expression.rb +13 -0
  310. data/lib/arel/nodes/ordering.rb +27 -0
  311. data/lib/arel/nodes/outer_join.rb +8 -0
  312. data/lib/arel/nodes/over.rb +15 -0
  313. data/lib/arel/nodes/regexp.rb +16 -0
  314. data/lib/arel/nodes/right_outer_join.rb +8 -0
  315. data/lib/arel/nodes/select_core.rb +67 -0
  316. data/lib/arel/nodes/select_statement.rb +41 -0
  317. data/lib/arel/nodes/sql_literal.rb +19 -0
  318. data/lib/arel/nodes/string_join.rb +11 -0
  319. data/lib/arel/nodes/table_alias.rb +31 -0
  320. data/lib/arel/nodes/terminal.rb +16 -0
  321. data/lib/arel/nodes/true.rb +16 -0
  322. data/lib/arel/nodes/unary.rb +44 -0
  323. data/lib/arel/nodes/unary_operation.rb +20 -0
  324. data/lib/arel/nodes/unqualified_column.rb +22 -0
  325. data/lib/arel/nodes/update_statement.rb +46 -0
  326. data/lib/arel/nodes/values_list.rb +9 -0
  327. data/lib/arel/nodes/window.rb +126 -0
  328. data/lib/arel/nodes/with.rb +11 -0
  329. data/lib/arel/nodes.rb +71 -0
  330. data/lib/arel/order_predications.rb +13 -0
  331. data/lib/arel/predications.rb +258 -0
  332. data/lib/arel/select_manager.rb +276 -0
  333. data/lib/arel/table.rb +117 -0
  334. data/lib/arel/tree_manager.rb +60 -0
  335. data/lib/arel/update_manager.rb +48 -0
  336. data/lib/arel/visitors/dot.rb +298 -0
  337. data/lib/arel/visitors/mysql.rb +99 -0
  338. data/lib/arel/visitors/postgresql.rb +110 -0
  339. data/lib/arel/visitors/sqlite.rb +38 -0
  340. data/lib/arel/visitors/to_sql.rb +955 -0
  341. data/lib/arel/visitors/visitor.rb +45 -0
  342. data/lib/arel/visitors.rb +13 -0
  343. data/lib/arel/window_predications.rb +9 -0
  344. data/lib/arel.rb +55 -0
  345. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  346. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  347. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  348. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  349. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  350. data/lib/rails/generators/active_record/migration.rb +19 -2
  351. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  352. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  353. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  354. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  355. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  356. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  357. metadata +162 -32
  358. data/lib/active_record/attribute_decorators.rb +0 -90
  359. data/lib/active_record/collection_cache_key.rb +0 -53
  360. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  361. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  362. data/lib/active_record/define_callbacks.rb +0 -22
  363. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  364. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,42 +1,107 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/enumerable"
4
- require "active_support/core_ext/string/conversions"
5
- require "active_support/core_ext/module/remove_method"
6
- require "active_record/errors"
7
-
8
3
  module ActiveRecord
9
- class AssociationNotFoundError < ConfigurationError #:nodoc:
4
+ class AssociationNotFoundError < ConfigurationError # :nodoc:
5
+ attr_reader :record, :association_name
6
+
10
7
  def initialize(record = nil, association_name = nil)
8
+ @record = record
9
+ @association_name = association_name
11
10
  if record && association_name
12
11
  super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
13
12
  else
14
13
  super("Association was not found.")
15
14
  end
16
15
  end
16
+
17
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
18
+ include DidYouMean::Correctable
19
+
20
+ def corrections
21
+ if record && association_name
22
+ @corrections ||= begin
23
+ maybe_these = record.class.reflections.keys
24
+ DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(association_name)
25
+ end
26
+ else
27
+ []
28
+ end
29
+ end
30
+ end
17
31
  end
18
32
 
19
- class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
33
+ class InverseOfAssociationNotFoundError < ActiveRecordError # :nodoc:
34
+ attr_reader :reflection, :associated_class
35
+
20
36
  def initialize(reflection = nil, associated_class = nil)
21
37
  if reflection
38
+ @reflection = reflection
39
+ @associated_class = associated_class.nil? ? reflection.klass : associated_class
22
40
  super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
23
41
  else
24
42
  super("Could not find the inverse association.")
25
43
  end
26
44
  end
45
+
46
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
47
+ include DidYouMean::Correctable
48
+
49
+ def corrections
50
+ if reflection && associated_class
51
+ @corrections ||= begin
52
+ maybe_these = associated_class.reflections.keys
53
+ DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:inverse_of].to_s)
54
+ end
55
+ else
56
+ []
57
+ end
58
+ end
59
+ end
27
60
  end
28
61
 
29
- class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
30
- def initialize(owner_class_name = nil, reflection = nil)
31
- if owner_class_name && reflection
32
- super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
62
+ class InverseOfAssociationRecursiveError < ActiveRecordError # :nodoc:
63
+ attr_reader :reflection
64
+ def initialize(reflection = nil)
65
+ if reflection
66
+ @reflection = reflection
67
+ super("Inverse association #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name}) is recursive.")
68
+ else
69
+ super("Inverse association is recursive.")
70
+ end
71
+ end
72
+ end
73
+
74
+ class HasManyThroughAssociationNotFoundError < ActiveRecordError # :nodoc:
75
+ attr_reader :owner_class, :reflection
76
+
77
+ def initialize(owner_class = nil, reflection = nil)
78
+ if owner_class && reflection
79
+ @owner_class = owner_class
80
+ @reflection = reflection
81
+ super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
33
82
  else
34
83
  super("Could not find the association.")
35
84
  end
36
85
  end
86
+
87
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
88
+ include DidYouMean::Correctable
89
+
90
+ def corrections
91
+ if owner_class && reflection
92
+ @corrections ||= begin
93
+ maybe_these = owner_class.reflections.keys
94
+ maybe_these -= [reflection.name.to_s] # remove failing reflection
95
+ DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:through].to_s)
96
+ end
97
+ else
98
+ []
99
+ end
100
+ end
101
+ end
37
102
  end
38
103
 
39
- class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
104
+ class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError # :nodoc:
40
105
  def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
41
106
  if owner_class_name && reflection && source_reflection
42
107
  super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
@@ -46,7 +111,7 @@ module ActiveRecord
46
111
  end
47
112
  end
48
113
 
49
- class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
114
+ class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
50
115
  def initialize(owner_class_name = nil, reflection = nil)
51
116
  if owner_class_name && reflection
52
117
  super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
@@ -56,7 +121,7 @@ module ActiveRecord
56
121
  end
57
122
  end
58
123
 
59
- class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
124
+ class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError # :nodoc:
60
125
  def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
61
126
  if owner_class_name && reflection && source_reflection
62
127
  super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
@@ -66,7 +131,7 @@ module ActiveRecord
66
131
  end
67
132
  end
68
133
 
69
- class HasOneThroughCantAssociateThroughCollection < ActiveRecordError #:nodoc:
134
+ class HasOneThroughCantAssociateThroughCollection < ActiveRecordError # :nodoc:
70
135
  def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
71
136
  if owner_class_name && reflection && through_reflection
72
137
  super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
@@ -76,7 +141,7 @@ module ActiveRecord
76
141
  end
77
142
  end
78
143
 
79
- class HasOneAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
144
+ class HasOneAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
80
145
  def initialize(owner_class_name = nil, reflection = nil)
81
146
  if owner_class_name && reflection
82
147
  super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
@@ -86,20 +151,20 @@ module ActiveRecord
86
151
  end
87
152
  end
88
153
 
89
- class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
154
+ class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError # :nodoc:
90
155
  def initialize(reflection = nil)
91
156
  if reflection
92
157
  through_reflection = reflection.through_reflection
93
158
  source_reflection_names = reflection.source_reflection_names
94
159
  source_associations = reflection.through_reflection.klass._reflections.keys
95
- super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ', locale: :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ', locale: :en)}?")
160
+ super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
96
161
  else
97
162
  super("Could not find the source association(s).")
98
163
  end
99
164
  end
100
165
  end
101
166
 
102
- class HasManyThroughOrderError < ActiveRecordError #:nodoc:
167
+ class HasManyThroughOrderError < ActiveRecordError # :nodoc:
103
168
  def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
104
169
  if owner_class_name && reflection && through_reflection
105
170
  super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through '#{owner_class_name}##{through_reflection.name}' before the through association is defined.")
@@ -109,7 +174,7 @@ module ActiveRecord
109
174
  end
110
175
  end
111
176
 
112
- class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc:
177
+ class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError # :nodoc:
113
178
  def initialize(owner = nil, reflection = nil)
114
179
  if owner && reflection
115
180
  super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
@@ -134,13 +199,13 @@ module ActiveRecord
134
199
  end
135
200
  end
136
201
 
137
- class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection #:nodoc:
202
+ class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
138
203
  end
139
204
 
140
- class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection #:nodoc:
205
+ class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
141
206
  end
142
207
 
143
- class ThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc:
208
+ class ThroughNestedAssociationsAreReadonly < ActiveRecordError # :nodoc:
144
209
  def initialize(owner = nil, reflection = nil)
145
210
  if owner && reflection
146
211
  super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
@@ -150,10 +215,10 @@ module ActiveRecord
150
215
  end
151
216
  end
152
217
 
153
- class HasManyThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly #:nodoc:
218
+ class HasManyThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
154
219
  end
155
220
 
156
- class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly #:nodoc:
221
+ class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
157
222
  end
158
223
 
159
224
  # This error is raised when trying to eager load a polymorphic association using a JOIN.
@@ -172,7 +237,7 @@ module ActiveRecord
172
237
  # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
173
238
  # (has_many, has_one) when there is at least 1 child associated instance.
174
239
  # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
175
- class DeleteRestrictionError < ActiveRecordError #:nodoc:
240
+ class DeleteRestrictionError < ActiveRecordError # :nodoc:
176
241
  def initialize(name = nil)
177
242
  if name
178
243
  super("Cannot delete record because of dependent #{name}")
@@ -196,7 +261,7 @@ module ActiveRecord
196
261
  autoload :CollectionProxy
197
262
  autoload :ThroughAssociation
198
263
 
199
- module Builder #:nodoc:
264
+ module Builder # :nodoc:
200
265
  autoload :Association, "active_record/associations/builder/association"
201
266
  autoload :SingularAssociation, "active_record/associations/builder/singular_association"
202
267
  autoload :CollectionAssociation, "active_record/associations/builder/collection_association"
@@ -218,16 +283,18 @@ module ActiveRecord
218
283
  autoload :Preloader
219
284
  autoload :JoinDependency
220
285
  autoload :AssociationScope
286
+ autoload :DisableJoinsAssociationScope
221
287
  autoload :AliasTracker
222
288
  end
223
289
 
224
290
  def self.eager_load!
225
291
  super
226
292
  Preloader.eager_load!
293
+ JoinDependency.eager_load!
227
294
  end
228
295
 
229
296
  # Returns the association instance for the given name, instantiating it if it doesn't already exist
230
- def association(name) #:nodoc:
297
+ def association(name) # :nodoc:
231
298
  association = association_instance_get(name)
232
299
 
233
300
  if association.nil?
@@ -250,17 +317,7 @@ module ActiveRecord
250
317
  super
251
318
  end
252
319
 
253
- def reload(*) # :nodoc:
254
- clear_association_cache
255
- super
256
- end
257
-
258
320
  private
259
- # Clears out the association cache.
260
- def clear_association_cache
261
- @association_cache.clear if persisted?
262
- end
263
-
264
321
  def init_internals
265
322
  @association_cache = {}
266
323
  super
@@ -292,13 +349,13 @@ module ActiveRecord
292
349
  #
293
350
  # The project class now has the following methods (and more) to ease the traversal and
294
351
  # manipulation of its relationships:
295
- # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
296
- # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
297
- # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
298
- # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
299
- # <tt>Project#milestones.build, Project#milestones.create</tt>
300
- # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
301
- # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
352
+ # * <tt>Project#portfolio</tt>, <tt>Project#portfolio=(portfolio)</tt>, <tt>Project#reload_portfolio</tt>
353
+ # * <tt>Project#project_manager</tt>, <tt>Project#project_manager=(project_manager)</tt>, <tt>Project#reload_project_manager</tt>
354
+ # * <tt>Project#milestones.empty?</tt>, <tt>Project#milestones.size</tt>, <tt>Project#milestones</tt>, <tt>Project#milestones<<(milestone)</tt>,
355
+ # <tt>Project#milestones.delete(milestone)</tt>, <tt>Project#milestones.destroy(milestone)</tt>, <tt>Project#milestones.find(milestone_id)</tt>,
356
+ # <tt>Project#milestones.build</tt>, <tt>Project#milestones.create</tt>
357
+ # * <tt>Project#categories.empty?</tt>, <tt>Project#categories.size</tt>, <tt>Project#categories</tt>, <tt>Project#categories<<(category1)</tt>,
358
+ # <tt>Project#categories.delete(category1)</tt>, <tt>Project#categories.destroy(category1)</tt>
302
359
  #
303
360
  # === A word of warning
304
361
  #
@@ -320,6 +377,8 @@ module ActiveRecord
320
377
  # create_other(attributes={}) | X | | X
321
378
  # create_other!(attributes={}) | X | | X
322
379
  # reload_other | X | X | X
380
+ # other_changed? | X | X |
381
+ # other_previously_changed? | X | X |
323
382
  #
324
383
  # === Collection associations (one-to-many / many-to-many)
325
384
  # | | | has_many
@@ -536,19 +595,27 @@ module ActiveRecord
536
595
  # you can also define callbacks that get triggered when you add an object to or remove an
537
596
  # object from an association collection.
538
597
  #
539
- # class Project
540
- # has_and_belongs_to_many :developers, after_add: :evaluate_velocity
598
+ # class Firm < ActiveRecord::Base
599
+ # has_many :clients,
600
+ # dependent: :destroy,
601
+ # after_add: :congratulate_client,
602
+ # after_remove: :log_after_remove
541
603
  #
542
- # def evaluate_velocity(developer)
543
- # ...
604
+ # def congratulate_client(record)
605
+ # # ...
606
+ # end
607
+ #
608
+ # def log_after_remove(record)
609
+ # # ...
544
610
  # end
545
- # end
546
611
  #
547
612
  # It's possible to stack callbacks by passing them as an array. Example:
548
613
  #
549
- # class Project
550
- # has_and_belongs_to_many :developers,
551
- # after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
614
+ # class Firm < ActiveRecord::Base
615
+ # has_many :clients,
616
+ # dependent: :destroy,
617
+ # after_add: [:congratulate_client, -> (firm, record) { firm.log << "after_adding#{record.id}" }],
618
+ # after_remove: :log_after_remove
552
619
  # end
553
620
  #
554
621
  # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
@@ -559,6 +626,18 @@ module ActiveRecord
559
626
  # Similarly, if any of the +before_remove+ callbacks throw an exception, the object
560
627
  # will not be removed from the collection.
561
628
  #
629
+ # Note: To trigger remove callbacks, you must use +destroy+ / +destroy_all+ methods. For example:
630
+ #
631
+ # * <tt>firm.clients.destroy(client)</tt>
632
+ # * <tt>firm.clients.destroy(*clients)</tt>
633
+ # * <tt>firm.clients.destroy_all</tt>
634
+ #
635
+ # +delete+ / +delete_all+ methods like the following do *not* trigger remove callbacks:
636
+ #
637
+ # * <tt>firm.clients.delete(client)</tt>
638
+ # * <tt>firm.clients.delete(*clients)</tt>
639
+ # * <tt>firm.clients.delete_all</tt>
640
+ #
562
641
  # == Association extensions
563
642
  #
564
643
  # The proxy objects that control the access to associations can be extended through anonymous
@@ -702,9 +781,10 @@ module ActiveRecord
702
781
  # inverse detection only works on #has_many, #has_one, and
703
782
  # #belongs_to associations.
704
783
  #
705
- # Extra options on the associations, as defined in the
706
- # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
707
- # also prevent the association's inverse from being found automatically.
784
+ # <tt>:foreign_key</tt> and <tt>:through</tt> options on the associations
785
+ # will also prevent the association's inverse from being found automatically,
786
+ # as will a custom scopes in some cases. See further details in the
787
+ # {Active Record Associations guide}[https://guides.rubyonrails.org/association_basics.html#bi-directional-associations].
708
788
  #
709
789
  # The automatic guessing of the inverse association uses a heuristic based
710
790
  # on the name of the class, so it may not work for all associations,
@@ -1291,10 +1371,15 @@ module ActiveRecord
1291
1371
  # similar callbacks may affect the <tt>:dependent</tt> behavior, and the
1292
1372
  # <tt>:dependent</tt> behavior may affect other callbacks.
1293
1373
  #
1374
+ # * <tt>nil</tt> do nothing (default).
1294
1375
  # * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
1376
+ # * <tt>:destroy_async</tt> destroys all the associated objects in a background job. <b>WARNING:</b> Do not use
1377
+ # this option if the association is backed by foreign key constraints in your database. The foreign key
1378
+ # constraint actions will occur inside the same transaction that deletes its owner.
1295
1379
  # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
1296
- # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
1297
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records.
1380
+ # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
1381
+ # on polymorphic associations. Callbacks are not executed.
1382
+ # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records.
1298
1383
  # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
1299
1384
  #
1300
1385
  # If using with the <tt>:through</tt> option, the association on the join model must be
@@ -1327,6 +1412,11 @@ module ActiveRecord
1327
1412
  # join model. This allows associated records to be built which will automatically create
1328
1413
  # the appropriate join model records when they are saved. (See the 'Association Join Models'
1329
1414
  # section above.)
1415
+ # [:disable_joins]
1416
+ # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1417
+ # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
1418
+ # due to database limitations. This option is only applicable on `has_many :through` associations as
1419
+ # `has_many` alone do not perform a join.
1330
1420
  # [:source]
1331
1421
  # Specifies the source association name used by #has_many <tt>:through</tt> queries.
1332
1422
  # Only use it if the name cannot be inferred from the association.
@@ -1355,6 +1445,12 @@ module ActiveRecord
1355
1445
  # Specifies a module or array of modules that will be extended into the association object returned.
1356
1446
  # Useful for defining methods on associations, especially when they should be shared between multiple
1357
1447
  # association objects.
1448
+ # [:strict_loading]
1449
+ # When set to +true+, enforces strict loading every time the associated record is loaded through this
1450
+ # association.
1451
+ # [:ensuring_owner_was]
1452
+ # Specifies an instance method to be called on the owner. The method must return true in order for the
1453
+ # associated records to be deleted in a background job.
1358
1454
  #
1359
1455
  # Option examples:
1360
1456
  # has_many :comments, -> { order("posted_on") }
@@ -1365,6 +1461,8 @@ module ActiveRecord
1365
1461
  # has_many :tags, as: :taggable
1366
1462
  # has_many :reports, -> { readonly }
1367
1463
  # has_many :subscribers, through: :subscriptions, source: :user
1464
+ # has_many :subscribers, through: :subscriptions, disable_joins: true
1465
+ # has_many :comments, strict_loading: true
1368
1466
  def has_many(name, scope = nil, **options, &extension)
1369
1467
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1370
1468
  Reflection.add_reflection self, name, reflection
@@ -1434,10 +1532,15 @@ module ActiveRecord
1434
1532
  # Controls what happens to the associated object when
1435
1533
  # its owner is destroyed:
1436
1534
  #
1535
+ # * <tt>nil</tt> do nothing (default).
1437
1536
  # * <tt>:destroy</tt> causes the associated object to also be destroyed
1537
+ # * <tt>:destroy_async</tt> causes the associated object to be destroyed in a background job. <b>WARNING:</b> Do not use
1538
+ # this option if the association is backed by foreign key constraints in your database. The foreign key
1539
+ # constraint actions will occur inside the same transaction that deletes its owner.
1438
1540
  # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
1439
- # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
1440
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
1541
+ # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
1542
+ # on polymorphic associations. Callbacks are not executed.
1543
+ # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record
1441
1544
  # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
1442
1545
  #
1443
1546
  # Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
@@ -1464,8 +1567,21 @@ module ActiveRecord
1464
1567
  # source reflection. You can only use a <tt>:through</tt> query through a #has_one
1465
1568
  # or #belongs_to association on the join model.
1466
1569
  #
1570
+ # If the association on the join model is a #belongs_to, the collection can be modified
1571
+ # and the records on the <tt>:through</tt> model will be automatically created and removed
1572
+ # as appropriate. Otherwise, the collection is read-only, so you should manipulate the
1573
+ # <tt>:through</tt> association directly.
1574
+ #
1467
1575
  # If you are going to modify the association (rather than just read from it), then it is
1468
- # a good idea to set the <tt>:inverse_of</tt> option.
1576
+ # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1577
+ # join model. This allows associated records to be built which will automatically create
1578
+ # the appropriate join model records when they are saved. (See the 'Association Join Models'
1579
+ # section above.)
1580
+ # [:disable_joins]
1581
+ # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1582
+ # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
1583
+ # due to database limitations. This option is only applicable on `has_one :through` associations as
1584
+ # `has_one` alone does not perform a join.
1469
1585
  # [:source]
1470
1586
  # Specifies the source association name used by #has_one <tt>:through</tt> queries.
1471
1587
  # Only use it if the name cannot be inferred from the association.
@@ -1492,6 +1608,11 @@ module ActiveRecord
1492
1608
  # When set to +true+, the association will also have its presence validated.
1493
1609
  # This will validate the association itself, not the id. You can use
1494
1610
  # +:inverse_of+ to avoid an extra query during validation.
1611
+ # [:strict_loading]
1612
+ # Enforces strict loading every time the associated record is loaded through this association.
1613
+ # [:ensuring_owner_was]
1614
+ # Specifies an instance method to be called on the owner. The method must return true in order for the
1615
+ # associated records to be deleted in a background job.
1495
1616
  #
1496
1617
  # Option examples:
1497
1618
  # has_one :credit_card, dependent: :destroy # destroys the associated credit card
@@ -1502,8 +1623,10 @@ module ActiveRecord
1502
1623
  # has_one :attachment, as: :attachable
1503
1624
  # has_one :boss, -> { readonly }
1504
1625
  # has_one :club, through: :membership
1626
+ # has_one :club, through: :membership, disable_joins: true
1505
1627
  # has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
1506
1628
  # has_one :credit_card, required: true
1629
+ # has_one :credit_card, strict_loading: true
1507
1630
  def has_one(name, scope = nil, **options)
1508
1631
  reflection = Builder::HasOne.build(self, name, scope, options)
1509
1632
  Reflection.add_reflection self, name, reflection
@@ -1524,6 +1647,7 @@ module ActiveRecord
1524
1647
  # Returns the associated object. +nil+ is returned if none is found.
1525
1648
  # [association=(associate)]
1526
1649
  # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
1650
+ # No modification or deletion of existing records takes place.
1527
1651
  # [build_association(attributes = {})]
1528
1652
  # Returns a new object of the associated type that has been instantiated
1529
1653
  # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
@@ -1536,6 +1660,10 @@ module ActiveRecord
1536
1660
  # if the record is invalid.
1537
1661
  # [reload_association]
1538
1662
  # Returns the associated object, forcing a database read.
1663
+ # [association_changed?]
1664
+ # Returns true if a new associate object has been assigned and the next save will update the foreign key.
1665
+ # [association_previously_changed?]
1666
+ # Returns true if the previous save updated the association to reference a new associate object.
1539
1667
  #
1540
1668
  # === Example
1541
1669
  #
@@ -1546,6 +1674,8 @@ module ActiveRecord
1546
1674
  # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
1547
1675
  # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
1548
1676
  # * <tt>Post#reload_author</tt>
1677
+ # * <tt>Post#author_changed?</tt>
1678
+ # * <tt>Post#author_previously_changed?</tt>
1549
1679
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1550
1680
  #
1551
1681
  # === Scopes
@@ -1581,10 +1711,11 @@ module ActiveRecord
1581
1711
  # association will use "taggable_type" as the default <tt>:foreign_type</tt>.
1582
1712
  # [:primary_key]
1583
1713
  # Specify the method that returns the primary key of associated object used for the association.
1584
- # By default this is id.
1714
+ # By default this is +id+.
1585
1715
  # [:dependent]
1586
1716
  # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
1587
- # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
1717
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to
1718
+ # <tt>:destroy_async</tt>, the associated object is scheduled to be destroyed in a background job.
1588
1719
  # This option should not be specified when #belongs_to is used in conjunction with
1589
1720
  # a #has_many relationship on another class because of the potential to leave
1590
1721
  # orphaned records behind.
@@ -1636,6 +1767,11 @@ module ActiveRecord
1636
1767
  # [:default]
1637
1768
  # Provide a callable (i.e. proc or lambda) to specify that the association should
1638
1769
  # be initialized with a particular record before validation.
1770
+ # [:strict_loading]
1771
+ # Enforces strict loading every time the associated record is loaded through this association.
1772
+ # [:ensuring_owner_was]
1773
+ # Specifies an instance method to be called on the owner. The method must return true in order for the
1774
+ # associated records to be deleted in a background job.
1639
1775
  #
1640
1776
  # Option examples:
1641
1777
  # belongs_to :firm, foreign_key: "client_of"
@@ -1650,6 +1786,7 @@ module ActiveRecord
1650
1786
  # belongs_to :company, touch: :employees_last_updated_at
1651
1787
  # belongs_to :user, optional: true
1652
1788
  # belongs_to :account, default: -> { company.account }
1789
+ # belongs_to :account, strict_loading: true
1653
1790
  def belongs_to(name, scope = nil, **options)
1654
1791
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1655
1792
  Reflection.add_reflection self, name, reflection
@@ -1672,7 +1809,7 @@ module ActiveRecord
1672
1809
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1673
1810
  # join table with a migration such as this:
1674
1811
  #
1675
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[5.0]
1812
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.0]
1676
1813
  # def change
1677
1814
  # create_join_table :developers, :projects
1678
1815
  # end
@@ -1761,6 +1898,7 @@ module ActiveRecord
1761
1898
  # has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
1762
1899
  # has_and_belongs_to_many :categories, ->(post) {
1763
1900
  # where("default_category = ?", post.default_category)
1901
+ # }
1764
1902
  #
1765
1903
  # === Extensions
1766
1904
  #
@@ -1811,6 +1949,8 @@ module ActiveRecord
1811
1949
  #
1812
1950
  # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
1813
1951
  # <tt>:autosave</tt> to <tt>true</tt>.
1952
+ # [:strict_loading]
1953
+ # Enforces strict loading every time an associated record is loaded through this association.
1814
1954
  #
1815
1955
  # Option examples:
1816
1956
  # has_and_belongs_to_many :projects
@@ -1818,6 +1958,7 @@ module ActiveRecord
1818
1958
  # has_and_belongs_to_many :nations, class_name: "Country"
1819
1959
  # has_and_belongs_to_many :categories, join_table: "prods_cats"
1820
1960
  # has_and_belongs_to_many :categories, -> { readonly }
1961
+ # has_and_belongs_to_many :categories, strict_loading: true
1821
1962
  def has_and_belongs_to_many(name, scope = nil, **options, &extension)
1822
1963
  habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
1823
1964
 
@@ -1848,11 +1989,11 @@ module ActiveRecord
1848
1989
  hm_options[:through] = middle_reflection.name
1849
1990
  hm_options[:source] = join_model.right_reflection.name
1850
1991
 
1851
- [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
1992
+ [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading].each do |k|
1852
1993
  hm_options[k] = options[k] if options.key? k
1853
1994
  end
1854
1995
 
1855
- has_many name, scope, hm_options, &extension
1996
+ has_many name, scope, **hm_options, &extension
1856
1997
  _reflections[name.to_s].parent_reflection = habtm_reflection
1857
1998
  end
1858
1999
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class AsynchronousQueriesTracker # :nodoc:
5
+ module NullSession # :nodoc:
6
+ class << self
7
+ def active?
8
+ true
9
+ end
10
+
11
+ def finalize
12
+ end
13
+ end
14
+ end
15
+
16
+ class Session # :nodoc:
17
+ def initialize
18
+ @active = true
19
+ end
20
+
21
+ def active?
22
+ @active
23
+ end
24
+
25
+ def finalize
26
+ @active = false
27
+ end
28
+ end
29
+
30
+ class << self
31
+ def install_executor_hooks(executor = ActiveSupport::Executor)
32
+ executor.register_hook(self)
33
+ end
34
+
35
+ def run
36
+ ActiveRecord::Base.asynchronous_queries_tracker.start_session
37
+ end
38
+
39
+ def complete(asynchronous_queries_tracker)
40
+ asynchronous_queries_tracker.finalize_session
41
+ end
42
+ end
43
+
44
+ attr_reader :current_session
45
+
46
+ def initialize
47
+ @current_session = NullSession
48
+ end
49
+
50
+ def start_session
51
+ @current_session = Session.new
52
+ self
53
+ end
54
+
55
+ def finalize_session
56
+ @current_session.finalize
57
+ @current_session = NullSession
58
+ end
59
+ end
60
+ end