activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  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 +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
5
+ # = Active Record Collection Proxy
6
+ #
5
7
  # Collection proxies in Active Record are middlemen between an
6
8
  # <tt>association</tt>, and its <tt>target</tt> result set.
7
9
  #
@@ -27,7 +29,7 @@ module ActiveRecord
27
29
  # is computed directly through SQL and does not trigger by itself the
28
30
  # instantiation of the actual post records.
29
31
  class CollectionProxy < Relation
30
- def initialize(klass, association) #:nodoc:
32
+ def initialize(klass, association, **) # :nodoc:
31
33
  @association = association
32
34
  super klass
33
35
 
@@ -46,11 +48,12 @@ module ActiveRecord
46
48
  # Returns +true+ if the association has been loaded, otherwise +false+.
47
49
  #
48
50
  # person.pets.loaded? # => false
49
- # person.pets
51
+ # person.pets.records
50
52
  # person.pets.loaded? # => true
51
53
  def loaded?
52
54
  @association.loaded?
53
55
  end
56
+ alias :loaded :loaded?
54
57
 
55
58
  ##
56
59
  # :method: select
@@ -93,21 +96,21 @@ module ActiveRecord
93
96
  # receive:
94
97
  #
95
98
  # person.pets.select(:name).first.person_id
96
- # # => ActiveModel::MissingAttributeError: missing attribute: person_id
99
+ # # => ActiveModel::MissingAttributeError: missing attribute 'person_id' for Pet
97
100
  #
98
- # *Second:* You can pass a block so it can be used just like Array#select.
101
+ # *Second:* You can pass a block so it can be used just like <tt>Array#select</tt>.
99
102
  # This builds an array of objects from the database for the scope,
100
103
  # converting them into an array and iterating through them using
101
- # Array#select.
104
+ # <tt>Array#select</tt>.
102
105
  #
103
- # person.pets.select { |pet| pet.name =~ /oo/ }
106
+ # person.pets.select { |pet| /oo/.match?(pet.name) }
104
107
  # # => [
105
108
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
106
109
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
107
110
  # # ]
108
111
 
109
112
  # Finds an object in the collection responding to the +id+. Uses the same
110
- # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
113
+ # rules as ActiveRecord::FinderMethods.find. Returns ActiveRecord::RecordNotFound
111
114
  # error if the object cannot be found.
112
115
  #
113
116
  # class Person < ActiveRecord::Base
@@ -217,7 +220,7 @@ module ActiveRecord
217
220
  # :call-seq:
218
221
  # third_to_last()
219
222
  #
220
- # Same as #first except returns only the third-to-last record.
223
+ # Same as #last except returns only the third-to-last record.
221
224
 
222
225
  ##
223
226
  # :method: second_to_last
@@ -225,7 +228,7 @@ module ActiveRecord
225
228
  # :call-seq:
226
229
  # second_to_last()
227
230
  #
228
- # Same as #first except returns only the second-to-last record.
231
+ # Same as #last except returns only the second-to-last record.
229
232
 
230
233
  # Returns the last record, or the last +n+ records, from the collection.
231
234
  # If the collection is empty, the first form returns +nil+, and the second
@@ -259,7 +262,7 @@ module ActiveRecord
259
262
  end
260
263
 
261
264
  # Gives a record (or N records if a parameter is supplied) from the collection
262
- # using the same rules as <tt>ActiveRecord::Base.take</tt>.
265
+ # using the same rules as ActiveRecord::FinderMethods.take.
263
266
  #
264
267
  # class Person < ActiveRecord::Base
265
268
  # has_many :pets
@@ -373,7 +376,7 @@ module ActiveRecord
373
376
  # person.pets
374
377
  # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
375
378
  #
376
- # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
379
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]
377
380
  #
378
381
  # person.pets.replace(other_pets)
379
382
  #
@@ -381,7 +384,7 @@ module ActiveRecord
381
384
  # # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
382
385
  #
383
386
  # If the supplied array has an incorrect association type, it raises
384
- # an <tt>ActiveRecord::AssociationTypeMismatch</tt> error:
387
+ # an ActiveRecord::AssociationTypeMismatch error:
385
388
  #
386
389
  # person.pets.replace(["doo", "ggie", "gaga"])
387
390
  # # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
@@ -474,7 +477,7 @@ module ActiveRecord
474
477
 
475
478
  # Deletes the records of the collection directly from the database
