activerecord 4.2.0 → 6.1.7.1

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 (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1221 -796
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +267 -249
  8. data/lib/active_record/association_relation.rb +45 -7
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +172 -67
  11. data/lib/active_record/associations/association_scope.rb +105 -129
  12. data/lib/active_record/associations/belongs_to_association.rb +85 -59
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -33
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +168 -279
  22. data/lib/active_record/associations/collection_proxy.rb +263 -155
  23. data/lib/active_record/associations/foreign_association.rb +33 -0
  24. data/lib/active_record/associations/has_many_association.rb +57 -84
  25. data/lib/active_record/associations/has_many_through_association.rb +70 -82
  26. data/lib/active_record/associations/has_one_association.rb +74 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -73
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  31. data/lib/active_record/associations/join_dependency.rb +175 -164
  32. data/lib/active_record/associations/preloader/association.rb +107 -112
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +99 -96
  35. data/lib/active_record/associations/singular_association.rb +18 -45
  36. data/lib/active_record/associations/through_association.rb +49 -24
  37. data/lib/active_record/associations.rb +1845 -1597
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -138
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -36
  46. data/lib/active_record/attribute_methods/write.rb +25 -56
  47. data/lib/active_record/attribute_methods.rb +153 -162
  48. data/lib/active_record/attributes.rb +234 -70
  49. data/lib/active_record/autosave_association.rb +157 -69
  50. data/lib/active_record/base.rb +49 -50
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +46 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +271 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +78 -161
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +499 -293
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +595 -382
  119. data/lib/active_record/connection_adapters/schema_cache.rb +191 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -389
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +488 -243
  133. data/lib/active_record/counter_cache.rb +71 -50
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +273 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +212 -94
  143. data/lib/active_record/errors.rb +225 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +11 -6
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +273 -496
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +64 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +52 -0
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +103 -95
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +298 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +685 -309
  170. data/lib/active_record/model_schema.rb +420 -113
  171. data/lib/active_record/nested_attributes.rb +265 -216
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +574 -135
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +175 -54
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +533 -216
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +485 -310
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +326 -244
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +318 -256
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +99 -84
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +139 -96
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -409
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +23 -21
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -342
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -26
  209. data/lib/active_record/scoping/default.rb +96 -82
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +133 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +366 -129
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +291 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +181 -152
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -41
  231. data/lib/active_record/type/date_time.rb +4 -38
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +33 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +65 -48
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +44 -28
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -10
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record.rb +7 -5
  339. metadata +175 -65
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  347. data/lib/active_record/attribute.rb +0 -149
  348. data/lib/active_record/attribute_decorators.rb +0 -66
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -30
  363. data/lib/active_record/type/decimal.rb +0 -40
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -55
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -36
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -101
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  374. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,5 +1,6 @@
1
- require 'thread'
2
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
3
4
 
4
5
  module ActiveRecord
5
6
  # = Active Record Reflection
@@ -7,41 +8,46 @@ module ActiveRecord
7
8
  extend ActiveSupport::Concern
8
9
 
9
10
  included do
10
- class_attribute :_reflections
11
- class_attribute :aggregate_reflections
12
- self._reflections = {}
13
- self.aggregate_reflections = {}
11
+ class_attribute :_reflections, instance_writer: false, default: {}
12
+ class_attribute :aggregate_reflections, instance_writer: false, default: {}
14
13
  end
15
14
 
16
- def self.create(macro, name, scope, options, ar)
17
- klass = case macro
18
- when :composed_of
19
- AggregateReflection
20
- when :has_many
21
- HasManyReflection
22
- when :has_one
23
- HasOneReflection
24
- when :belongs_to
25
- BelongsToReflection
26
- else
27
- raise "Unsupported Macro: #{macro}"
28
- end
29
-
30
- reflection = klass.new(name, scope, options, ar)
31
- options[:through] ? ThroughReflection.new(reflection) : reflection
32
- end
15
+ class << self
16
+ def create(macro, name, scope, options, ar)
17
+ reflection = reflection_class_for(macro).new(name, scope, options, ar)
18
+ options[:through] ? ThroughReflection.new(reflection) : reflection
19
+ end
33
20
 
34
- def self.add_reflection(ar, name, reflection)
35
- ar._reflections = ar._reflections.merge(name.to_s => reflection)
36
- end
21
+ def add_reflection(ar, name, reflection)
22
+ ar.clear_reflections_cache
23
+ name = -name.to_s
24
+ ar._reflections = ar._reflections.except(name).merge!(name => reflection)
25
+ end
37
26
 
38
- def self.add_aggregate_reflection(ar, name, reflection)
39
- ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
27
+ def add_aggregate_reflection(ar, name, reflection)
28
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
29
+ end
30
+
31
+ private
32
+ def reflection_class_for(macro)
33
+ case macro
34
+ when :composed_of
35
+ AggregateReflection
36
+ when :has_many
37
+ HasManyReflection
38
+ when :has_one
39
+ HasOneReflection
40
+ when :belongs_to
41
+ BelongsToReflection
42
+ else
43
+ raise "Unsupported Macro: #{macro}"
44
+ end
45
+ end
40
46
  end
41
47
 
42
- # \Reflection enables interrogating of Active Record classes and objects
43
- # about their associations and aggregations. This information can,
44
- # for example, be used in a form builder that takes an Active Record object
48
+ # \Reflection enables the ability to examine the associations and aggregations of
49
+ # Active Record classes and objects. This information, for example,
50
+ # can be used in a form builder that takes an Active Record object
45
51
  # and creates input fields for all of the attributes depending on their type
46
52
  # and displays the associations to other objects.
47
53
  #
@@ -61,22 +67,27 @@ module ActiveRecord
61
67
  aggregate_reflections[aggregation.to_s]
62
68
  end
63
69
 
64
- # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
70
+ # Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
65
71
  #
66
72
  # Account.reflections # => {"balance" => AggregateReflection}
67
73
  #
68
- # @api public
69
74
  def reflections
70
- ref = {}
71
- _reflections.each do |name, reflection|
72
- parent_name, parent_reflection = reflection.parent_reflection
73
- if parent_name
74
- ref[parent_name] = parent_reflection
75
- else
76
- ref[name] = reflection
75
+ @__reflections ||= begin
76
+ ref = {}
77
+
78
+ _reflections.each do |name, reflection|
79
+ parent_reflection = reflection.parent_reflection
80
+
81
+ if parent_reflection
82
+ parent_name = parent_reflection.name
83
+ ref[parent_name.to_s] = parent_reflection
84
+ else
85
+ ref[name] = reflection
86
+ end
77
87
  end
88
+
89
+ ref
78
90
  end
79
- ref
80
91
  end
81
92
 
82
93
  # Returns an array of AssociationReflection objects for all the
@@ -89,10 +100,10 @@ module ActiveRecord
89
100
  # Account.reflect_on_all_associations # returns an array of all associations
90
101
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
91
102
  #
92
- # @api public
93
103
  def reflect_on_all_associations(macro = nil)
94
104
  association_reflections = reflections.values
95
- macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
105
+ association_reflections.select! { |reflection| reflection.macro == macro } if macro
106
+ association_reflections
96
107
  end
97
108
 
98
109
  # Returns the AssociationReflection object for the +association+ (use the symbol).
@@ -100,27 +111,42 @@ module ActiveRecord
100
111
  # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
101
112
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
102
113
  #
103
- # @api public
104
114
  def reflect_on_association(association)
105
115
  reflections[association.to_s]
106
116
  end
107
117
 
108
- # @api private
109
118
  def _reflect_on_association(association) #:nodoc:
110
119
  _reflections[association.to_s]
111
120
  end
112
121
 
113
122
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
114
- #
115
- # @api public
116
123
  def reflect_on_all_autosave_associations
117
124
  reflections.values.select { |reflection| reflection.options[:autosave] }
118
125
  end
126
+
127
+ def clear_reflections_cache # :nodoc:
128
+ @__reflections = nil
129
+ end
119
130
  end
120
131
 
121
- # Holds all the methods that are shared between MacroReflection, AssociationReflection
122
- # and ThroughReflection
132
+ # Holds all the methods that are shared between MacroReflection and ThroughReflection.
133
+ #
134
+ # AbstractReflection
135
+ # MacroReflection
136
+ # AggregateReflection
137
+ # AssociationReflection
138
+ # HasManyReflection
139
+ # HasOneReflection
140
+ # BelongsToReflection
141
+ # HasAndBelongsToManyReflection
142
+ # ThroughReflection
143
+ # PolymorphicReflection
144
+ # RuntimeReflection
123
145
  class AbstractReflection # :nodoc:
146
+ def through_reflection?
147
+ false
148
+ end
149
+
124
150
  def table_name
125
151
  klass.table_name
126
152
  end
@@ -131,47 +157,159 @@ module ActiveRecord
131
157
  klass.new(attributes, &block)
132
158
  end
133
159
 
134
- def quoted_table_name
135
- klass.quoted_table_name
136
- end
137
-
138
- def primary_key_type
139
- klass.type_for_attribute(klass.primary_key)
140
- end
141
-
142
160
  # Returns the class name for the macro.
143
161
  #
144
162
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
145
163
  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
146
164
  def class_name
147
- @class_name ||= (options[:class_name] || derive_class_name).to_s
165
+ @class_name ||= -(options[:class_name] || derive_class_name).to_s
166
+ end
167
+
168
+ # Returns a list of scopes that should be applied for this Reflection
169
+ # object when querying the database.
170
+ def scopes
171
+ scope ? [scope] : []
172
+ end
173
+
174
+ def join_scope(table, foreign_table, foreign_klass)
175
+ predicate_builder = predicate_builder(table)
176
+ scope_chain_items = join_scopes(table, predicate_builder)
177
+ klass_scope = klass_join_scope(table, predicate_builder)
178
+
179
+ if type
180
+ klass_scope.where!(type => foreign_klass.polymorphic_name)
181
+ end
182
+
183
+ scope_chain_items.inject(klass_scope, &:merge!)
184
+
185
+ primary_key = join_primary_key
186
+ foreign_key = join_foreign_key
187
+
188
+ klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
189
+
190
+ if klass.finder_needs_type_condition?
191
+ klass_scope.where!(klass.send(:type_condition, table))
192
+ end
193
+
194
+ klass_scope
195
+ end
196
+
197
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
198
+ if scope
199
+ [scope_for(build_scope(table, predicate_builder, klass))]
200
+ else
201
+ []
202
+ end
203
+ end
204
+
205
+ def klass_join_scope(table, predicate_builder) # :nodoc:
206
+ relation = build_scope(table, predicate_builder)
207
+ klass.scope_for_association(relation)
208
+ end
209
+
210
+ def constraints
211
+ chain.flat_map(&:scopes)
212
+ end
213
+
214
+ def counter_cache_column
215
+ @counter_cache_column ||= if belongs_to?
216
+ if options[:counter_cache] == true
217
+ -"#{active_record.name.demodulize.underscore.pluralize}_count"
218
+ elsif options[:counter_cache]
219
+ -options[:counter_cache].to_s
220
+ end
221
+ else
222
+ -(options[:counter_cache]&.to_s || "#{name}_count")
223
+ end
224
+ end
225
+
226
+ def inverse_of
227
+ return unless inverse_name
228
+
229
+ @inverse_of ||= klass._reflect_on_association inverse_name
230
+ end
231
+
232
+ def check_validity_of_inverse!
233
+ unless polymorphic?
234
+ if has_inverse? && inverse_of.nil?
235
+ raise InverseOfAssociationNotFoundError.new(self)
236
+ end
237
+ end
238
+ end
239
+
240
+ # This shit is nasty. We need to avoid the following situation:
241
+ #
242
+ # * An associated record is deleted via record.destroy
243
+ # * Hence the callbacks run, and they find a belongs_to on the record with a
244
+ # :counter_cache options which points back at our owner. So they update the
245
+ # counter cache.
246
+ # * In which case, we must make sure to *not* update the counter cache, or else
247
+ # it will be decremented twice.
248
+ #
249
+ # Hence this method.
250
+ def inverse_which_updates_counter_cache
251
+ return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
252
+ @inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
253
+ inverse.counter_cache_column == counter_cache_column
254
+ end
255
+ end
256
+ alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
257
+
258
+ def inverse_updates_counter_in_memory?
259
+ inverse_of && inverse_which_updates_counter_cache == inverse_of
260
+ end
261
+
262
+ # Returns whether a counter cache should be used for this association.
263
+ #
264
+ # The counter_cache option must be given on either the owner or inverse
265
+ # association, and the column must be present on the owner.
266
+ def has_cached_counter?
267
+ options[:counter_cache] ||
268
+ inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
269
+ active_record.has_attribute?(counter_cache_column)
148
270
  end
149
271
 
150
- JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
272
+ def counter_must_be_updated_by_has_many?
273
+ !inverse_updates_counter_in_memory? && has_cached_counter?
274
+ end
151
275
 
152
- def join_keys(assoc_klass)
153
- JoinKeys.new(foreign_key, active_record_primary_key)
276
+ def alias_candidate(name)
277
+ "#{plural_name}_#{name}"
154
278
  end
155
279
 
156
- def source_macro
157
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
158
- ActiveRecord::Base.source_macro is deprecated and will be removed
159
- without replacement.
160
- MSG
280
+ def chain
281
+ collect_join_chain
282
+ end
161
283
 
162
- macro
284
+ def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
285
+ Relation.create(
286
+ klass,
287
+ table: table,
288
+ predicate_builder: predicate_builder
289
+ )
163
290
  end
291
+
292
+ def strict_loading?
293
+ options[:strict_loading]
294
+ end
295
+
296
+ protected
297
+ def actual_source_reflection # FIXME: this is a horrible name
298
+ self
299
+ end
300
+
301
+ private
302
+ def predicate_builder(table)
303
+ PredicateBuilder.new(TableMetadata.new(klass, table))
304
+ end
305
+
306
+ def primary_key(klass)
307
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
308
+ end
164
309
  end
310
+
165
311
  # Base class for AggregateReflection and AssociationReflection. Objects of
166
312
  # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
167
- #
168
- # MacroReflection
169
- # AssociationReflection
170
- # AggregateReflection
171
- # HasManyReflection
172
- # HasOneReflection
173
- # BelongsToReflection
174
- # ThroughReflection
175
313
  class MacroReflection < AbstractReflection
176
314
  # Returns the name of the macro.
177
315
  #
@@ -196,15 +334,14 @@ module ActiveRecord
196
334
  @scope = scope
197
335
  @options = options
198
336
  @active_record = active_record
199
- @klass = options[:class]
337
+ @klass = options[:anonymous_class]
200
338
  @plural_name = active_record.pluralize_table_names ?
201
339
  name.to_s.pluralize : name.to_s
202
340
  end
203
341
 
204
342
  def autosave=(autosave)
205
- @automatic_inverse_of = false
206
343
  @options[:autosave] = autosave
207
- _, parent_reflection = self.parent_reflection
344
+ parent_reflection = self.parent_reflection
208
345
  if parent_reflection
209
346
  parent_reflection.autosave = autosave
210
347
  end
@@ -214,6 +351,17 @@ module ActiveRecord
214
351
  #
215
352
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
216
353
  # <tt>has_many :clients</tt> returns the Client class
354
+ #
355
+ # class Company < ActiveRecord::Base
356
+ # has_many :clients
357
+ # end
358
+ #
359
+ # Company.reflect_on_association(:clients).klass
360
+ # # => Client
361
+ #
362
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
363
+ # a new association object. Use +build_association+ or +create_association+
364
+ # instead. This allows plugins to hook into association object creation.
217
365
  def klass
218
366
  @klass ||= compute_class(class_name)
219
367
  end
@@ -232,14 +380,17 @@ module ActiveRecord
232
380
  active_record == other_aggregation.active_record
233
381
  end
234
382
 
383
+ def scope_for(relation, owner = nil)
384
+ relation.instance_exec(owner, &scope) || relation
385
+ end
386
+
235
387
  private
236
388
  def derive_class_name
237
389
  name.to_s.camelize
238
390
  end
239
391
  end
240
392
 
241
-
242
- # Holds all the meta-data about an aggregation as it was specified in the
393
+ # Holds all the metadata about an aggregation as it was specified in the
243
394
  # Active Record class.
244
395
  class AggregateReflection < MacroReflection #:nodoc:
245
396
  def mapping
@@ -248,50 +399,36 @@ module ActiveRecord
248
399
  end
249
400
  end
250
401
 
251
- # Holds all the meta-data about an association as it was specified in the
402
+ # Holds all the metadata about an association as it was specified in the
252
403
  # Active Record class.
253
404
  class AssociationReflection < MacroReflection #:nodoc:
254
- # Returns the target association's class.
255
- #
256
- # class Author < ActiveRecord::Base
257
- # has_many :books
258
- # end
259
- #
260
- # Author.reflect_on_association(:books).klass
261
- # # => Book
262
- #
263
- # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
264
- # a new association object. Use +build_association+ or +create_association+
265
- # instead. This allows plugins to hook into association object creation.
266
- def klass
267
- @klass ||= compute_class(class_name)
268
- end
269
-
270
405
  def compute_class(name)
406
+ if polymorphic?
407
+ raise ArgumentError, "Polymorphic associations do not support computing the class."
408
+ end
271
409
  active_record.send(:compute_type, name)
272
410
  end
273
411
 
274
412
  attr_reader :type, :foreign_type
275
- attr_accessor :parent_reflection # [:name, Reflection]
413
+ attr_accessor :parent_reflection # Reflection
276
414
 
277
415
  def initialize(name, scope, options, active_record)
278
416
  super
279
- @automatic_inverse_of = nil
280
- @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
281
- @foreign_type = options[:foreign_type] || "#{name}_type"
417
+ @type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
418
+ @foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
282
419
  @constructable = calculate_constructable(macro, options)
283
- @association_scope_cache = {}
284
- @scope_lock = Mutex.new
420
+
421
+ if options[:class_name] && options[:class_name].class == Class
422
+ raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
423
+ end
285
424
  end
286
425
 
287
- def association_scope_cache(conn, owner)
288
- key = conn.prepared_statements
426
+ def association_scope_cache(klass, owner, &block)
427
+ key = self
289
428
  if polymorphic?
290
429
  key = [key, owner._read_attribute(@foreign_type)]
291
430
  end
292
- @association_scope_cache[key] ||= @scope_lock.synchronize {
293
- @association_scope_cache[key] ||= yield
294
- }
431
+ klass.cached_find_by_statement(key, &block)
295
432
  end
296
433
 
297
434
  def constructable? # :nodoc:
@@ -299,64 +436,52 @@ module ActiveRecord
299
436
  end
300
437
 
301
438
  def join_table
302
- @join_table ||= options[:join_table] || derive_join_table
439
+ @join_table ||= -(options[:join_table]&.to_s || derive_join_table)
303
440
  end
304
441
 
305
442
  def foreign_key
306
- @foreign_key ||= options[:foreign_key] || derive_foreign_key
443
+ @foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
307
444
  end
308
445
 
309
446
  def association_foreign_key
310
- @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
447
+ @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
311
448
  end
312
449
 
313
- # klass option is necessary to support loading polymorphic associations
314
450
  def association_primary_key(klass = nil)
315
- options[:primary_key] || primary_key(klass || self.klass)
451
+ primary_key(klass || self.klass)
316
452
  end
317
453
 
318
454
  def active_record_primary_key
319
- @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
455
+ @active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
320
456
  end
321
457
 
322
- def counter_cache_column
323
- if options[:counter_cache] == true
324
- "#{active_record.name.demodulize.underscore.pluralize}_count"
325
- elsif options[:counter_cache]
326
- options[:counter_cache].to_s
327
- end
458
+ def join_primary_key(klass = nil)
459
+ foreign_key
328
460
  end
329
461
 
330
- def check_validity!
331
- check_validity_of_inverse!
462
+ def join_foreign_key
463
+ active_record_primary_key
332
464
  end
333
465
 
334
- def check_validity_of_inverse!
335
- unless polymorphic?
336
- if has_inverse? && inverse_of.nil?
337
- raise InverseOfAssociationNotFoundError.new(self)
338
- end
339
- end
466
+ def check_validity!
467
+ check_validity_of_inverse!
340
468
  end
341
469
 
342
470
  def check_preloadable!
343
471
  return unless scope
344
472
 
345
- if scope.arity > 0
346
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
473
+ unless scope.arity == 0
474
+ raise ArgumentError, <<-MSG.squish
347
475
  The association scope '#{name}' is instance dependent (the scope
348
- block takes an argument). Preloading happens before the individual
349
- instances are created. This means that there is no instance being
350
- passed to the association scope. This will most likely result in
351
- broken or incorrect behavior. Joining, Preloading and eager loading
352
- of these associations is deprecated and will be removed in the future.
476
+ block takes an argument). Preloading instance dependent scopes is
477
+ not supported.
353
478
  MSG
