activerecord 4.2.11.1 → 6.0.3.5

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 (373) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +721 -1522
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +266 -251
  9. data/lib/active_record/association_relation.rb +20 -13
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +128 -57
  12. data/lib/active_record/associations/association_scope.rb +103 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -66
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +136 -288
  23. data/lib/active_record/associations/collection_proxy.rb +241 -147
  24. data/lib/active_record/associations/foreign_association.rb +10 -1
  25. data/lib/active_record/associations/has_many_association.rb +34 -98
  26. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  27. data/lib/active_record/associations/has_one_association.rb +61 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +149 -166
  33. data/lib/active_record/associations/preloader/association.rb +90 -123
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +90 -93
  36. data/lib/active_record/associations/singular_association.rb +18 -39
  37. data/lib/active_record/associations/through_association.rb +38 -18
  38. data/lib/active_record/associations.rb +1737 -1597
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +57 -37
  48. data/lib/active_record/attribute_methods/write.rb +32 -55
  49. data/lib/active_record/attribute_methods.rb +120 -135
  50. data/lib/active_record/attributes.rb +213 -82
  51. data/lib/active_record/autosave_association.rb +97 -41
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +23 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +804 -297
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +240 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +371 -242
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +694 -256
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +473 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +507 -639
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  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 +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -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 +58 -181
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +51 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -296
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -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 +102 -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 +299 -349
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +252 -230
  130. data/lib/active_record/counter_cache.rb +70 -49
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  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 +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +227 -501
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +86 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +623 -305
  165. data/lib/active_record/model_schema.rb +313 -112
  166. data/lib/active_record/nested_attributes.rb +263 -223
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +557 -126
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +430 -281
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +268 -254
  182. data/lib/active_record/relation/delegation.rb +75 -84
  183. data/lib/active_record/relation/finder_methods.rb +285 -241
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +78 -88
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +110 -119
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +603 -397
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +11 -14
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +530 -341
  201. data/lib/active_record/result.rb +79 -43
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping/default.rb +98 -83
  208. data/lib/active_record/scoping/named.rb +86 -33
  209. data/lib/active_record/scoping.rb +45 -27
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -100
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +80 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +223 -157
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -45
  228. data/lib/active_record/type/date_time.rb +4 -49
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +23 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +11 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +42 -55
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +42 -22
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -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/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -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/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -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/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  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 → module.rb.tt} +0 -0
  340. data/lib/rails/generators/active_record.rb +7 -5
  341. metadata +168 -59
  342. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  343. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  344. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  345. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  346. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  347. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  348. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  349. data/lib/active_record/attribute.rb +0 -163
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -31
  363. data/lib/active_record/type/decimal.rb +0 -64
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -59
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -40
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -110
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,165 +1,132 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class Preloader
4
6
  class Association #:nodoc:
5
- attr_reader :owners, :reflection, :preload_scope, :model, :klass
6
- attr_reader :preloaded_records
7
-
8
7
  def initialize(klass, owners, reflection, preload_scope)
9
8
  @klass = klass
10
9
  @owners = owners
11
10
  @reflection = reflection
12
11
  @preload_scope = preload_scope
13
12
  @model = owners.first && owners.first.class
14
- @scope = nil
15
- @owners_by_key = nil
16
- @preloaded_records = []
17
- end
18
-
19
- def run(preloader)
20
- preload(preloader)
21
13
  end
22
14
 
23
- def preload(preloader)
24
- raise NotImplementedError
25
- end
26
-
27
- def scope
28
- @scope ||= build_scope
29
- end
30
-
31
- def records_for(ids)
32
- query_scope(ids)
33
- end
34
-
35
- def query_scope(ids)
36
- scope.where(association_key.in(ids))
37
- end
38
-
39
- def table
40
- klass.arel_table
41
- end
42
-
43
- # The name of the key on the associated records
44
- def association_key_name
45
- raise NotImplementedError
46
- end
47
-
48
- # This is overridden by HABTM as the condition should be on the foreign_key column in
49
- # the join table
50
- def association_key
51
- table[association_key_name]
52
- end
53
-
54
- # The name of the key on the model which declares the association
55
- def owner_key_name
56
- raise NotImplementedError
15
+ def run
16
+ if !preload_scope || preload_scope.empty_scope?
17
+ owners.each do |owner|
18
+ associate_records_to_owner(owner, records_by_owner[owner] || [])
19
+ end
20
+ else
21
+ # Custom preload scope is used and
22
+ # the association can not be marked as loaded
23
+ # Loading into a Hash instead
24
+ records_by_owner
25
+ end
26
+ self
57
27
  end
