activerecord 3.2.6 → 6.0.0

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