354
479
  end
355
480
  end
356
481
  alias :check_eager_loadable! :check_preloadable!
357
482
 
358
483
  def join_id_for(owner) # :nodoc:
359
- owner[active_record_primary_key]
484
+ owner[join_foreign_key]
360
485
  end
361
486
 
362
487
  def through_reflection
@@ -369,30 +494,28 @@ module ActiveRecord
369
494
 
370
495
  # A chain of reflections from this one back to the owner. For more see the explanation in
371
496
  # ThroughReflection.
372
- def chain
497
+ def collect_join_chain
373
498
  [self]
374
499
  end
375
500
 
501
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
502
+ # SQL queries on associations.
503
+ def clear_association_scope_cache # :nodoc:
504
+ klass.initialize_find_by_cache
505
+ end
506
+
376
507
  def nested?
377
508
  false
378
509
  end
379
510
 
380
- # An array of arrays of scopes. Each item in the outside array corresponds to a reflection
381
- # in the #chain.
382
- def scope_chain
383
- scope ? [[scope]] : [[]]
511
+ def has_scope?
512
+ scope
384
513
  end
385
514
 
386
515
  def has_inverse?
387
516
  inverse_name
388
517
  end
389
518
 
390
- def inverse_of
391
- return unless inverse_name
392
-
393
- @inverse_of ||= klass._reflect_on_association inverse_name
394
- end
395
-
396
519
  def polymorphic_inverse_of(associated_class)