58
28
 
59
- def owners_by_key
60
- @owners_by_key ||= if key_conversion_required?
61
- owners.group_by do |owner|
62
- owner[owner_key_name].to_s
63
- end
64
- else
65
- owners.group_by do |owner|
66
- owner[owner_key_name]
67
- end
68
- end
29
+ def records_by_owner
30
+ # owners can be duplicated when a relation has a collection association join
31
+ # #compare_by_identity makes such owners different hash keys
32
+ @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
33
+ owners_by_key[convert_key(record[association_key_name])].each do |owner|
34
+ (result[owner] ||= []) << record
35
+ end
36
+ end
69
37
  end
70
38
 
71
- def options
72
- reflection.options
39
+ def preloaded_records
40
+ return @preloaded_records if defined?(@preloaded_records)
41
+ @preloaded_records = owner_keys.empty? ? [] : records_for(owner_keys)
73
42
  end
74
43
 
75
44
  private
45
+ attr_reader :owners, :reflection, :preload_scope, :model, :klass
76
46
 
77
- def associated_records_by_owner(preloader)
78
- owners_map = owners_by_key
79
- owner_keys = owners_map.keys.compact
80
-
81
- # Each record may have multiple owners, and vice-versa
82
- records_by_owner = owners.each_with_object({}) do |owner,h|
83
- h[owner] = []
47
+ # The name of the key on the associated records
48
+ def association_key_name
49
+ reflection.join_primary_key(klass)
84
50
  end
85
51
 
86
- if owner_keys.any?
87
- # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
88
- # Make several smaller queries if necessary or make one query if the adapter supports it
89
- sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
52
+ # The name of the key on the model which declares the association
53
+ def owner_key_name
54
+ reflection.join_foreign_key
55
+ end
90
56
 
91
- records = load_slices sliced
92
- records.each do |record, owner_key|
93
- owners_map[owner_key].each do |owner|
94
- records_by_owner[owner] << record
95
- end
57
+ def associate_records_to_owner(owner, records)
58
+ association = owner.association(reflection.name)
59
+ if reflection.collection?
60
+ association.target = records
61
+ else
62
+ association.target = records.first
96
63
  end
97
64
  end
98
65
 
99
- records_by_owner
100
- end
101
-
102
- def key_conversion_required?
103
- association_key_type != owner_key_type
104
- end
105
-
106
- def association_key_type
107
- @klass.type_for_attribute(association_key_name.to_s).type
108
- end
109
-
110
- def owner_key_type
111
- @model.type_for_attribute(owner_key_name.to_s).type
112
- end
113
-
114
- def load_slices(slices)
115
- @preloaded_records = slices.flat_map { |slice|
116
- records_for(slice)
117
- }
118
-
119
- @preloaded_records.map { |record|
120
- key = record[association_key_name]
121
- key = key.to_s if key_conversion_required?
66
+ def owner_keys
67
+ @owner_keys ||= owners_by_key.keys
68
+ end
122
69
 
123
- [record, key]
124
- }
125
- end
70
+ def owners_by_key
71
+ @owners_by_key ||= owners.each_with_object({}) do |owner, result|
72
+ key = convert_key(owner[owner_key_name])
73
+ (result[key] ||= []) << owner if key
74
+ end
75
+ end
126
76
 
127
- def reflection_scope
128
- @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
129
- end
77
+ def key_conversion_required?
78
+ unless defined?(@key_conversion_required)
79
+ @key_conversion_required = (association_key_type != owner_key_type)
80
+ end
130
81
 
131
- def build_scope
132
- scope = klass.unscoped
82
+ @key_conversion_required
83
+ end
133
84
 