476
479
  # ignoring the +:dependent+ option. Records are instantiated and it
477
- # invokes +before_remove+, +after_remove+ , +before_destroy+ and
480
+ # invokes +before_remove+, +after_remove+, +before_destroy+, and
478
481
  # +after_destroy+ callbacks.
479
482
  #
480
483
  # class Person < ActiveRecord::Base
@@ -812,7 +815,7 @@ module ActiveRecord
812
815
  # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
813
816
  # it is equivalent to <tt>!collection.exists?</tt>. If the collection has
814
817
  # not already been loaded and you are going to fetch the records anyway it
815
- # is better to check <tt>collection.length.zero?</tt>.
818
+ # is better to check <tt>collection.load.empty?</tt>.
816
819
  #
817
820
  # class Person < ActiveRecord::Base
818
821
  # has_many :pets
@@ -848,6 +851,11 @@ module ActiveRecord
848
851
  # person.pets.count # => 1
849
852
  # person.pets.any? # => true
850
853
  #
854
+ # Calling it without a block when the collection is not yet
855
+ # loaded is equivalent to <tt>collection.exists?</tt>.
856
+ # If you're going to load the collection anyway, it is better
857
+ # to call <tt>collection.load.any?</tt> to avoid an extra query.
858
+ #
851
859
  # You can also pass a +block+ to define criteria. The behavior
852
860
  # is the same, it returns true if the collection based on the
853
861
  # criteria is not empty.
@@ -920,11 +928,24 @@ module ActiveRecord
920
928
  !!@association.include?(record)
921
929
  end
922
930
 
931
+ # Returns the association object for the collection.
932
+ #
933
+ # class Person < ActiveRecord::Base
934
+ # has_many :pets
935
+ # end
936
+ #
937
+ # person.pets.proxy_association
938
+ # # => #<ActiveRecord::Associations::HasManyAssociation owner="#<Person:0x00>">
939
+ #
940
+ # Returns the same object as <tt>person.association(:pets)</tt>,
941
+ # allowing you to make calls like <tt>person.pets.proxy_association.owner</tt>.
942
+ #
943
+ # See Associations::ClassMethods@Association+extensions for more.
923
944
  def proxy_association
924
945
  @association
925
946
  end
926
947
 
927
- # Returns a <tt>Relation</tt> object for the records in this association
948
+ # Returns a Relation object for the records in this association
928
949
  def scope
929
950
  @scope ||= @association.scope
930
951
  end
@@ -949,10 +970,13 @@ module ActiveRecord
949
970
  # person.pets == other
950
971
  # # => true
951
972
  #
973
+ #
974
+ # Note that unpersisted records can still be seen as equal:
975
+ #
952
976
  # other = [Pet.new(id: 1), Pet.new(id: 2)]
953
977
  #
954
978
  # person.pets == other
955
- # # => false
979
+ # # => true
956
980
  def ==(other)
957
981
  load_target == other
958
982
  end
@@ -1086,22 +1110,33 @@ module ActiveRecord
1086
1110
  end
1087
1111
 
1088
1112
  def reset_scope # :nodoc:
1089
- @offsets = {}
1113
+ @offsets = @take = nil
1090
1114
  @scope = nil
1091
1115
  self
1092
1116
  end
1093
1117
 
1118
+ def inspect # :nodoc:
1119
+ load_target if find_from_target?
1120
+ super
1121
+ end
1122
+
1123
+ def pretty_print(pp) # :nodoc:
1124
+ load_target if find_from_target?
1125
+ super
1126
+ end
1127
+
1094
1128
  delegate_methods = [
1095
1129
  QueryMethods,
1096
1130
  SpawnMethods,
1097
1131
  ].flat_map { |klass|
1098
1132
  klass.public_instance_methods(false)
1099
- } - self.public_instance_methods(false) - [:select] + [:scoping, :values]
1133
+ } - self.public_instance_methods(false) - [:select] + [
1134
+ :scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all, :load_async
1135
+ ]
1100
1136
 
1101
1137
  delegate(*delegate_methods, to: :scope)
1102
1138
 
1103
1139
  private
1104
-
1105
1140
  def find_nth_with_limit(index, limit)
1106
1141
  load_target if find_from_target?