397
520
  if has_inverse?
398
521
  if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
@@ -434,72 +557,51 @@ module ActiveRecord
434
557
  # Returns +true+ if +self+ is a +has_one+ reflection.
435
558
  def has_one?; false; end
436
559
 
437
- def association_class
438
- case macro
439
- when :belongs_to
440
- if polymorphic?
441
- Associations::BelongsToPolymorphicAssociation
442
- else
443
- Associations::BelongsToAssociation
444
- end
445
- when :has_many
446
- if options[:through]
447
- Associations::HasManyThroughAssociation
448
- else
449
- Associations::HasManyAssociation
450
- end
451
- when :has_one
452
- if options[:through]
453
- Associations::HasOneThroughAssociation
454
- else
455
- Associations::HasOneAssociation
456
- end
457
- end
458
- end
560
+ def association_class; raise NotImplementedError; end
459
561
 
460
562
  def polymorphic?
461
563
  options[:polymorphic]
462
564
  end
463
565
 
464
566
  VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
465
- INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
567
+ INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
466
568
 
467
- protected
569
+ def add_as_source(seed)
570
+ seed
571
+ end
468
572
 
469
- def actual_source_reflection # FIXME: this is a horrible name
470
- self
471
- end
573
+ def add_as_polymorphic_through(reflection, seed)
574
+ seed + [PolymorphicReflection.new(self, reflection)]
575
+ end
472
576
 