134
- values = reflection_scope.values
135
- reflection_binds = reflection_scope.bind_values
136
- preload_values = preload_scope.values
137
- preload_binds = preload_scope.bind_values
85
+ def convert_key(key)
86
+ if key_conversion_required?
87
+ key.to_s
88
+ else
89
+ key
90
+ end
91
+ end
138
92
 
139
- scope.where_values = Array(values[:where]) + Array(preload_values[:where])
140
- scope.references_values = Array(values[:references]) + Array(preload_values[:references])
141
- scope.bind_values = (reflection_binds + preload_binds)
93
+ def association_key_type
94
+ @klass.type_for_attribute(association_key_name).type
95
+ end
142
96
 
143
- scope._select! preload_values[:select] || values[:select] || table[Arel.star]
144
- scope.includes! preload_values[:includes] || values[:includes]
145
- scope.joins! preload_values[:joins] || values[:joins]
146
- scope.order! preload_values[:order] || values[:order]
97
+ def owner_key_type
98
+ @model.type_for_attribute(owner_key_name).type
99
+ end
147
100
 
148
- if preload_values[:reordering] || values[:reordering]
149
- scope.reordering_value = true
101
+ def records_for(ids)
102
+ scope.where(association_key_name => ids).load do |record|
103
+ # Processing only the first owner
104
+ # because the record is modified but not an owner
105
+ owner = owners_by_key[convert_key(record[association_key_name])].first
106
+ association = owner.association(reflection.name)
107
+ association.set_inverse_instance(record)
108
+ end
150
109
  end
151
110
 
152
- if preload_values[:readonly] || values[:readonly]
153
- scope.readonly!
111
+ def scope
112
+ @scope ||= build_scope
154
113
  end
155
114
 
156
- if options[:as]
157
- scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
115
+ def reflection_scope
116
+ @reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
158
117
  end
159
118
 
160
- scope.unscope_values = Array(values[:unscope]) + Array(preload_values[:unscope])
161
- klass.default_scoped.merge(scope)
162
- end
119
+ def build_scope
120
+ scope = klass.scope_for_association
121
+
122
+ if reflection.type && !reflection.through_reflection?
123
+ scope.where!(reflection.type => model.polymorphic_name)
124
+ end
125
+
126
+ scope.merge!(reflection_scope) if reflection.scope
127
+ scope.merge!(preload_scope) if preload_scope
128
+ scope
129
+ end
163
130
  end
164
131
  end
165
132
  end
@@ -1,95 +1,115 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class Preloader
4
- module ThroughAssociation #:nodoc:
5
- def through_reflection
6
- reflection.through_reflection
7
- end
6
+ class ThroughAssociation < Association # :nodoc:
7
+ PRELOADER = ActiveRecord::Associations::Preloader.new
8
8
 
9
- def source_reflection
10
- reflection.source_reflection
9
+ def initialize(*)
10
+ super
11
+ @already_loaded = owners.first.association(through_reflection.name).loaded?
11
12
  end
12
13
 
13
- def associated_records_by_owner(preloader)
14
- preloader.preload(owners,
15
- through_reflection.name,
16
- through_scope)
17
-
18
- through_records = owners.map do |owner|
19
- association = owner.association through_reflection.name
14
+ def preloaded_records
15
+ @preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
16
+ end
20
17
 
21
- [owner, Array(association.reader)]
22
- end
18
+ def records_by_owner
19
+ return @records_by_owner if defined?(@records_by_owner)
20
+ source_records_by_owner = source_preloaders.map(&:records_by_owner).reduce(:merge)
21
+ through_records_by_owner = through_preloaders.map(&:records_by_owner).reduce(:merge)
23
22
 
24
- reset_association owners, through_reflection.name
23
+ @records_by_owner = owners.each_with_object({}) do |owner, result|
24
+ through_records = through_records_by_owner[owner] || []
25
25
 
26
- middle_records = through_records.flat_map { |(_,rec)| rec }
26
+ if @already_loaded
27
+ if source_type = reflection.options[:source_type]
28
+ through_records = through_records.select do |record|
29
+ record[reflection.foreign_type] == source_type
30
+ end
31
+ end
32
+ end
27
33
 