1107
1142
  super
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class DisableJoinsAssociationScope < AssociationScope # :nodoc:
6
+ def scope(association)
7
+ source_reflection = association.reflection
8
+ owner = association.owner
9
+ unscoped = association.klass.unscoped
10
+ reverse_chain = get_chain(source_reflection, association, unscoped.alias_tracker).reverse
11
+
12
+ last_reflection, last_ordered, last_join_ids = last_scope_chain(reverse_chain, owner)
13
+
14
+ add_constraints(last_reflection, last_reflection.join_primary_key, last_join_ids, owner, last_ordered)
15
+ end
16
+
17
+ private
18
+ def last_scope_chain(reverse_chain, owner)
19
+ first_item = reverse_chain.shift
20
+ first_scope = [first_item, false, [owner._read_attribute(first_item.join_foreign_key)]]
21
+
22
+ reverse_chain.inject(first_scope) do |(reflection, ordered, join_ids), next_reflection|
23
+ key = reflection.join_primary_key
24
+ records = add_constraints(reflection, key, join_ids, owner, ordered)
25
+ foreign_key = next_reflection.join_foreign_key
26
+ record_ids = records.pluck(foreign_key)
27
+ records_ordered = records && records.order_values.any?
28
+
29
+ [next_reflection, records_ordered, record_ids]
30
+ end
31
+ end
32
+
33
+ def add_constraints(reflection, key, join_ids, owner, ordered)
34
+ scope = reflection.build_scope(reflection.aliased_table).where(key => join_ids)
35
+
36
+ relation = reflection.klass.scope_for_association
37
+ scope.merge!(
38
+ relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
39
+ )
40
+
41
+ scope = reflection.constraints.inject(scope) do |memo, scope_chain_item|
42
+ item = eval_scope(reflection, scope_chain_item, owner)
43
+ scope.unscope!(*item.unscope_values)
44
+ scope.where_clause += item.where_clause
45
+ scope.order_values = item.order_values | scope.order_values
46
+ scope
47
+ end
48
+
49
+ if scope.order_values.empty? && ordered
50
+ split_scope = DisableJoinsAssociationRelation.create(scope.klass, key, join_ids)
51
+ split_scope.where_clause += scope.where_clause
52
+ split_scope
53
+ else
54
+ scope
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,265 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class AssociationNotFoundError < ConfigurationError # :nodoc:
5
+ attr_reader :record, :association_name
6
+
7
+ def initialize(record = nil, association_name = nil)
8
+ @record = record
9
+ @association_name = association_name
10
+ if record && association_name
11
+ super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
12
+ else
13
+ super("Association was not found.")
14
+ end
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
31
+ end
32
+
33
+ class InverseOfAssociationNotFoundError < ActiveRecordError # :nodoc:
34
+ attr_reader :reflection, :associated_class
35
+
36
+ def initialize(reflection = nil, associated_class = nil)
37
+ if reflection
38
+ @reflection = reflection
39
+ @associated_class = associated_class.nil? ? reflection.klass : associated_class
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})")
41
+ else
42
+ super("Could not find the inverse association.")
43
+ end
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
60
+ end
61
+
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}")
82
+ else
83
+ super("Could not find the association.")
84
+ end
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
102
+ end
103
+
104
+ class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError # :nodoc:
105
+ def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
106
+ if owner_class_name && reflection && source_reflection
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.")
108
+ else
109
+ super("Cannot have a has_many :through association.")
110
+ end
111
+ end
112
+ end
113
+
114
+ class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
115
+ def initialize(owner_class_name = nil, reflection = nil)
116
+ if owner_class_name && reflection
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}'.")
118
+ else
119
+ super("Cannot have a has_many :through association.")
120
+ end
121
+ end
122
+ end
123
+
124
+ class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError # :nodoc:
125
+ def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
126
+ if owner_class_name && reflection && source_reflection
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.")
128
+ else
129
+ super("Cannot have a has_many :through association.")
130
+ end
131
+ end
132
+ end
133
+
134
+ class HasOneThroughCantAssociateThroughCollection < ActiveRecordError # :nodoc:
135
+ def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
136
+ if owner_class_name && reflection && through_reflection
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.")
138
+ else
139
+ super("Cannot have a has_one :through association.")
140
+ end
141
+ end
142
+ end
143
+
144
+ class HasOneAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
145
+ def initialize(owner_class_name = nil, reflection = nil)
146
+ if owner_class_name && reflection
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}'.")
148
+ else
149
+ super("Cannot have a has_one :through association.")
150
+ end
151
+ end
152
+ end
153
+
154
+ class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError # :nodoc:
155
+ def initialize(reflection = nil)
156
+ if reflection
157
+ through_reflection = reflection.through_reflection
158
+ source_reflection_names = reflection.source_reflection_names
159
+ source_associations = reflection.through_reflection.klass._reflections.keys
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 ')}?")
161
+ else
162
+ super("Could not find the source association(s).")
163
+ end
164
+ end
165
+ end
166
+
167
+ class HasManyThroughOrderError < ActiveRecordError # :nodoc:
168
+ def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
169
+ if owner_class_name && reflection && through_reflection
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.")
171
+ else
172
+ super("Cannot have a has_many :through association before the through association is defined.")
173
+ end
174
+ end
175
+ end
176
+
177
+ class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError # :nodoc:
178
+ def initialize(owner = nil, reflection = nil)
179
+ if owner && reflection
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}.")
181
+ else
182
+ super("Cannot modify association.")
183
+ end
184
+ end
185
+ end
186
+
187
+ class CompositePrimaryKeyMismatchError < ActiveRecordError # :nodoc:
188
+ attr_reader :reflection
189
+
190
+ def initialize(reflection = nil)
191
+ if reflection
192
+ if reflection.has_one? || reflection.collection?
193
+ super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.active_record_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
194
+ else
195
+ super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.association_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
196
+ end
197
+ else
198
+ super("Association primary key doesn't match with foreign key.")
199
+ end
200
+ end
201
+ end
202
+
203
+ class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc:
204
+ def initialize(klass, macro, association_name, options, possible_sources)
205
+ example_options = options.dup
206
+ example_options[:source] = possible_sources.first
207
+
208
+ super("Ambiguous source reflection for through association. Please " \
209
+ "specify a :source directive on your declaration like:\n" \
210
+ "\n" \
211
+ " class #{klass} < ActiveRecord::Base\n" \
212
+ " #{macro} :#{association_name}, #{example_options}\n" \
213
+ " end"
214
+ )
215
+ end
216
+ end
217
+
218
+ class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
219
+ end
220
+
221
+ class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
222
+ end
223
+
224
+ class ThroughNestedAssociationsAreReadonly < ActiveRecordError # :nodoc:
225
+ def initialize(owner = nil, reflection = nil)
226
+ if owner && reflection
227
+ super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
228
+ else
229
+ super("Through nested associations are read-only.")
230
+ end
231
+ end
232
+ end
233
+
234
+ class HasManyThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
235
+ end
236
+
237
+ class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
238
+ end
239
+
240
+ # This error is raised when trying to eager load a polymorphic association using a JOIN.
241
+ # Eager loading polymorphic associations is only possible with
242
+ # {ActiveRecord::Relation#preload}[rdoc-ref:QueryMethods#preload].
243
+ class EagerLoadPolymorphicError < ActiveRecordError
244
+ def initialize(reflection = nil)
245
+ if reflection
246
+ super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
247
+ else
248
+ super("Eager load polymorphic error.")
249
+ end
250
+ end
251
+ end
252
+
253
+ # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
254
+ # (has_many, has_one) when there is at least 1 child associated instance.
255
+ # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
256
+ class DeleteRestrictionError < ActiveRecordError # :nodoc:
257
+ def initialize(name = nil)
258
+ if name
259
+ super("Cannot delete record because of dependent #{name}")
260
+ else
261
+ super("Delete restriction error.")
262
+ end
263
+ end
264
+ end
265
+ end
@@ -12,9 +12,29 @@ module ActiveRecord::Associations
12
12
 