473
- private
577
+ def add_as_through(seed)
578
+ seed + [self]
579
+ end
580
+
581
+ def extensions
582
+ Array(options[:extend])
583
+ end
474
584
 
585
+ private
475
586
  def calculate_constructable(macro, options)
476
- case macro
477
- when :belongs_to
478
- !polymorphic?
479
- when :has_one
480
- !options[:through]
481
- else
482
- true
483
- end
587
+ true
484
588
  end
485
589
 
486
590
  # Attempts to find the inverse association name automatically.
487
591
  # If it cannot find a suitable inverse association name, it returns
488
- # nil.
592
+ # +nil+.
489
593
  def inverse_name
490
- options.fetch(:inverse_of) do
491
- if @automatic_inverse_of == false
492
- nil
493
- else
494
- @automatic_inverse_of ||= automatic_inverse_of
495
- end
594
+ unless defined?(@inverse_name)
595
+ @inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
496
596
  end
597
+
598
+ @inverse_name
497
599
  end
498
600
 
499
- # returns either nil or the inverse association name that it finds.
601
+ # returns either +nil+ or the inverse association name that it finds.
500
602
  def automatic_inverse_of
501
603
  if can_find_inverse_of_automatically?(self)
502
- inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
604
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
503
605
 
504
606
  begin