28
- preloaders = preloader.preload(middle_records,
29
- source_reflection.name,
30
- reflection_scope)
34
+ records = through_records.flat_map do |record|
35
+ source_records_by_owner[record]
36
+ end
31
37
 
32
- @preloaded_records = preloaders.flat_map(&:preloaded_records)
38
+ records.compact!
39
+ records.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
40
+ records.uniq! if scope.distinct_value
41
+ result[owner] = records
42
+ end
43
+ end
33
44
 
34
- middle_to_pl = preloaders.each_with_object({}) do |pl,h|
35
- pl.owners.each { |middle|
36
- h[middle] = pl
37
- }
45
+ private
46
+ def source_preloaders
47
+ @source_preloaders ||= PRELOADER.preload(middle_records, source_reflection.name, scope)
38
48
  end
39
49
 
40
- record_offset = {}
41
- @preloaded_records.each_with_index do |record,i|
42
- record_offset[record] = i
50
+ def middle_records
51
+ through_preloaders.flat_map(&:preloaded_records)
43
52
  end
44
53
 
45
- through_records.each_with_object({}) { |(lhs,center),records_by_owner|
46
- pl_to_middle = center.group_by { |record| middle_to_pl[record] }
54
+ def through_preloaders
55
+ @through_preloaders ||= PRELOADER.preload(owners, through_reflection.name, through_scope)
56
+ end
47
57
 
48
- records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
49
- rhs_records = middles.flat_map { |r|
50
- association = r.association source_reflection.name
58
+ def through_reflection
59
+ reflection.through_reflection
60
+ end
51
61
 
52
- association.reader
53
- }.compact
62
+ def source_reflection
63
+ reflection.source_reflection
64
+ end
54
65
 
55
- rhs_records.sort_by { |rhs| record_offset[rhs] }
66
+ def preload_index
67
+ @preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
68
+ result[record] = index
56
69
  end
57
- }
58
- end
59
-
60
- private
61
-
62
- def reset_association(owners, association_name)
63
- should_reset = (through_scope != through_reflection.klass.unscoped) ||
64
- (reflection.options[:source_type] && through_reflection.collection?)
65
-
66
- # Don't cache the association - we would only be caching a subset
67
- if should_reset
68
- owners.each { |owner|
69
- owner.association(association_name).reset
70
- }
71
70
  end
72
- end
73
71
 
72
+ def through_scope
73
+ scope = through_reflection.klass.unscoped
74
+ options = reflection.options
74
75
 
75
- def through_scope
76
- scope = through_reflection.klass.unscoped
76
+ values = reflection_scope.values
77
+ if annotations = values[:annotate]
78
+ scope.annotate!(*annotations)
79
+ end
77
80
 
78
- if options[:source_type]
79
- scope.where! reflection.foreign_type => options[:source_type]
80
- else
81
- unless reflection_scope.where_values.empty?
82
- scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
83
- scope.where_values = reflection_scope.values[:where]
84
- scope.bind_values = reflection_scope.bind_values
81
+ if options[:source_type]
82
+ scope.where! reflection.foreign_type => options[:source_type]
83
+ elsif !reflection_scope.where_clause.empty?
84
+ scope.where_clause = reflection_scope.where_clause
85
+
86
+ if includes = values[:includes]
87
+ scope.includes!(source_reflection.name => includes)
88
+ else
89
+ scope.includes!(source_reflection.name)
90
+ end
91
+
92
+ if values[:references] && !values[:references].empty?
93
+ scope.references!(values[:references])
94
+ else
95
+ scope.references!(source_reflection.table_name)
96
+ end
97
+
98
+ if joins = values[:joins]
99
+ scope.joins!(source_reflection.name => joins)
100
+ end
101
+
102
+ if left_outer_joins = values[:left_outer_joins]
103
+ scope.left_outer_joins!(source_reflection.name => left_outer_joins)
104
+ end
105
+
106
+ if scope.eager_loading? && order_values = values[:order]
107
+ scope = scope.order(order_values)
108
+ end
85
109
  end
86
110
 
87
- scope.references! reflection_scope.values[:references]
88
- scope = scope.order reflection_scope.values[:order] if scope.eager_loading?
111
+ scope
89
112
  end