13
13
  def nullified_owner_attributes
14
14
  Hash.new.tap do |attrs|
15
- attrs[reflection.foreign_key] = nil
15
+ Array(reflection.foreign_key).each { |foreign_key| attrs[foreign_key] = nil }
16
16
  attrs[reflection.type] = nil if reflection.type.present?
17
17
  end
18
18
  end
19
+
20
+ private
21
+ # Sets the owner attributes on the given record
22
+ def set_owner_attributes(record)
23
+ return if options[:through]
24
+
25
+ primary_key_attribute_names = Array(reflection.join_primary_key)
26
+ foreign_key_attribute_names = Array(reflection.join_foreign_key)
27
+
28
+ primary_key_foreign_key_pairs = primary_key_attribute_names.zip(foreign_key_attribute_names)
29
+
30
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
31
+ value = owner._read_attribute(foreign_key)
32
+ record._write_attribute(primary_key, value)
33
+ end
34
+
35
+ if reflection.type
36
+ record._write_attribute(reflection.type, owner.class.polymorphic_name)
37
+ end
38
+ end
19
39
  end
20
40
  end
@@ -3,11 +3,12 @@
3
3
  module ActiveRecord
4
4
  module Associations
5
5
  # = Active Record Has Many Association
6
+ #
6
7
  # This is the proxy that handles a has many association.