505
607
  reflection = klass._reflect_on_association(inverse_name)
@@ -510,23 +612,19 @@ module ActiveRecord
510
612
  end
511
613
 
512
614
  if valid_inverse_reflection?(reflection)
513
- return inverse_name
615
+ inverse_name
514
616
  end
515
617
  end
516
-
517
- false
518
618
  end
519
619
 
520
620
  # Checks if the inverse reflection that is returned from the
521
621
  # +automatic_inverse_of+ method is a valid reflection. We must
522
622
  # make sure that the reflection's active_record name matches up
523
623
  # with the current reflection's klass name.
524
- #
525
- # Note: klass will always be valid because when there's a NameError
526
- # from calling +klass+, +reflection+ will already be set to false.
527
624
  def valid_inverse_reflection?(reflection)
528
625
  reflection &&
529
- klass.name == reflection.active_record.name &&
626
+ foreign_key == reflection.foreign_key &&
627
+ klass <= reflection.active_record &&
530
628
  can_find_inverse_of_automatically?(reflection)
531
629
  end
532
630
 
@@ -534,9 +632,8 @@ module ActiveRecord
534
632
  # us from being able to guess the inverse automatically. First, the
535
633
  # <tt>inverse_of</tt> option cannot be set to false. Second, we must
536
634
  # have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