90
-
91
- scope
92
- end
93
113
  end
94
114
  end
95
115
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  # Implements the details of eager loading of Active Record associations.
@@ -42,16 +44,8 @@ module ActiveRecord
42
44
  extend ActiveSupport::Autoload
43
45
 
44
46
  eager_autoload do
45
- autoload :Association, 'active_record/associations/preloader/association'
46
- autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
47
- autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
48
- autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
49
-
50
- autoload :HasMany, 'active_record/associations/preloader/has_many'
51
- autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
52
- autoload :HasOne, 'active_record/associations/preloader/has_one'
53
- autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
54
- autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
47
+ autoload :Association, "active_record/associations/preloader/association"
48
+ autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
55
49
  end
56
50
 
57
51
  # Eager loads the named associations for the given Active Record record(s).
@@ -88,116 +82,119 @@ module ActiveRecord
88
82
  # [ :books, :author ]
89
83
  # { author: :avatar }
90
84
  # [ :books, { author: :avatar } ]
91
-
92
- NULL_RELATION = Struct.new(:values, :bind_values).new({}, [])
93
-
94
85
  def preload(records, associations, preload_scope = nil)
95
- records = Array.wrap(records).compact.uniq
96
- associations = Array.wrap(associations)
97
- preload_scope = preload_scope || NULL_RELATION
86
+ records = Array.wrap(records).compact
98
87
 
99
88
  if records.empty?
100
89
  []
101
90
  else
