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
@@ -2,39 +2,6 @@
2
2
 
3
3
  module ActiveRecord::Associations::Builder # :nodoc:
4
4
  class HasAndBelongsToMany # :nodoc:
5
- class JoinTableResolver # :nodoc:
6
- KnownTable = Struct.new :join_table
7
-
8
- class KnownClass # :nodoc:
9
- def initialize(lhs_class, rhs_class_name)
10
- @lhs_class = lhs_class
11
- @rhs_class_name = rhs_class_name
12
- @join_table = nil
13
- end
14
-
15
- def join_table
16
- @join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
17
- end
18
-
19
- private
20
-
21
- def klass
22
- @lhs_class.send(:compute_type, @rhs_class_name)
23
- end
24
- end
25
-
26
- def self.build(lhs_class, name, options)
27
- if options[:join_table]
28
- KnownTable.new options[:join_table].to_s
29
- else
30
- class_name = options.fetch(:class_name) {
31
- name.to_s.camelize.singularize
32
- }
33
- KnownClass.new lhs_class, class_name.to_s
34
- end
35
- end
36
- end
37
-
38
5
  attr_reader :lhs_model, :association_name, :options
39
6
 
40
7
  def initialize(association_name, lhs_model, options)
@@ -44,8 +11,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
44
11
  end
45
12
 
46
13
  def through_model
47
- habtm = JoinTableResolver.build lhs_model, association_name, options
48
-
49
14
  join_model = Class.new(ActiveRecord::Base) {
50
15
  class << self
51
16
  attr_accessor :left_model
@@ -56,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
56
21
  end
57
22
 
58
23
  def self.table_name
59
- table_name_resolver.join_table
24
+ # Table name needs to be resolved lazily
25
+ # because RHS class might not have been loaded
26
+ @table_name ||= table_name_resolver.call
60
27
  end
61
28
 
62
29
  def self.compute_type(class_name)
@@ -79,14 +46,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
79
46
  end
80
47
 
81
48
  private
82
-
83
49
  def self.suppress_composite_primary_key(pk)
84
50
  pk unless pk.is_a?(Array)
85
51
  end
86
52
  }
87
53
 
88
54
  join_model.name = "HABTM_#{association_name.to_s.camelize}"
89
- join_model.table_name_resolver = habtm
55
+ join_model.table_name_resolver = -> { table_name }
90
56
  join_model.left_model = lhs_model
91
57
 
92
58
  join_model.add_left_association :left_side, anonymous_class: lhs_model
@@ -96,7 +62,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
96
62
 
97
63
  def middle_reflection(join_model)
98
64
  middle_name = [lhs_model.name.downcase.pluralize,
99
- association_name].join("_".freeze).gsub("::".freeze, "_".freeze).to_sym
65
+ association_name.to_s].sort.join("_").gsub("::", "_").to_sym
100
66
  middle_options = middle_options join_model
101
67
 