537
- # Third, we must not have options such as <tt>:polymorphic</tt> or
538
- # <tt>:foreign_key</tt> which prevent us from correctly guessing the
539
- # inverse association.
635
+ # Third, we must not have options such as <tt>:foreign_key</tt>
636
+ # which prevent us from correctly guessing the inverse association.
540
637
  #
541
638
  # Anything with a scope can additionally ruin our attempt at finding an
542
639
  # inverse, so we exclude reflections with scopes.
@@ -566,56 +663,86 @@ module ActiveRecord
566
663
  def derive_join_table
567
664
  ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
568
665
  end
569
-
570
- def primary_key(klass)
571
- klass.primary_key || raise(UnknownPrimaryKey.new(klass))
572
- end
573
666
  end
574
667
 
575
668
  class HasManyReflection < AssociationReflection # :nodoc:
576
- def initialize(name, scope, options, active_record)
577
- super(name, scope, options, active_record)
578
- end
579
-
580
669
  def macro; :has_many; end
581
670
 
582
671
  def collection?; true; end
583
- end
584
672
 
585
- class HasOneReflection < AssociationReflection # :nodoc:
586
- def initialize(name, scope, options, active_record)
587
- super(name, scope, options, active_record)
673
+ def association_class
674
+ if options[:through]
675
+ Associations::HasManyThroughAssociation
676
+ else
677
+ Associations::HasManyAssociation
678
+ end
588
679
  end
680
+ end
589
681
 
682
+ class HasOneReflection < AssociationReflection # :nodoc:
590
683
  def macro; :has_one; end
591
684
 
592
685
  def has_one?; true; end
593
- end
594
686
 
595
- class BelongsToReflection < AssociationReflection # :nodoc:
596
- def initialize(name, scope, options, active_record)
597
- super(name, scope, options, active_record)
687
+ def association_class
688
+ if options[:through]
689
+ Associations::HasOneThroughAssociation
690
+ else
691
+ Associations::HasOneAssociation
692
+ end
598
693
  end
599
694
 
695
+ private
696
+ def calculate_constructable(macro, options)
697
+ !options[:through]
698
+ end
699
+ end
700
+
701
+ class BelongsToReflection < AssociationReflection # :nodoc:
600
702
  def macro; :belongs_to; end
601
703
 
602
704
  def belongs_to?; true; end
603
705
 
604
- def join_keys(assoc_klass)
605
- key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
606
- JoinKeys.new(key, foreign_key)
706
+ def association_class
707
+ if polymorphic?
708
+ Associations::BelongsToPolymorphicAssociation
709
+ else
710
+ Associations::BelongsToAssociation
711
+ end
607
712
  end
608
713
 
609
- def join_id_for(owner) # :nodoc:
610
- owner[foreign_key]
714
+ # klass option is necessary to support loading polymorphic associations
715
+ def association_primary_key(klass = nil)
716
+ if primary_key = options[:primary_key]
717
+ @association_primary_key ||= -primary_key.to_s
718
+ else
719
+ primary_key(klass || self.klass)
720
+ end
611
721
  end
612
- end
613
722
 
614
- class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
615
- def initialize(name, scope, options, active_record)
616
- super
723
+ def join_primary_key(klass = nil)
724
+ polymorphic? ? association_primary_key(klass) : association_primary_key
725
+ end
726
+
727
+ def join_foreign_key
728
+ foreign_key
729
+ end
730
+
731
+ def join_foreign_type
732
+ foreign_type
617
733
  end
618
734
 
735
+ private
736
+ def can_find_inverse_of_automatically?(_)
737
+ !polymorphic? && super
738
+ end
739
+
740
+ def calculate_constructable(macro, options)
741
+ !polymorphic?
742
+ end
743
+ end
744
+
745
+ class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
619
746
  def macro; :has_and_belongs_to_many; end