102
- associations.flat_map { |association|
91
+ Array.wrap(associations).flat_map { |association|
103
92
  preloaders_on association, records, preload_scope
104
93
  }
105
94
  end
106
95
  end
107
96
 
108
97
  private
109
-
110
- def preloaders_on(association, records, scope)
111
- case association
112
- when Hash
113
- preloaders_for_hash(association, records, scope)
114
- when Symbol
115
- preloaders_for_one(association, records, scope)
116
- when String
117
- preloaders_for_one(association.to_sym, records, scope)
118
- else
119
- raise ArgumentError, "#{association.inspect} was not recognised for preload"
98
+ # Loads all the given data into +records+ for the +association+.
99
+ def preloaders_on(association, records, scope, polymorphic_parent = false)
100
+ case association
101
+ when Hash
102
+ preloaders_for_hash(association, records, scope, polymorphic_parent)
103
+ when Symbol, String
104
+ preloaders_for_one(association, records, scope, polymorphic_parent)
105
+ else
106
+ raise ArgumentError, "#{association.inspect} was not recognized for preload"
107
+ end
120
108
  end
121
- end
122
-
123
- def preloaders_for_hash(association, records, scope)
124
- association.flat_map { |parent, child|
125
- loaders = preloaders_for_one parent, records, scope
126
109
 
127
- recs = loaders.flat_map(&:preloaded_records).uniq
128
- loaders.concat Array.wrap(child).flat_map { |assoc|
129
- preloaders_on assoc, recs, scope
110
+ def preloaders_for_hash(association, records, scope, polymorphic_parent)
111
+ association.flat_map { |parent, child|
112
+ grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
113
+ loaders = preloaders_for_reflection(reflection, reflection_records, scope)
114
+ recs = loaders.flat_map(&:preloaded_records).uniq
115
+ child_polymorphic_parent = reflection && reflection.options[:polymorphic]
116
+ loaders.concat Array.wrap(child).flat_map { |assoc|
117
+ preloaders_on assoc, recs, scope, child_polymorphic_parent
118
+ }
119
+ loaders
120
+ end
130
121
  }
131
- loaders
132
- }
133
- end
134
-
135
- # Not all records have the same class, so group then preload group on the reflection
136
- # itself so that if various subclass share the same association then we do not split
137
- # them unnecessarily
138
- #
139
- # Additionally, polymorphic belongs_to associations can have multiple associated
140
- # classes, depending on the polymorphic_type field. So we group by the classes as
141
- # well.
142
- def preloaders_for_one(association, records, scope)
143
- grouped_records(association, records).flat_map do |reflection, klasses|
144
- klasses.map do |rhs_klass, rs|
145
- loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
146
- loader.run self
147
- loader
148
- end
149
122
  end
150
- end
151
123
 
152
- def grouped_records(association, records)
153
- h = {}
154
- records.each do |record|
155
- next unless record
156
- assoc = record.association(association)
157
- klasses = h[assoc.reflection] ||= {}
158
- (klasses[assoc.klass] ||= []) << record
124
+ # Loads all the given data into +records+ for a singular +association+.
125
+ #
126
+ # Functions by instantiating a preloader class such as Preloader::Association and
127
+ # call the +run+ method for each passed in class in the +records+ argument.
128
+ #
129
+ # Not all records have the same class, so group then preload group on the reflection
130
+ # itself so that if various subclass share the same association then we do not split
131
+ # them unnecessarily
132
+ #
133
+ # Additionally, polymorphic belongs_to associations can have multiple associated
134
+ # classes, depending on the polymorphic_type field. So we group by the classes as
135
+ # well.
136
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
137
+ grouped_records(association, records, polymorphic_parent)
138
+ .flat_map do |reflection, reflection_records|
139
+ preloaders_for_reflection reflection, reflection_records, scope
140
+ end
159
141
  end
160
- h
161
- end
162
142
 
163
- class AlreadyLoaded # :nodoc:
164
- attr_reader :owners, :reflection
143
+ def preloaders_for_reflection(reflection, records, scope)
144
+ records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
145
+ preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope).run
146
+ end
147
+ end
165
148
 
166
- def initialize(klass, owners, reflection, preload_scope)
167
- @owners = owners
168
- @reflection = reflection
149
+ def grouped_records(association, records, polymorphic_parent)
150
+ h = {}
151
+ records.each do |record|
152
+ reflection = record.class._reflect_on_association(association)
153
+ next if polymorphic_parent && !reflection || !record.association(association).klass
154
+ (h[reflection] ||= []) << record
155
+ end
156
+ h
169
157
  end
170
158
 
171
- def run(preloader); end
159
+ class AlreadyLoaded # :nodoc:
160
+ def initialize(klass, owners, reflection, preload_scope)
161
+ @owners = owners
162
+ @reflection = reflection
163
+ end
172
164
 
173
- def preloaded_records
174
- owners.flat_map { |owner| owner.association(reflection.name).target }
175
- end
176
- end
165
+ def run
166
+ self
167
+ end
177
168
 
178
- class NullPreloader # :nodoc:
179
- def self.new(klass, owners, reflection, preload_scope); self; end
180
- def self.run(preloader); end
181
- def self.preloaded_records; []; end
182
- end
169
+ def preloaded_records
170
+ @preloaded_records ||= records_by_owner.flat_map(&:last)
171
+ end
183
172
 
184
- def preloader_for(reflection, owners, rhs_klass)
185
- return NullPreloader unless rhs_klass
173
+ def records_by_owner
174
+ @records_by_owner ||= owners.each_with_object({}) do |owner, result|
175
+ result[owner] = Array(owner.association(reflection.name).target)
176
+ end
177
+ end
186
178
 
187
- if owners.first.association(reflection.name).loaded?
188
- return AlreadyLoaded
179
+ private
180
+ attr_reader :owners, :reflection
189
181
  end
190
- reflection.check_preloadable!
191
-
192
- case reflection.macro
193
- when :has_many
194
- reflection.options[:through] ? HasManyThrough : HasMany
195
- when :has_one
196
- reflection.options[:through] ? HasOneThrough : HasOne
197
- when :belongs_to
198
- BelongsTo
182
+
183
+ # Returns a class containing the logic needed to load preload the data
184
+ # and attach it to a relation. The class returned implements a `run` method
185
+ # that accepts a preloader.
186
+ def preloader_for(reflection, owners)
187
+ if owners.all? { |o| o.association(reflection.name).loaded? }
188
+ return AlreadyLoaded
189
+ end
190
+ reflection.check_preloadable!
191
+
192
+ if reflection.options[:through]
193
+ ThroughAssociation
194
+ else
195
+ Association
196
+ end
199
197
  end
200
- end
201
198
  end
202
199
  end
203
200
  end