7
8
  #
8
9
  # If the association has a <tt>:through</tt> option further specialization
9
10
  # is provided by its child HasManyThroughAssociation.
10
- class HasManyAssociation < CollectionAssociation #:nodoc:
11
+ class HasManyAssociation < CollectionAssociation # :nodoc:
11
12
  include ForeignAssociation
12
13
 
13
14
  def handle_dependency
@@ -26,6 +27,32 @@ module ActiveRecord
26
27
  # No point in executing the counter update since we're going to destroy the parent anyway
27
28
  load_target.each { |t| t.destroyed_by_association = reflection }
28
29
  destroy_all
30
+ when :destroy_async
31
+ load_target.each do |t|
32
+ t.destroyed_by_association = reflection
33
+ end
34
+
35
+ unless target.empty?
36
+ association_class = target.first.class
37
+ if association_class.query_constraints_list
38
+ primary_key_column = association_class.query_constraints_list
39
+ ids = target.collect { |assoc| primary_key_column.map { |col| assoc.public_send(col) } }
40
+ else
41
+ primary_key_column = association_class.primary_key
42
+ ids = target.collect { |assoc| assoc.public_send(primary_key_column) }
43
+ end
44
+
45
+ ids.each_slice(owner.class.destroy_association_async_batch_size || ids.size) do |ids_batch|
46
+ enqueue_destroy_association(
47
+ owner_model_name: owner.class.to_s,
48
+ owner_id: owner.id,
49
+ association_class: reflection.klass.to_s,
50
+ association_ids: ids_batch,
51
+ association_primary_key_column: primary_key_column,
52
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
53
+ )
54
+ end
55
+ end
29
56
  else
30
57
  delete_all
31
58
  end
@@ -37,7 +64,6 @@ module ActiveRecord
37
64
  end
38
65
 
39
66
  private
40
-
41
67
  # Returns the number of records in this collection.
42
68
  #
43
69
  # If the association has a counter cache it gets that value. Otherwise
@@ -52,16 +78,19 @@ module ActiveRecord
52
78
  # If the collection is empty the target is set to an empty array and
53
79
  # the loaded flag is set to true as well.
54
80
  def count_records
55
- count = if reflection.has_cached_counter?
56
- owner._read_attribute(reflection.counter_cache_column).to_i
81
+ count = if reflection.has_active_cached_counter?
82
+ owner.read_attribute(reflection.counter_cache_column).to_i
57
83
  else
58
84
  scope.count(:all)
59
85
  end
60
86
 
61
- # If there's nothing in the database and @target has no new records
62
- # we are certain the current target is an empty array. This is a
63
- # documented side-effect of the method that may avoid an extra SELECT.
64
- loaded! if count == 0
87
+ # If there's nothing in the database, @target should only contain new
88
+ # records or be an empty array. This is a documented side-effect of
89
+ # the method that may avoid an extra SELECT.
90
+ if count == 0
91
+ target.select!(&:new_record?)
92
+ loaded!
93
+ end
65
94
 
66
95
  [association_scope.limit_value, count].compact.min
67
96
  end
@@ -76,7 +105,7 @@ module ActiveRecord
76
105
  if reflection.counter_must_be_updated_by_has_many?
77
106
  counter = reflection.counter_cache_column
78
107
  owner.increment(counter, difference)
79
- owner.send(:clear_attribute_change, counter) # eww
108
+ owner.send(:"clear_#{counter}_change")
80
109
  end
81
110
  end
82
111
 
@@ -100,7 +129,9 @@ module ActiveRecord
100
129
  records.each(&:destroy!)
101
130
  update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
102
131
  else
103
- scope = self.scope.where(reflection.klass.primary_key => records)
132
+ query_constraints = reflection.klass.composite_query_constraints_list
133
+ values = records.map { |r| query_constraints.map { |col| r._read_attribute(col) } }
134
+ scope = self.scope.where(query_constraints => values)
104
135
  update_counter(-delete_count(method, scope))
105
136
  end
106
137
  end