620
747
 
621
748
  def collection?
@@ -623,19 +750,22 @@ module ActiveRecord
623
750
  end
624
751
  end
625
752
 
626
- # Holds all the meta-data about a :through association as it was specified
753
+ # Holds all the metadata about a :through association as it was specified
627
754
  # in the Active Record class.
628
755
  class ThroughReflection < AbstractReflection #:nodoc:
629
- attr_reader :delegate_reflection
630
- delegate :foreign_key, :foreign_type, :association_foreign_key,
631
- :active_record_primary_key, :type, :to => :source_reflection
756
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
757
+ :active_record_primary_key, :join_foreign_key, to: :source_reflection
632
758
 
633
759
  def initialize(delegate_reflection)
634
760
  @delegate_reflection = delegate_reflection
635
- @klass = delegate_reflection.options[:class]
761
+ @klass = delegate_reflection.options[:anonymous_class]
636
762
  @source_reflection_name = delegate_reflection.options[:source]
637
763
  end
638
764
 
765
+ def through_reflection?
766
+ true
767
+ end
768
+
639
769
  def klass
640
770
  @klass ||= delegate_reflection.compute_class(class_name)
641
771
  end
@@ -694,74 +824,35 @@ module ActiveRecord
694
824
  # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
695
825
  # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
696
826
  #
697
- def chain
698
- @chain ||= begin
699
- a = source_reflection.chain
700
- b = through_reflection.chain
701
- chain = a + b
702
- chain[0] = self # Use self so we don't lose the information from :source_type
703
- chain
704
- end
827
+ def collect_join_chain
828
+ collect_join_reflections [self]
705
829
  end
706
830
 
707
- # Consider the following example:
708
- #
709
- # class Person
710
- # has_many :articles
711
- # has_many :comment_tags, through: :articles
712
- # end
713
- #
714
- # class Article
715
- # has_many :comments
716
- # has_many :comment_tags, through: :comments, source: :tags
717
- # end
718
- #
719
- # class Comment
720
- # has_many :tags
721
- # end
722
- #
723
- # There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
724
- # but only Comment.tags will be represented in the #chain. So this method creates an array
725
- # of scopes corresponding to the chain.
726
- def scope_chain
727
- @scope_chain ||= begin
728
- scope_chain = source_reflection.scope_chain.map(&:dup)
729
-
730
- # Add to it the scope from this reflection (if any)
731
- scope_chain.first << scope if scope
732
-
733
- through_scope_chain = through_reflection.scope_chain.map(&:dup)
734
-
735
- if options[:source_type]
736
- type = foreign_type
737
- source_type = options[:source_type]
738
- through_scope_chain.first << lambda { |object|
739
- where(type => source_type)
740
- }
741
- end
742
-
743
- # Recursively fill out the rest of the array from the through reflection
744
- scope_chain + through_scope_chain
745
- end
831
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
832
+ # SQL queries on associations.
833
+ def clear_association_scope_cache # :nodoc:
834
+ delegate_reflection.clear_association_scope_cache
835
+ source_reflection.clear_association_scope_cache
836
+ through_reflection.clear_association_scope_cache
746
837
  end
747
838
 
748
- def join_keys(assoc_klass)
749
- source_reflection.join_keys(assoc_klass)
839
+ def scopes
840
+ source_reflection.scopes + super
750
841
  end
751
842
 
752
- # The macro used by the source association
753
- def source_macro
754
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
755
- ActiveRecord::Base.source_macro is deprecated and will be removed
756
- without replacement.
757
- MSG
843
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
844
+ source_reflection.join_scopes(table, predicate_builder, klass) + super
845
+ end
758
846
 
759
- source_reflection.source_macro
847
+ def has_scope?
848
+ scope || options[:source_type] ||
849
+ source_reflection.has_scope? ||
850
+ through_reflection.has_scope?
760
851
  end
761
852
 
762
853
  # A through association is nested if there would be more than one join table
763
854
  def nested?
764
- chain.length > 2
855
+ source_reflection.through_reflection? || through_reflection.through_reflection?
765
856
  end
766
857
 
767
858
  # We want to use the klass from this reflection, rather than just delegate straight to
@@ -770,7 +861,15 @@ module ActiveRecord
770
861
  def association_primary_key(klass = nil)
771
862
  # Get the "actual" source reflection if the immediate source reflection has a
772
863
  # source reflection itself
773
- actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
864
+ if primary_key = actual_source_reflection.options[:primary_key]
865
+ @association_primary_key ||= -primary_key.to_s
866
+ else
867
+ primary_key(klass || self.klass)
868
+ end
869
+ end
870
+
871
+ def join_primary_key(klass = self.klass)
872
+ source_reflection.join_primary_key(klass)
774
873
  end
775
874
 
776
875
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
@@ -791,21 +890,19 @@ module ActiveRecord
791
890
  def source_reflection_name # :nodoc:
792
891
  return @source_reflection_name if @source_reflection_name