102
68
  HasMany.create_reflection(lhs_model,
@@ -106,17 +72,27 @@ module ActiveRecord::Associations::Builder # :nodoc:
106
72
  end
107
73
 
108
74
  private
109
-
110
75
  def middle_options(join_model)
111
76
  middle_options = {}
112
77
  middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
113
- middle_options[:source] = join_model.left_reflection.name
114
78
  if options.key? :foreign_key
115
79
  middle_options[:foreign_key] = options[:foreign_key]
116
80
  end
117
81
  middle_options
118
82
  end
119
83
 
84
+ def table_name
85
+ if options[:join_table]
86
+ options[:join_table].to_s
87
+ else
88
+ class_name = options.fetch(:class_name) {
89
+ association_name.to_s.camelize.singularize
90
+ }
91
+ klass = lhs_model.send(:compute_type, class_name.to_s)
92
+ [lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
93
+ end
94
+ end
95
+
120
96
  def belongs_to_options(options)
121
97
  rhs_options = {}
122
98
 
@@ -1,17 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord::Associations::Builder # :nodoc:
4
- class HasMany < CollectionAssociation #:nodoc:
4
+ class HasMany < CollectionAssociation # :nodoc:
5
5
  def self.macro
6
6
  :has_many
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
10
+ valid = super + [:counter_cache, :join_table, :index_errors]
11
+ valid += [:as, :foreign_type] if options[:as]
12
+ valid += [:through, :source, :source_type] if options[:through]
13
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
14
+ valid += [:disable_joins] if options[:disable_joins] && options[:through]
15
+ valid
11
16
  end
12
17
 
13
18
  def self.valid_dependent_options
14
- [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
19
+ [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception, :destroy_async]
15
20
  end
21
+
22
+ private_class_method :macro, :valid_options, :valid_dependent_options
16
23
  end
17
24
  end
@@ -1,19 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord::Associations::Builder # :nodoc:
4
- class HasOne < SingularAssociation #:nodoc:
4
+ class HasOne < SingularAssociation # :nodoc:
5
5
  def self.macro
6
6
  :has_one
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- valid = super + [:as]
10
+ valid = super
11
+ valid += [:as, :foreign_type] if options[:as]
12
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
11
13
  valid += [:through, :source, :source_type] if options[:through]
14
+ valid += [:disable_joins] if options[:disable_joins] && options[:through]
12
15
  valid
13
16
  end
14
17
 
15
18
  def self.valid_dependent_options
16
- [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
19
+ [:destroy, :destroy_async, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
20
+ end
21
+
22
+ def self.define_callbacks(model, reflection)
23
+ super
24
+ add_touch_callbacks(model, reflection) if reflection.options[:touch]
17
25
  end
18
26
 
19
27
  def self.add_destroy_callbacks(model, reflection)
@@ -26,5 +34,29 @@ module ActiveRecord::Associations::Builder # :nodoc:
26
34
  model.validates_presence_of reflection.name, message: :required
27
35
  end
28
36
  end
37
+
38
+ def self.touch_record(record, name, touch)
39
+ instance = record.send(name)
40
+
41
+ if instance&.persisted?
42
+ touch != true ?
43
+ instance.touch(touch) : instance.touch
44
+ end
45
+ end
46
+
47
+ def self.add_touch_callbacks(model, reflection)
48
+ name = reflection.name
49
+ touch = reflection.options[:touch]
50
+
51
+ callback = -> (record) { HasOne.touch_record(record, name, touch) }
52
+ model.after_create callback, if: :saved_changes?
53
+ model.after_create_commit { association(name).reset_negative_cache }
54
+ model.after_update callback, if: :saved_changes?
55
+ model.after_destroy callback
56
+ model.after_touch callback
57
+ end
58
+
59
+ private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
60
+ :define_callbacks, :define_validations, :add_touch_callbacks
29
61
  end
30
62
  end
@@ -3,9 +3,9 @@
3
3
  # This class is inherited by the has_one and belongs_to association classes
4
4
 
5
5
  module ActiveRecord::Associations::Builder # :nodoc:
6
- class SingularAssociation < Association #:nodoc:
6
+ class SingularAssociation < Association # :nodoc:
7
7
  def self.valid_options(options)
8
- super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
8
+ super + [:required, :touch]
9
9
  end
10
10
 
11
11
  def self.define_accessors(model, reflection)
@@ -13,7 +13,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
13
13
  mixin = model.generated_association_methods
14
14
  name = reflection.name
15
15
 
16
- define_constructors(mixin, name) if reflection.constructable?
16
+ define_constructors(mixin, name) unless reflection.polymorphic?
17
17
 
18
18
  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
19
19
  def reload_#{name}
@@ -38,5 +38,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
38
38
  end
39
39
  CODE
40
40
  end
41
+
42
+ private_class_method :valid_options, :define_accessors, :define_constructors
41
43
  end
42
44
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
4
+
3
5
  module ActiveRecord
4
6
  module Associations
5
7
  # = Active Record Association Collection
@@ -25,9 +27,11 @@ module ActiveRecord
25
27
  #
26
28
  # If you need to work on all current children, new and existing records,
27
29
  # +load_target+ and the +loaded+ flag are your friends.
28
- class CollectionAssociation < Association #:nodoc:
30
+ class CollectionAssociation < Association # :nodoc:
29
31
  # Implements the reader method, e.g. foo.items for Foo.has_many :items
30
32
  def reader
33
+ ensure_klass_exists!
34
+
31
35
  if stale_target?
32
36
  reload
33
37
  end
@@ -56,7 +60,7 @@ module ActiveRecord
56
60
  def ids_writer(ids)
57
61
  primary_key = reflection.association_primary_key
58
62
  pk_type = klass.type_for_attribute(primary_key)
59
- ids = Array(ids).reject(&:blank?)
63
+ ids = Array(ids).compact_blank
60
64
  ids.map! { |i| pk_type.cast(i) }
61
65
 
62
66
  records = klass.where(primary_key => ids).index_by do |r|
@@ -75,6 +79,7 @@ module ActiveRecord
75
79
  def reset
76
80
  super
77
81
  @target = []
82
+ @replaced_or_added_targets = Set.new
78
83
  @association_ids = nil
79
84
  end
80
85
 
@@ -101,11 +106,11 @@ module ActiveRecord
101
106
  end
102
107
  end
103
108
 
104
- def build(attributes = {}, &block)
109
+ def build(attributes = nil, &block)
105
110
  if attributes.is_a?(Array)
106
111
  attributes.collect { |attr| build(attr, &block) }
107
112
  else
108
- add_to_target(build_record(attributes, &block))
113
+ add_to_target(build_record(attributes, &block), replace: true)
109
114
  end
110
115
  end
111
116
 
@@ -121,21 +126,6 @@ module ActiveRecord
121
126
  end
122
127
  end
123
128
 
124
- # Starts a transaction in the association class's database connection.
125
- #
126
- # class Author < ActiveRecord::Base
127
- # has_many :books
128
- # end
129
- #
130
- # Author.first.books.transaction do
131
- # # same effect as calling Book.transaction
132
- # end
133
- def transaction(*args)
134
- reflection.klass.transaction(*args) do
135
- yield
136
- end
137
- end
138
-
139
129
  # Removes all records from the association without calling callbacks
140
130
  # on the associated records. It honors the +:dependent+ option. However
141
131
  # if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
@@ -211,9 +201,11 @@ module ActiveRecord
211
201
  def size
212
202
  if !find_target? || loaded?
213
203
  target.size
204
+ elsif @association_ids
205
+ @association_ids.size
214
206
  elsif !association_scope.group_values.empty?
215
207
  load_target.size
216
- elsif !association_scope.distinct_value && target.is_a?(Array)
208
+ elsif !association_scope.distinct_value && !target.empty?
217
209
  unsaved_records = target.select(&:new_record?)
218
210
  unsaved_records.size + count_records
219
211
  else
@@ -226,14 +218,14 @@ module ActiveRecord
226
218
  # If the collection has been loaded
227
219
  # it is equivalent to <tt>collection.size.zero?</tt>. If the
228
220
  # collection has not been loaded, it is equivalent to
229
- # <tt>collection.exists?</tt>. If the collection has not already been
221
+ # <tt>!collection.exists?</tt>. If the collection has not already been
230
222
  # loaded and you are going to fetch the records anyway it is better to
231
223
  # check <tt>collection.length.zero?</tt>.
232
224
  def empty?
233
- if loaded?
225
+ if loaded? || @association_ids || reflection.has_cached_counter?
234
226
  size.zero?
235
227
  else
236
- @target.blank? && !scope.exists?
228
+ target.empty? && !scope.exists?
237
229
  end
238
230
  end
239
231
 
@@ -276,11 +268,21 @@ module ActiveRecord
276
268
  target
277
269
  end
278
270
 
279
- def add_to_target(record, skip_callbacks = false, &block)
280
- if association_scope.distinct_value
281
- index = @target.index(record)
271
+ def add_to_target(record, skip_callbacks: false, replace: false, &block)
272
+ replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
273
+ end
274
+
275
+ def target=(record)
276
+ return super unless reflection.klass.has_many_inversing
277
+
278
+ case record
279
+ when nil
280
+ # It's not possible to remove the record from the inverse association.
281
+ when Array
282
+ super
283
+ else
284
+ replace_on_target(record, true, replace: true, inversing: true)
282
285
  end
283
- replace_on_target(record, index, skip_callbacks, &block)
284
286
  end
285
287
 
286
288
  def scope
@@ -295,26 +297,15 @@ module ActiveRecord
295
297
 
296
298
  def find_from_target?
297
299
  loaded? ||
300
+ owner.strict_loading? ||
301
+ reflection.strict_loading? ||
298
302
  owner.new_record? ||
299
303
  target.any? { |record| record.new_record? || record.changed? }
300
304
  end
301
305
 
302
306
  private
303
-
304
- def find_target
305
- scope = self.scope
306
- return scope.to_a if skip_statement_cache?(scope)
307
-
308
- conn = klass.connection
309
- sc = reflection.association_scope_cache(conn, owner) do |params|
310
- as = AssociationScope.create { params.bind }
311
- target_scope.merge!(as.scope(self))
312
- end
313
-
314
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
315
- sc.execute(binds, conn) do |record|
316
- set_inverse_instance(record)
317
- end
307
+ def transaction(&block)
308
+ reflection.klass.transaction(&block)
318
309
  end
319
310
 
320
311
  # We have some records loaded from the database (persisted) and some that are
@@ -344,12 +335,12 @@ module ActiveRecord
344
335
  end
345
336
  end
346
337
 
347
- persisted + memory
338
+ persisted + memory.reject(&:persisted?)
348
339
  end
349
340
 
350
341
  def _create_record(attributes, raise = false, &block)
351
342
  unless owner.persisted?
352
- raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
343
+ raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
353
344
  end
354
345
 
355
346
  if attributes.is_a?(Array)
@@ -393,10 +384,12 @@ module ActiveRecord
393
384
  end
394
385
 
395
386
  def remove_records(existing_records, records, method)
396
- records.each { |record| callback(:before_remove, record) }
387
+ catch(:abort) do
388
+ records.each { |record| callback(:before_remove, record) }
389
+ end || return
397
390
 
398
391
  delete_records(existing_records, method) if existing_records.any?
399
- records.each { |record| target.delete(record) }
392
+ @target -= records
400
393
  @association_ids = nil
401
394
 
402
395
  records.each { |record| callback(:after_remove, record) }
@@ -425,7 +418,7 @@ module ActiveRecord
425
418
  common_records = intersection(new_target, original_target)
426
419
  common_records.each do |record|
427
420
  skip_callbacks = true
428
- replace_on_target(record, @target.index(record), skip_callbacks)
421
+ replace_on_target(record, skip_callbacks, replace: true)
429
422
  end
430
423
  end
431
424
 
@@ -448,8 +441,14 @@ module ActiveRecord
448
441
  records
449
442
  end
450
443
 
451
- def replace_on_target(record, index, skip_callbacks)
452
- callback(:before_add, record) unless skip_callbacks
444
+ def replace_on_target(record, skip_callbacks, replace:, inversing: false)
445
+ if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
446
+ index = @target.index(record)
447
+ end
448
+
449
+ catch(:abort) do
450
+ callback(:before_add, record)
451
+ end || return unless skip_callbacks
453
452
 
454
453
  set_inverse_instance(record)
455
454
 
@@ -457,6 +456,12 @@ module ActiveRecord
457
456
 
458
457
  yield(record) if block_given?
459
458
 
459
+ if !index && @replaced_or_added_targets.include?(record)
460
+ index = @target.index(record)
461
+ end
462
+
463
+ @replaced_or_added_targets << record if inversing || index || record.new_record?
464
+
460
465
  if index
461
466
  target[index] = record
462
467
  elsif @_was_loaded || !loaded?
@@ -479,7 +484,11 @@ module ActiveRecord
479
484
 
480
485
  def callbacks_for(callback_name)
481
486
  full_callback_name = "#{callback_name}_for_#{reflection.name}"
482
- owner.class.send(full_callback_name)
487
+ if owner.class.respond_to?(full_callback_name)
488
+ owner.class.send(full_callback_name)
489
+ else
490
+ []
491
+ end
483
492
  end
484
493
 
485
494
  def include_in_memory?(record)
@@ -2,11 +2,8 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
5
- # Association proxies in Active Record are middlemen between the object that
6
- # holds the association, known as the <tt>@owner</tt>, and the actual associated
7
- # object, known as the <tt>@target</tt>. The kind of association any proxy is
8
- # about is available in <tt>@reflection</tt>. That's an instance of the class
9
- # ActiveRecord::Reflection::AssociationReflection.
5
+ # Collection proxies in Active Record are middlemen between an
6
+ # <tt>association</tt>, and its <tt>target</tt> result set.
10
7
  #
11
8
  # For example, given
12
9
  #
@@ -16,21 +13,21 @@ module ActiveRecord
16
13
  #
17
14
  # blog = Blog.first
18
15
  #
19
- # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
20
- # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
21
- # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
16
+ # The collection proxy returned by <tt>blog.posts</tt> is built from a
17
+ # <tt>:has_many</tt> <tt>association</tt>, and delegates to a collection
18
+ # of posts as the <tt>target</tt>.
22
19
  #
23
- # This class delegates unknown methods to <tt>@target</tt> via
24
- # <tt>method_missing</tt>.
20
+ # This class delegates unknown methods to the <tt>association</tt>'s
21
+ # relation class via a delegate cache.
25
22
  #
26
- # The <tt>@target</tt> object is not \loaded until needed. For example,
23
+ # The <tt>target</tt> result set is not loaded until needed. For example,
27
24
  #
28
25
  # blog.posts.count
29
26
  #
30
27
  # is computed directly through SQL and does not trigger by itself the
31
28
  # instantiation of the actual post records.
32
29
  class CollectionProxy < Relation
33
- def initialize(klass, association) #:nodoc:
30
+ def initialize(klass, association, **) # :nodoc:
34
31
  @association = association
35
32
  super klass
36
33
 
@@ -49,11 +46,12 @@ module ActiveRecord
49
46
  # Returns +true+ if the association has been loaded, otherwise +false+.
50
47
  #
51
48
  # person.pets.loaded? # => false
52
- # person.pets
49
+ # person.pets.records
53
50
  # person.pets.loaded? # => true
54
51
  def loaded?
55
52
  @association.loaded?
56
53
  end
54
+ alias :loaded :loaded?
57
55
 
58
56
  ##
59
57
  # :method: select
@@ -103,7 +101,7 @@ module ActiveRecord
103
101
  # converting them into an array and iterating through them using
104
102
  # Array#select.
105
103
  #
106
- # person.pets.select { |pet| pet.name =~ /oo/ }
104
+ # person.pets.select { |pet| /oo/.match?(pet.name) }
107
105
  # # => [
108
106
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
109
107
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
@@ -376,7 +374,7 @@ module ActiveRecord
376
374
  # person.pets
377
375
  # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
378
376
  #
379
- # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
377
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]
380
378
  #
381
379
  # person.pets.replace(other_pets)
382
380
  #
@@ -815,7 +813,7 @@ module ActiveRecord
815
813
  # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
816
814
  # it is equivalent to <tt>!collection.exists?</tt>. If the collection has
817
815
  # not already been loaded and you are going to fetch the records anyway it
818
- # is better to check <tt>collection.length.zero?</tt>.
816
+ # is better to check <tt>collection.load.empty?</tt>.
819
817
  #
820
818
  # class Person < ActiveRecord::Base
821
819
  # has_many :pets
@@ -851,6 +849,11 @@ module ActiveRecord
851
849
  # person.pets.count # => 1
852
850
  # person.pets.any? # => true
853
851
  #
852
+ # Calling it without a block when the collection is not yet
853
+ # loaded is equivalent to <tt>collection.exists?</tt>.
854
+ # If you're going to load the collection anyway, it is better
855
+ # to call <tt>collection.load.any?</tt> to avoid an extra query.
856
+ #
854
857
  # You can also pass a +block+ to define criteria. The behavior
855
858
  # is the same, it returns true if the collection based on the
856
859
  # criteria is not empty.
@@ -923,7 +926,7 @@ module ActiveRecord
923
926
  !!@association.include?(record)
924
927
  end
925
928
 
926
- def proxy_association
929
+ def proxy_association # :nodoc:
927
930
  @association
928
931
  end
929
932
 
@@ -1005,7 +1008,7 @@ module ActiveRecord
1005
1008
  end
1006
1009
 
1007
1010
  # Adds one or more +records+ to the collection by setting their foreign keys
1008
- # to the association's primary key. Since +<<+ flattens its argument list and
1011
+ # to the association's primary key. Since <tt><<</tt> flattens its argument list and
1009
1012
  # inserts each record, +push+ and +concat+ behave identically. Returns +self+
1010
1013
  # so several appends may be chained together.
1011
1014
  #
@@ -1032,7 +1035,7 @@ module ActiveRecord
1032
1035
  alias_method :append, :<<
1033
1036
  alias_method :concat, :<<
1034
1037
 
1035
- def prepend(*args)
1038
+ def prepend(*args) # :nodoc:
1036
1039
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
1037
1040
  end
1038
1041
 
@@ -1062,7 +1065,7 @@ module ActiveRecord
1062
1065
  # person.pets.reload # fetches pets from the database
1063
1066
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1064
1067
  def reload
1065
- proxy_association.reload
1068
+ proxy_association.reload(true)
1066
1069
  reset_scope
1067
1070
  end
1068
1071
 
@@ -1089,22 +1092,28 @@ module ActiveRecord
1089
1092
  end
1090
1093
 
1091
1094
  def reset_scope # :nodoc:
1092
- @offsets = {}
1095
+ @offsets = @take = nil
1093
1096
  @scope = nil
1094
1097
  self
1095
1098
  end
1096
1099
 
1100
+ def inspect # :nodoc:
1101
+ load_target if find_from_target?
1102
+ super
1103
+ end
1104
+
1097
1105
  delegate_methods = [
1098
1106
  QueryMethods,
1099
1107
  SpawnMethods,
1100
1108
  ].flat_map { |klass|
1101
1109
  klass.public_instance_methods(false)
1102
- } - self.public_instance_methods(false) - [:select] + [:scoping]
1110
+ } - self.public_instance_methods(false) - [:select] + [
1111
+ :scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
1112
+ ]
1103
1113
 
1104
1114
  delegate(*delegate_methods, to: :scope)
1105
1115
 
1106
1116
  private
1107
-
1108
1117
  def find_nth_with_limit(index, limit)
1109
1118
  load_target if find_from_target?
1110
1119
  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