activerecord 6.0.1

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 (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1086 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +195 -0
  8. data/lib/active_record/aggregations.rb +285 -0
  9. data/lib/active_record/association_relation.rb +49 -0
  10. data/lib/active_record/associations.rb +1865 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +340 -0
  13. data/lib/active_record/associations/association_scope.rb +166 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +124 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
  16. data/lib/active_record/associations/builder/association.rb +136 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +130 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +72 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
  20. data/lib/active_record/associations/builder/has_many.rb +19 -0
  21. data/lib/active_record/associations/builder/has_one.rb +64 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +44 -0
  23. data/lib/active_record/associations/collection_association.rb +498 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1128 -0
  25. data/lib/active_record/associations/foreign_association.rb +20 -0
  26. data/lib/active_record/associations/has_many_association.rb +136 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +220 -0
  28. data/lib/active_record/associations/has_one_association.rb +118 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +262 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +201 -0
  35. data/lib/active_record/associations/preloader/association.rb +133 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +116 -0
  37. data/lib/active_record/associations/singular_association.rb +59 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +85 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +420 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +221 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +136 -0
  45. data/lib/active_record/attribute_methods/query.rb +41 -0
  46. data/lib/active_record/attribute_methods/read.rb +47 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +61 -0
  50. data/lib/active_record/attributes.rb +279 -0
  51. data/lib/active_record/autosave_association.rb +512 -0
  52. data/lib/active_record/base.rb +328 -0
  53. data/lib/active_record/callbacks.rb +339 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1175 -0
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +516 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +155 -0
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +772 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +830 -0
  69. data/lib/active_record/connection_adapters/column.rb +95 -0
  70. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +146 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +184 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +953 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +561 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +274 -0
  129. data/lib/active_record/core.rb +603 -0
  130. data/lib/active_record/counter_cache.rb +193 -0
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +122 -0
  137. data/lib/active_record/enum.rb +274 -0
  138. data/lib/active_record/errors.rb +388 -0
  139. data/lib/active_record/explain.rb +50 -0
  140. data/lib/active_record/explain_registry.rb +32 -0
  141. data/lib/active_record/explain_subscriber.rb +34 -0
  142. data/lib/active_record/fixture_set/file.rb +82 -0
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +738 -0
  148. data/lib/active_record/gem_version.rb +17 -0
  149. data/lib/active_record/inheritance.rb +293 -0
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +207 -0
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +48 -0
  155. data/lib/active_record/locking/optimistic.rb +197 -0
  156. data/lib/active_record/locking/pessimistic.rb +89 -0
  157. data/lib/active_record/log_subscriber.rb +118 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +1397 -0
  162. data/lib/active_record/migration/command_recorder.rb +284 -0
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +17 -0
  165. data/lib/active_record/model_schema.rb +545 -0
  166. data/lib/active_record/nested_attributes.rb +600 -0
  167. data/lib/active_record/no_touching.rb +65 -0
  168. data/lib/active_record/null_relation.rb +68 -0
  169. data/lib/active_record/persistence.rb +967 -0
  170. data/lib/active_record/query_cache.rb +52 -0
  171. data/lib/active_record/querying.rb +82 -0
  172. data/lib/active_record/railtie.rb +263 -0
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +7 -0
  175. data/lib/active_record/railties/controller_runtime.rb +51 -0
  176. data/lib/active_record/railties/databases.rake +527 -0
  177. data/lib/active_record/readonly_attributes.rb +24 -0
  178. data/lib/active_record/reflection.rb +1042 -0
  179. data/lib/active_record/relation.rb +860 -0
  180. data/lib/active_record/relation/batches.rb +290 -0
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +424 -0
  183. data/lib/active_record/relation/delegation.rb +130 -0
  184. data/lib/active_record/relation/finder_methods.rb +561 -0
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +184 -0
  187. data/lib/active_record/relation/predicate_builder.rb +150 -0
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +1371 -0
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +77 -0
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +168 -0
  202. data/lib/active_record/runtime_registry.rb +24 -0
  203. data/lib/active_record/sanitization.rb +214 -0
  204. data/lib/active_record/schema.rb +61 -0
  205. data/lib/active_record/schema_dumper.rb +270 -0
  206. data/lib/active_record/schema_migration.rb +60 -0
  207. data/lib/active_record/scoping.rb +106 -0
  208. data/lib/active_record/scoping/default.rb +151 -0
  209. data/lib/active_record/scoping/named.rb +217 -0
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +22 -0
  212. data/lib/active_record/statement_cache.rb +148 -0
  213. data/lib/active_record/store.rb +290 -0
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +506 -0
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +167 -0
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +493 -0
  225. data/lib/active_record/translation.rb +24 -0
  226. data/lib/active_record/type.rb +78 -0
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +9 -0
  229. data/lib/active_record/type/date_time.rb +9 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +71 -0
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +21 -0
  237. data/lib/active_record/type/type_map.rb +62 -0
  238. data/lib/active_record/type/unsigned_integer.rb +17 -0
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +94 -0
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +60 -0
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +68 -0
  247. data/lib/active_record/validations/uniqueness.rb +226 -0
  248. data/lib/active_record/version.rb +10 -0
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +19 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +48 -0
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  337. data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  340. metadata +418 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ # = Active Record Has One Through Association
6
+ class HasOneThroughAssociation < HasOneAssociation #:nodoc:
7
+ include ThroughAssociation
8
+
9
+ private
10
+ def replace(record, save = true)
11
+ create_through_record(record, save)
12
+ self.target = record
13
+ end
14
+
15
+ def create_through_record(record, save)
16
+ ensure_not_nested
17
+
18
+ through_proxy = through_association
19
+ through_record = through_proxy.load_target
20
+
21
+ if through_record && !record
22
+ through_record.destroy
23
+ elsif record
24
+ attributes = construct_join_attributes(record)
25
+
26
+ if through_record && through_record.destroyed?
27
+ through_record = through_proxy.tap(&:reload).target
28
+ end
29
+
30
+ if through_record
31
+ if through_record.new_record?
32
+ through_record.assign_attributes(attributes)
33
+ else
34
+ through_record.update(attributes)
35
+ end
36
+ elsif owner.new_record? || !save
37
+ through_proxy.build(attributes)
38
+ else
39
+ through_proxy.create(attributes)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class JoinDependency # :nodoc:
6
+ autoload :JoinBase, "active_record/associations/join_dependency/join_base"
7
+ autoload :JoinAssociation, "active_record/associations/join_dependency/join_association"
8
+
9
+ class Aliases # :nodoc:
10
+ def initialize(tables)
11
+ @tables = tables
12
+ @alias_cache = tables.each_with_object({}) { |table, h|
13
+ h[table.node] = table.columns.each_with_object({}) { |column, i|
14
+ i[column.name] = column.alias
15
+ }
16
+ }
17
+ @columns_cache = tables.each_with_object({}) { |table, h|
18
+ h[table.node] = table.columns
19
+ }
20
+ end
21
+
22
+ def columns
23
+ @tables.flat_map(&:column_aliases)
24
+ end
25
+
26
+ def column_aliases(node)
27
+ @columns_cache[node]
28
+ end
29
+
30
+ def column_alias(node, column)
31
+ @alias_cache[node][column]
32
+ end
33
+
34
+ Table = Struct.new(:node, :columns) do # :nodoc:
35
+ def column_aliases
36
+ t = node.table
37
+ columns.map { |column| t[column.name].as Arel.sql column.alias }
38
+ end
39
+ end
40
+ Column = Struct.new(:name, :alias)
41
+ end
42
+
43
+ def self.make_tree(associations)
44
+ hash = {}
45
+ walk_tree associations, hash
46
+ hash
47
+ end
48
+
49
+ def self.walk_tree(associations, hash)
50
+ case associations
51
+ when Symbol, String
52
+ hash[associations.to_sym] ||= {}
53
+ when Array
54
+ associations.each do |assoc|
55
+ walk_tree assoc, hash
56
+ end
57
+ when Hash
58
+ associations.each do |k, v|
59
+ cache = hash[k] ||= {}
60
+ walk_tree v, cache
61
+ end
62
+ else
63
+ raise ConfigurationError, associations.inspect
64
+ end
65
+ end
66
+
67
+ def initialize(base, table, associations, join_type)
68
+ tree = self.class.make_tree associations
69
+ @join_root = JoinBase.new(base, table, build(tree, base))
70
+ @join_type = join_type
71
+ end
72
+
73
+ def base_klass
74
+ join_root.base_klass
75
+ end
76
+
77
+ def reflections
78
+ join_root.drop(1).map!(&:reflection)
79
+ end
80
+
81
+ def join_constraints(joins_to_add, alias_tracker)
82
+ @alias_tracker = alias_tracker
83
+
84
+ construct_tables!(join_root)
85
+ joins = make_join_constraints(join_root, join_type)
86
+
87
+ joins.concat joins_to_add.flat_map { |oj|
88
+ construct_tables!(oj.join_root)
89
+ if join_root.match? oj.join_root
90
+ walk(join_root, oj.join_root, oj.join_type)
91
+ else
92
+ make_join_constraints(oj.join_root, oj.join_type)
93
+ end
94
+ }
95
+ end
96
+
97
+ def instantiate(result_set, &block)
98
+ primary_key = aliases.column_alias(join_root, join_root.primary_key)
99
+
100
+ seen = Hash.new { |i, object_id|
101
+ i[object_id] = Hash.new { |j, child_class|
102
+ j[child_class] = {}
103
+ }
104
+ }
105
+
106
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
107
+ parents = model_cache[join_root]
108
+ column_aliases = aliases.column_aliases join_root
109
+
110
+ message_bus = ActiveSupport::Notifications.instrumenter
111
+
112
+ payload = {
113
+ record_count: result_set.length,
114
+ class_name: join_root.base_klass.name
115
+ }
116
+
117
+ message_bus.instrument("instantiation.active_record", payload) do
118
+ result_set.each { |row_hash|
119
+ parent_key = primary_key ? row_hash[primary_key] : row_hash
120
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
121
+ construct(parent, join_root, row_hash, seen, model_cache)
122
+ }
123
+ end
124
+
125
+ parents.values
126
+ end
127
+
128
+ def apply_column_aliases(relation)
129
+ relation._select!(-> { aliases.columns })
130
+ end
131
+
132
+ protected
133
+ attr_reader :join_root, :join_type
134
+
135
+ private
136
+ attr_reader :alias_tracker
137
+
138
+ def aliases
139
+ @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
140
+ columns = join_part.column_names.each_with_index.map { |column_name, j|
141
+ Aliases::Column.new column_name, "t#{i}_r#{j}"
142
+ }
143
+ Aliases::Table.new(join_part, columns)
144
+ }
145
+ end
146
+
147
+ def construct_tables!(join_root)
148
+ join_root.each_children do |parent, child|
149
+ child.tables = table_aliases_for(parent, child)
150
+ end
151
+ end
152
+
153
+ def make_join_constraints(join_root, join_type)
154
+ join_root.children.flat_map do |child|
155
+ make_constraints(join_root, child, join_type)
156
+ end
157
+ end
158
+
159
+ def make_constraints(parent, child, join_type)
160
+ foreign_table = parent.table
161
+ foreign_klass = parent.base_klass
162
+ joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
163
+ joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
164
+ end
165
+
166
+ def table_aliases_for(parent, node)
167
+ node.reflection.chain.map { |reflection|
168
+ alias_tracker.aliased_table_for(
169
+ reflection.table_name,
170
+ table_alias_for(reflection, parent, reflection != node.reflection),
171
+ reflection.klass.type_caster
172
+ )
173
+ }
174
+ end
175
+
176
+ def table_alias_for(reflection, parent, join)
177
+ name = reflection.alias_candidate(parent.table_name)
178
+ join ? "#{name}_join" : name
179
+ end
180
+
181
+ def walk(left, right, join_type)
182
+ intersection, missing = right.children.map { |node1|
183
+ [left.children.find { |node2| node1.match? node2 }, node1]
184
+ }.partition(&:first)
185
+
186
+ joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
187
+ joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
188
+ end
189
+
190
+ def find_reflection(klass, name)
191
+ klass._reflect_on_association(name) ||
192
+ raise(ConfigurationError, "Can't join '#{klass.name}' to association named '#{name}'; perhaps you misspelled it?")
193
+ end
194
+
195
+ def build(associations, base_klass)
196
+ associations.map do |name, right|
197
+ reflection = find_reflection base_klass, name
198
+ reflection.check_validity!
199
+ reflection.check_eager_loadable!
200
+
201
+ if reflection.polymorphic?
202
+ raise EagerLoadPolymorphicError.new(reflection)
203
+ end
204
+
205
+ JoinAssociation.new(reflection, build(right, reflection.klass))
206
+ end
207
+ end
208
+
209
+ def construct(ar_parent, parent, row, seen, model_cache)
210
+ return if ar_parent.nil?
211
+
212
+ parent.children.each do |node|
213
+ if node.reflection.collection?
214
+ other = ar_parent.association(node.reflection.name)
215
+ other.loaded!
216
+ elsif ar_parent.association_cached?(node.reflection.name)
217
+ model = ar_parent.association(node.reflection.name).target
218
+ construct(model, node, row, seen, model_cache)
219
+ next
220
+ end
221
+
222
+ key = aliases.column_alias(node, node.primary_key)
223
+ id = row[key]
224
+ if id.nil?
225
+ nil_association = ar_parent.association(node.reflection.name)
226
+ nil_association.loaded!
227
+ next
228
+ end
229
+
230
+ model = seen[ar_parent.object_id][node][id]
231
+
232
+ if model
233
+ construct(model, node, row, seen, model_cache)
234
+ else
235
+ model = construct_model(ar_parent, node, row, model_cache, id)
236
+
237
+ seen[ar_parent.object_id][node][id] = model
238
+ construct(model, node, row, seen, model_cache)
239
+ end
240
+ end
241
+ end
242
+
243
+ def construct_model(record, node, row, model_cache, id)
244
+ other = record.association(node.reflection.name)
245
+
246
+ model = model_cache[node][id] ||=
247
+ node.instantiate(row, aliases.column_aliases(node)) do |m|
248
+ other.set_inverse_instance(m)
249
+ end
250
+
251
+ if node.reflection.collection?
252
+ other.target.push(model)
253
+ else
254
+ other.target = model
255
+ end
256
+
257
+ model.readonly! if node.readonly?
258
+ model
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/associations/join_dependency/join_part"
4
+ require "active_support/core_ext/array/extract"
5
+
6
+ module ActiveRecord
7
+ module Associations
8
+ class JoinDependency # :nodoc:
9
+ class JoinAssociation < JoinPart # :nodoc:
10
+ attr_reader :reflection, :tables
11
+ attr_accessor :table
12
+
13
+ def initialize(reflection, children)
14
+ super(reflection.klass, children)
15
+
16
+ @reflection = reflection
17
+ @tables = nil
18
+ end
19
+
20
+ def match?(other)
21
+ return true if self == other
22
+ super && reflection == other.reflection
23
+ end
24
+
25
+ def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
26
+ joins = []
27
+
28
+ # The chain starts with the target table, but we want to end with it here (makes
29
+ # more sense in this context), so we reverse
30
+ reflection.chain.reverse_each.with_index(1) do |reflection, i|
31
+ table = tables[-i]
32
+ klass = reflection.klass
33
+
34
+ join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
35
+
36
+ arel = join_scope.arel(alias_tracker.aliases)
37
+ nodes = arel.constraints.first
38
+
39
+ others = nodes.children.extract! do |node|
40
+ !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
41
+ end
42
+
43
+ joins << table.create_join(table, table.create_on(nodes), join_type)
44
+
45
+ unless others.empty?
46
+ joins.concat arel.join_sources
47
+ append_constraints(joins.last, others)
48
+ end
49
+
50
+ # The current table in this iteration becomes the foreign table in the next
51
+ foreign_table, foreign_klass = table, klass
52
+ end
53
+
54
+ joins
55
+ end
56
+
57
+ def tables=(tables)
58
+ @tables = tables
59
+ @table = tables.first
60
+ end
61
+
62
+ def readonly?
63
+ return @readonly if defined?(@readonly)
64
+
65
+ @readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
66
+ end
67
+
68
+ private
69
+ def append_constraints(join, constraints)
70
+ if join.is_a?(Arel::Nodes::StringJoin)
71
+ join_string = table.create_and(constraints.unshift(join.left))
72
+ join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
73
+ else
74
+ join.right.expr.children.concat(constraints)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/associations/join_dependency/join_part"
4
+
5
+ module ActiveRecord
6
+ module Associations
7
+ class JoinDependency # :nodoc:
8
+ class JoinBase < JoinPart # :nodoc:
9
+ attr_reader :table
10
+
11
+ def initialize(base_klass, table, children)
12
+ super(base_klass, children)
13
+ @table = table
14
+ end
15
+
16
+ def match?(other)
17
+ return true if self == other
18
+ super && base_klass == other.base_klass
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class JoinDependency # :nodoc:
6
+ # A JoinPart represents a part of a JoinDependency. It is inherited
7
+ # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which
8
+ # everything else is being joined onto. A JoinAssociation represents an association which
9
+ # is joining to the base. A JoinAssociation may result in more than one actual join
10
+ # operations (for example a has_and_belongs_to_many JoinAssociation would result in
11
+ # two; one for the join table and one for the target table).
12
+ class JoinPart # :nodoc:
13
+ include Enumerable
14
+
15
+ # The Active Record class which this join part is associated 'about'; for a JoinBase
16
+ # this is the actual base model, for a JoinAssociation this is the target model of the
17
+ # association.
18
+ attr_reader :base_klass, :children
19
+
20
+ delegate :table_name, :column_names, :primary_key, to: :base_klass
21
+
22
+ def initialize(base_klass, children)
23
+ @base_klass = base_klass
24
+ @children = children
25
+ end
26
+
27
+ def match?(other)
28
+ self.class == other.class
29
+ end
30
+
31
+ def each(&block)
32
+ yield self
33
+ children.each { |child| child.each(&block) }
34
+ end
35
+
36
+ def each_children(&block)
37
+ children.each do |child|
38
+ yield self, child
39
+ child.each_children(&block)
40
+ end
41
+ end
42
+
43
+ # An Arel::Table for the active_record
44
+ def table
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def extract_record(row, column_names_with_alias)
49
+ # This code is performance critical as it is called per row.
50
+ # see: https://github.com/rails/rails/pull/12185
51
+ hash = {}
52
+
53
+ index = 0
54
+ length = column_names_with_alias.length
55
+
56
+ while index < length
57
+ column = column_names_with_alias[index]
58
+ hash[column.name] = row[column.alias]
59
+ index += 1
60
+ end
61
+
62
+ hash
63
+ end
64
+
65
+ def instantiate(row, aliases, &block)
66
+ base_klass.instantiate(extract_record(row, aliases), &block)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end