793
892
 
794
- names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
893
+ names = [name.to_s.singularize, name].collect(&:to_sym).uniq
795
894
  names = names.find_all { |n|
796
895
  through_reflection.klass._reflect_on_association(n)
797
896
  }
798
897
 
799
898
  if names.length > 1
800
- example_options = options.dup
801
- example_options[:source] = source_reflection_names.first
802
- ActiveSupport::Deprecation.warn \
803
- "Ambiguous source reflection for through association. Please " \
804
- "specify a :source directive on your declaration like:\n" \
805
- "\n" \
806
- " class #{active_record.name} < ActiveRecord::Base\n" \
807
- " #{macro} :#{name}, #{example_options}\n" \
808
- " end"
899
+ raise AmbiguousSourceReflectionForThroughAssociation.new(
900
+ active_record.name,
901
+ macro,
902
+ name,
903
+ options,
904
+ source_reflection_names
905
+ )
809
906
  end
810
907
 
811
908
  @source_reflection_name = names.first
@@ -819,13 +916,9 @@ module ActiveRecord
819
916
  through_reflection.options
820
917
  end
821
918
 
822
- def join_id_for(owner) # :nodoc:
823
- source_reflection.join_id_for(owner)
824
- end
825
-
826
919
  def check_validity!
827
920
  if through_reflection.nil?
828
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
921
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
829
922
  end
830
923
 
831
924
  if through_reflection.polymorphic?
@@ -852,20 +945,54 @@ module ActiveRecord
852
945
  raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
853
946
  end
854
947
 
948
+ if parent_reflection.nil?
949
+ reflections = active_record.reflections.keys.map(&:to_sym)
950
+
951
+ if reflections.index(through_reflection.name) > reflections.index(name)
952
+ raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
953
+ end
954
+ end
955
+
855
956
  check_validity_of_inverse!
856
957
  end
857
958
 
858
- protected
959
+ def constraints
960
+ scope_chain = source_reflection.constraints
961
+ scope_chain << scope if scope
962
+ scope_chain
963
+ end
964
+
965
+ def add_as_source(seed)
966
+ collect_join_reflections seed
967
+ end
968
+
969
+ def add_as_polymorphic_through(reflection, seed)
970
+ collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
971
+ end
972
+
973
+ def add_as_through(seed)
974
+ collect_join_reflections(seed + [self])
975
+ end
859
976
 
977
+ protected
860
978
  def actual_source_reflection # FIXME: this is a horrible name
861
- source_reflection.send(:actual_source_reflection)
979
+ source_reflection.actual_source_reflection
862
980
  end
863
981
 
864
- def primary_key(klass)
865
- klass.primary_key || raise(UnknownPrimaryKey.new(klass))
982
+ private
983
+ attr_reader :delegate_reflection
984
+
985
+ def collect_join_reflections(seed)
986
+ a = source_reflection.add_as_source seed
987
+ if options[:source_type]
988
+ through_reflection.add_as_polymorphic_through self, a
989
+ else
990
+ through_reflection.add_as_through a
991
+ end
866
992
  end
867
993
 
868
- private
994
+ def inverse_name; delegate_reflection.send(:inverse_name); end
995
+
869
996
  def derive_class_name
870
997
  # get the class_name of the belongs_to association of the through reflection
871
998
  options[:source_type] || source_reflection.class_name
@@ -875,7 +1002,55 @@ module ActiveRecord
875
1002
  public_instance_methods
876
1003
 
877
1004
  delegate(*delegate_methods, to: :delegate_reflection)
1005
+ end
1006
+
1007
+ class PolymorphicReflection < AbstractReflection # :nodoc:
1008
+ delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
1009
+ :name, :scope_for, to: :@reflection
1010
+
1011
+ def initialize(reflection, previous_reflection)
1012
+ @reflection = reflection
1013
+ @previous_reflection = previous_reflection
1014
+ end
1015
+
1016
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
1017
+ scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
1018
+ scopes << build_scope(table, predicate_builder, klass).instance_exec(nil, &source_type_scope)
1019
+ end
1020
+
1021
+ def constraints
1022
+ @reflection.constraints + [source_type_scope]
1023
+ end
1024
+
1025
+ private
1026
+ def source_type_scope
1027
+ type = @previous_reflection.foreign_type
1028
+ source_type = @previous_reflection.options[:source_type]
1029
+ lambda { |object| where(type => source_type) }
1030
+ end
1031
+ end
1032
+
1033
+ class RuntimeReflection < AbstractReflection # :nodoc:
1034
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1035
+
1036
+ def initialize(reflection, association)
1037
+ @reflection = reflection
1038
+ @association = association
1039
+ end
1040
+
1041
+ def klass
1042
+ @association.klass
1043
+ end
1044
+
1045
+ def aliased_table
1046
+ klass.arel_table
1047
+ end
1048
+
1049
+ def join_primary_key(klass = self.klass)
1050
+ @reflection.join_primary_key(klass)
1051
+ end
878
1052
 
1053
+ def all_includes; yield; end
879
1054
  end
880
1055
  end
881
1056
  end