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,4 +1,4 @@
1
- require 'active_support/core_ext/array/wrap'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
@@ -6,7 +6,15 @@ module ActiveRecord
6
6
  #
7
7
  # CollectionAssociation is an abstract class that provides common stuff to
8
8
  # ease the implementation of association proxies that represent
9
- # collections. See the class hierarchy in AssociationProxy.
9
+ # collections. See the class hierarchy in Association.
10
+ #
11
+ # CollectionAssociation:
12
+ # HasManyAssociation => has_many
13
+ # HasManyThroughAssociation + ThroughAssociation => has_many :through
14
+ #
15
+ # The CollectionAssociation class provides common methods to the collections
16
+ # defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
17
+ # the +:through association+ option.
10
18
  #
11
19
  # You need to be careful with assumptions regarding the target: The proxy
12
20
  # does not fetch records from the database until it needs them, but new
@@ -18,22 +26,14 @@ module ActiveRecord
18
26
  # If you need to work on all current children, new and existing records,
19
27
  # +load_target+ and the +loaded+ flag are your friends.
20
28
  class CollectionAssociation < Association #:nodoc:
21
- attr_reader :proxy
22
-
23
- def initialize(owner, reflection)
24
- super
25
- @proxy = CollectionProxy.new(self)
26
- end
27
-
28
29
  # Implements the reader method, e.g. foo.items for Foo.has_many :items
29
- def reader(force_reload = false)
30
- if force_reload
31
- klass.uncached { reload }
32
- elsif stale_target?
30
+ def reader
31
+ if stale_target?
33
32
  reload
34
33
  end
35
34
 
36
- proxy
35
+ @proxy ||= CollectionProxy.create(klass, self)
36
+ @proxy.reset_scope
37
37
  end
38
38
 
39
39
  # Implements the writer method, e.g. foo.items= for Foo.has_many :items
@@ -43,92 +43,78 @@ module ActiveRecord
43
43
 
44
44
  # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
45
45
  def ids_reader
46
- if loaded? || options[:finder_sql]
47
- load_target.map do |record|
48
- record.send(reflection.association_primary_key)
49
- end
46
+ if loaded?
47
+ target.pluck(reflection.association_primary_key)
48
+ elsif !target.empty?
49
+ load_target.pluck(reflection.association_primary_key)
50
50
  else
51
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
52
- relation = scoped
53
-
54
- including = (relation.eager_load_values + relation.includes_values).uniq
55
-
56
- if including.any?
57
- join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, [])
58
- relation = join_dependency.join_associations.inject(relation) do |r, association|
59
- association.join_relation(r)
60
- end
61
- end
62
-
63
- relation.pluck(column)
51
+ @association_ids ||= scope.pluck(reflection.association_primary_key)
64
52
  end
65
53
  end
66
54
 
67
55
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
68
56
  def ids_writer(ids)
69
- pk_column = reflection.primary_key_column
70
- ids = Array.wrap(ids).reject { |id| id.blank? }
71
- ids.map! { |i| pk_column.type_cast(i) }
72
- replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
57
+ primary_key = reflection.association_primary_key
58
+ pk_type = klass.type_for_attribute(primary_key)
59
+ ids = Array(ids).reject(&:blank?)
60
+ ids.map! { |i| pk_type.cast(i) }
61
+
62
+ records = klass.where(primary_key => ids).index_by do |r|
63
+ r.public_send(primary_key)
64
+ end.values_at(*ids).compact
65
+
66
+ if records.size != ids.size
67
+ found_ids = records.map { |record| record.public_send(primary_key) }
68
+ not_found_ids = ids - found_ids
69
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
70
+ else
71
+ replace(records)
72
+ end
73
73
  end
74
74
 
75
75
  def reset
76
- @loaded = false
76
+ super
77
77
  @target = []
78
- end
79
-
80
- def select(select = nil)
81
- if block_given?
82
- load_target.select.each { |e| yield e }
83
- else
84
- scoped.select(select)
85
- end
78
+ @association_ids = nil
86
79
  end
87
80
 
88
81
  def find(*args)
89
- if block_given?
90
- load_target.find(*args) { |*block_args| yield(*block_args) }
91
- else
92
- if options[:finder_sql]
93
- find_by_scan(*args)
94
- else
95
- scoped.find(*args)
82
+ if options[:inverse_of] && loaded?
83
+ args_flatten = args.flatten
84
+ model = scope.klass
85
+
86
+ if args_flatten.blank?
87
+ error_message = "Couldn't find #{model.name} without an ID"
88
+ raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
96
89
  end
97
- end
98
- end
99
90
 
100
- def first(*args)
101
- first_or_last(:first, *args)
102
- end
91
+ result = find_by_scan(*args)
103
92
 
104
- def last(*args)
105
- first_or_last(:last, *args)
93
+ result_size = Array(result).size
94
+ if !result || result_size != args_flatten.size
95
+ scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
96
+ else
97
+ result
98
+ end
99
+ else
100
+ scope.find(*args)
101
+ end
106
102
  end
107
103
 
108
- def build(attributes = {}, options = {}, &block)
104
+ def build(attributes = {}, &block)
109
105
  if attributes.is_a?(Array)
110
- attributes.collect { |attr| build(attr, options, &block) }
106
+ attributes.collect { |attr| build(attr, &block) }
111
107
  else
112
- add_to_target(build_record(attributes, options)) do |record|
113
- yield(record) if block_given?
114
- end
108
+ add_to_target(build_record(attributes, &block))
115
109
  end
116
110
  end
117
111
 
118
- def create(attributes = {}, options = {}, &block)
119
- create_record(attributes, options, &block)
120
- end
121
-
122
- def create!(attributes = {}, options = {}, &block)
123
- create_record(attributes, options, true, &block)
124
- end
125
-
126
- # Add +records+ to this association. Returns +self+ so method calls may be chained.
127
- # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
112
+ # Add +records+ to this association. Since +<<+ flattens its argument list
113
+ # and inserts each record, +push+ and +concat+ behave identically.
128
114
  def concat(*records)
129
- load_target if owner.new_record?
130
-
115
+ records = records.flatten
131
116
  if owner.new_record?
117
+ load_target
132
118
  concat_records(records)
133
119
  else
134
120
  transaction { concat_records(records) }
@@ -150,23 +136,38 @@ module ActiveRecord
150
136
  end
151
137
  end
152
138
 
153
- # Remove all records from this association
139
+ # Removes all records from the association without calling callbacks
140
+ # on the associated records. It honors the +:dependent+ option. However
141
+ # if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
142
+ # deletion strategy for the association is applied.
143
+ #
144
+ # You can force a particular deletion strategy by passing a parameter.
145
+ #
146
+ # Example:
147
+ #
148
+ # @author.books.delete_all(:nullify)
149
+ # @author.books.delete_all(:delete_all)
154
150
  #
155
151
  # See delete for more info.
156
- def delete_all
157
- delete(load_target).tap do
152
+ def delete_all(dependent = nil)
153
+ if dependent && ![:nullify, :delete_all].include?(dependent)
154
+ raise ArgumentError, "Valid values are :nullify or :delete_all"
155
+ end
156
+
157
+ dependent = if dependent
158
+ dependent
159
+ elsif options[:dependent] == :destroy
160
+ :delete_all
161
+ else
162
+ options[:dependent]
163
+ end
164
+
165
+ delete_or_nullify_all_records(dependent).tap do
158
166
  reset
159
167
  loaded!
160
168
  end
161
169
  end
162
170
 
163
- # Called when the association is declared as :dependent => :delete_all. This is
164
- # an optimised version which avoids loading the records into memory. Not really
165
- # for public consumption.
166
- def delete_all_on_destroy
167
- scoped.delete_all
168
- end
169
-
170
171
  # Destroy all the records from this association.
171
172
  #
172
173
  # See destroy for more info.
@@ -177,47 +178,6 @@ module ActiveRecord
177
178
  end
178
179
  end
179
180
 
180
- # Calculate sum using SQL, not Enumerable
181
- def sum(*args)
182
- if block_given?
183
- scoped.sum(*args) { |*block_args| yield(*block_args) }
184
- else
185
- scoped.sum(*args)
186
- end
187
- end
188
-
189
- # Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
190
- # association, it will be used for the query. Otherwise, construct options and pass them with
191
- # scope to the target class's +count+.
192
- def count(column_name = nil, count_options = {})
193
- column_name, count_options = nil, column_name if column_name.is_a?(Hash)
194
-
195
- if options[:counter_sql] || options[:finder_sql]
196
- unless count_options.blank?
197
- raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
198
- end
199
-
200
- reflection.klass.count_by_sql(custom_counter_sql)
201
- else
202
- if options[:uniq]
203
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
204
- column_name ||= reflection.klass.primary_key
205
- count_options.merge!(:distinct => true)
206
- end
207
-
208
- value = scoped.count(column_name, count_options)
209
-
210
- limit = options[:limit]
211
- offset = options[:offset]
212
-
213
- if limit || offset
214
- [ [value - offset.to_i, 0].max, limit.to_i ].min
215
- else
216
- value
217
- end
218
- end
219
- end
220
-
221
181
  # Removes +records+ from this association calling +before_remove+ and
222
182
  # +after_remove+ callbacks.
223
183
  #
@@ -229,13 +189,12 @@ module ActiveRecord
229
189
  delete_or_destroy(records, options[:dependent])
230
190
  end
231
191
 
232
- # Destroy +records+ and remove them from this association calling
233
- # +before_remove+ and +after_remove+ callbacks.
192
+ # Deletes the +records+ and removes them from this association calling
193
+ # +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
234
194
  #
235
- # Note that this method will _always_ remove records from the database
236
- # ignoring the +:dependent+ option.
195
+ # Note that this method removes records from the database ignoring the
196
+ # +:dependent+ option.
237
197
  def destroy(*records)
238
- records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
239
198
  delete_or_destroy(records, :destroy)
240
199
  end
241
200
 
@@ -250,68 +209,51 @@ module ActiveRecord
250
209
  # This method is abstract in the sense that it relies on
251
210
  # +count_records+, which is a method descendants have to provide.
252
211
  def size
253
- if !find_target? || (loaded? && !options[:uniq])
212
+ if !find_target? || loaded?
254
213
  target.size
255
- elsif !loaded? && options[:group]
214
+ elsif @association_ids
215
+ @association_ids.size
216
+ elsif !association_scope.group_values.empty?
256
217
  load_target.size
257
- elsif !loaded? && !options[:uniq] && target.is_a?(Array)
258
- unsaved_records = target.select { |r| r.new_record? }
218
+ elsif !association_scope.distinct_value && !target.empty?
219
+ unsaved_records = target.select(&:new_record?)
259
220
  unsaved_records.size + count_records
260
221
  else
261
222
  count_records
262
223
  end
263
224
  end
264
225
 
265
- # Returns the size of the collection calling +size+ on the target.
226
+ # Returns true if the collection is empty.
266
227
  #
267
- # If the collection has been already loaded +length+ and +size+ are
268
- # equivalent. If not and you are going to need the records anyway this
269
- # method will take one less query. Otherwise +size+ is more efficient.
270
- def length
271
- load_target.size
272
- end
273
-
274
- # Equivalent to <tt>collection.size.zero?</tt>. If the collection has
275
- # not been already loaded and you are going to fetch the records anyway
276
- # it is better to check <tt>collection.length.zero?</tt>.
228
+ # If the collection has been loaded
229
+ # it is equivalent to <tt>collection.size.zero?</tt>. If the
230
+ # collection has not been loaded, it is equivalent to
231
+ # <tt>collection.exists?</tt>. If the collection has not already been
232
+ # loaded and you are going to fetch the records anyway it is better to
233
+ # check <tt>collection.length.zero?</tt>.
277
234
  def empty?
278
- size.zero?
279
- end
280
-
281
- def any?
282
- if block_given?
283
- load_target.any? { |*block_args| yield(*block_args) }
284
- else
285
- !empty?
286
- end
287
- end
288
-
289
- # Returns true if the collection has more than 1 record. Equivalent to collection.size > 1.
290
- def many?
291
- if block_given?
292
- load_target.many? { |*block_args| yield(*block_args) }
235
+ if loaded? || @association_ids || reflection.has_cached_counter?
236
+ size.zero?
293
237
  else
294
- size > 1
295
- end
296
- end
297
-
298
- def uniq(collection = load_target)
299
- seen = {}
300
- collection.find_all do |record|
301
- seen[record.id] = true unless seen.key?(record.id)
238
+ target.empty? && !scope.exists?
302
239
  end
303
240
  end
304
241
 
305
- # Replace this collection with +other_array+
306
- # This will perform a diff and delete/add only records that have changed.
242
+ # Replace this collection with +other_array+. This will perform a diff
243
+ # and delete/add only records that have changed.
307
244
  def replace(other_array)
308
- other_array.each { |val| raise_on_type_mismatch(val) }
245
+ other_array.each { |val| raise_on_type_mismatch!(val) }
309
246
  original_target = load_target.dup
310
247
 
311
248
  if owner.new_record?
312
249
  replace_records(other_array, original_target)
313
250
  else
314
- transaction { replace_records(other_array, original_target) }
251
+ replace_common_records_in_memory(other_array, original_target)
252
+ if other_array != original_target
253
+ transaction { replace_records(other_array, original_target) }
254
+ else
255
+ other_array
256
+ end
315
257
  end
316
258
  end
317
259
 
@@ -320,8 +262,7 @@ module ActiveRecord
320
262
  if record.new_record?
321
263
  include_in_memory?(record)
322
264
  else
323
- load_target if options[:finder_sql]
324
- loaded? ? target.include?(record) : scoped.exists?(record)
265
+ loaded? ? target.include?(record) : scope.exists?(record.id)
325
266
  end
326
267
  else
327
268
  false
@@ -337,54 +278,30 @@ module ActiveRecord
337
278
  target
338
279
  end
339
280
 
340
- def add_to_target(record)
341
- callback(:before_add, record)
342
- yield(record) if block_given?
343
-
344
- if options[:uniq] && index = @target.index(record)
345
- @target[index] = record
346
- else
347
- @target << record
281
+ def add_to_target(record, skip_callbacks = false, &block)
282
+ if association_scope.distinct_value
283
+ index = @target.index(record)
348
284
  end
349
-
350
- callback(:after_add, record)
351
- set_inverse_instance(record)
352
-
353
- record
285
+ replace_on_target(record, index, skip_callbacks, &block)
354
286
  end
355
287
 
356
- private
357
-
358
- def custom_counter_sql
359
- if options[:counter_sql]
360
- interpolate(options[:counter_sql])
361
- else
362
- # replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
363
- interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
364
- count_with = $2.to_s
365
- count_with = '*' if count_with.blank? || count_with =~ /,/
366
- "SELECT #{$1}COUNT(#{count_with}) FROM"
367
- end
368
- end
369
- end
370
-
371
- def custom_finder_sql
372
- interpolate(options[:finder_sql])
373
- end
288
+ def scope
289
+ scope = super
290
+ scope.none! if null_scope?
291
+ scope
292
+ end
374
293
 
375
- def find_target
376
- records =
377
- if options[:finder_sql]
378
- reflection.klass.find_by_sql(custom_finder_sql)
379
- else
380
- scoped.all
381
- end
294
+ def null_scope?
295
+ owner.new_record? && !foreign_key_present?
296
+ end
382
297
 
383
- records = options[:uniq] ? uniq(records) : records
384
- records.each { |record| set_inverse_instance(record) }
385
- records
386
- end
298
+ def find_from_target?
299
+ loaded? ||
300
+ owner.new_record? ||
301
+ target.any? { |record| record.new_record? || record.changed? }
302
+ end
387
303
 
304
+ private
388
305
  # We have some records loaded from the database (persisted) and some that are
389
306
  # in-memory (memory). The same record may be represented in the persisted array
390
307
  # and in the memory array.
@@ -400,14 +317,9 @@ module ActiveRecord
400
317
  return memory if persisted.empty?
401
318
 
402
319
  persisted.map! do |record|
403
- # Unfortunately we cannot simply do memory.delete(record) since on 1.8 this returns
404
- # record rather than memory.at(memory.index(record)). The behavior is fixed in 1.9.
405
- mem_index = memory.index(record)
406
-
407
- if mem_index
408
- mem_record = memory.delete_at(mem_index)
320
+ if mem_record = memory.delete(record)
409
321
 
410
- (record.attribute_names - mem_record.changes.keys).each do |name|
322
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
411
323
  mem_record[name] = record[name]
412
324
  end
413
325
 
@@ -420,36 +332,43 @@ module ActiveRecord
420
332
  persisted + memory
421
333
  end
422
334
 
423
- def create_record(attributes, options, raise = false, &block)
335
+ def _create_record(attributes, raise = false, &block)
424
336
  unless owner.persisted?
425
337
  raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
426
338
  end
427
339
 
428
340
  if attributes.is_a?(Array)
429
- attributes.collect { |attr| create_record(attr, options, raise, &block) }
341
+ attributes.collect { |attr| _create_record(attr, raise, &block) }
430
342
  else
343
+ record = build_record(attributes, &block)
431
344
  transaction do
432
- add_to_target(build_record(attributes, options)) do |record|
433
- yield(record) if block_given?
434
- insert_record(record, true, raise)
345
+ result = nil
346
+ add_to_target(record) do
347
+ result = insert_record(record, true, raise) {
348
+ @_was_loaded = loaded?
349
+ }
435
350
  end
351
+ raise ActiveRecord::Rollback unless result
436
352
  end
353
+ record
437
354
  end
438
355
  end
439
356
 
440
357
  # Do the relevant stuff to insert the given record into the association collection.
441
- def insert_record(record, validate = true, raise = false)
442
- raise NotImplementedError
443
- end
444
-
445
- def create_scope
446
- scoped.scope_for_create.stringify_keys
358
+ def insert_record(record, validate = true, raise = false, &block)
359
+ if raise
360
+ record.save!(validate: validate, &block)
361
+ else
362
+ record.save(validate: validate, &block)
363
+ end
447
364
  end
448
365
 
449
366
  def delete_or_destroy(records, method)
367
+ return if records.empty?
368
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
450
369
  records = records.flatten
451
- records.each { |record| raise_on_type_mismatch(record) }
452
- existing_records = records.reject { |r| r.new_record? }
370
+ records.each { |record| raise_on_type_mismatch!(record) }
371
+ existing_records = records.reject(&:new_record?)
453
372
 
454
373
  if existing_records.empty?
455
374
  remove_records(existing_records, records, method)
@@ -462,21 +381,23 @@ module ActiveRecord
462
381
  records.each { |record| callback(:before_remove, record) }
463
382
 
464
383
  delete_records(existing_records, method) if existing_records.any?
465
- records.each { |record| target.delete(record) }
384
+ @target -= records
385
+ @association_ids = nil
466
386
 
467
387
  records.each { |record| callback(:after_remove, record) }
468
388
  end
469
389
 
470
- # Delete the given records from the association, using one of the methods :destroy,
471
- # :delete_all or :nullify (or nil, in which case a default is used).
390
+ # Delete the given records from the association,
391
+ # using one of the methods +:destroy+, +:delete_all+
392
+ # or +:nullify+ (or +nil+, in which case a default is used).
472
393
  def delete_records(records, method)
473
394
  raise NotImplementedError
474
395
  end
475
396
 
476
397
  def replace_records(new_target, original_target)
477
- delete(target - new_target)
398
+ delete(difference(target, new_target))
478
399
 
479
- unless concat(new_target - target)
400
+ unless concat(difference(new_target, target))
480
401
  @target = original_target
481
402
  raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
482
403
  "new records could not be saved."
@@ -485,92 +406,93 @@ module ActiveRecord
485
406
  target
486
407
  end
487
408
 
488
- def concat_records(records)
409
+ def replace_common_records_in_memory(new_target, original_target)
410
+ common_records = intersection(new_target, original_target)
411
+ common_records.each do |record|
412
+ skip_callbacks = true
413
+ replace_on_target(record, @target.index(record), skip_callbacks)
414
+ end
415
+ end
416
+
417
+ def concat_records(records, raise = false)
489
418
  result = true
490
419
 
491
- records.flatten.each do |record|
492
- raise_on_type_mismatch(record)
493
- add_to_target(record) do |r|
494
- result &&= insert_record(record) unless owner.new_record?
420
+ records.each do |record|
421
+ raise_on_type_mismatch!(record)
422
+ add_to_target(record) do
423
+ unless owner.new_record?
424
+ result &&= insert_record(record, true, raise) {
425
+ @_was_loaded = loaded?
426
+ }
427
+ end
495
428
  end
496
429
  end
497
430
 
498
- result && records
431
+ raise ActiveRecord::Rollback unless result
432
+
433
+ records
434
+ end
435
+
436
+ def replace_on_target(record, index, skip_callbacks)
437
+ callback(:before_add, record) unless skip_callbacks
438
+
439
+ set_inverse_instance(record)
440
+
441
+ @_was_loaded = true
442
+
443
+ yield(record) if block_given?
444
+
445
+ if index
446
+ target[index] = record
447
+ elsif @_was_loaded || !loaded?
448
+ @association_ids = nil
449
+ target << record
450
+ end
451
+
452
+ callback(:after_add, record) unless skip_callbacks
453
+
454
+ record
455
+ ensure
456
+ @_was_loaded = nil
499
457
  end
500
458
 
501
459
  def callback(method, record)
502
460
  callbacks_for(method).each do |callback|
503
- case callback
504
- when Symbol
505
- owner.send(callback, record)
506
- when Proc
507
- callback.call(owner, record)
508
- else
509
- callback.send(method, owner, record)
510
- end
461
+ callback.call(method, owner, record)
511
462
  end
512
463
  end
513
464
 
514
465
  def callbacks_for(callback_name)
515
466
  full_callback_name = "#{callback_name}_for_#{reflection.name}"
516
- owner.class.send(full_callback_name.to_sym) || []
517
- end
518
-
519
- # Should we deal with assoc.first or assoc.last by issuing an independent query to
520
- # the database, or by getting the target, and then taking the first/last item from that?
521
- #
522
- # If the args is just a non-empty options hash, go to the database.
523
- #
524
- # Otherwise, go to the database only if none of the following are true:
525
- # * target already loaded
526
- # * owner is new record
527
- # * custom :finder_sql exists
528
- # * target contains new or changed record(s)
529
- # * the first arg is an integer (which indicates the number of records to be returned)
530
- def fetch_first_or_last_using_find?(args)
531
- if args.first.is_a?(Hash)
532
- true
533
- else
534
- !(loaded? ||
535
- owner.new_record? ||
536
- options[:finder_sql] ||
537
- target.any? { |record| record.new_record? || record.changed? } ||
538
- args.first.kind_of?(Integer))
539
- end
467
+ owner.class.send(full_callback_name)
540
468
  end
541
469
 
542
470
  def include_in_memory?(record)
543
471
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
544
- owner.send(reflection.through_reflection.name).any? { |source|
545
- target = source.send(reflection.source_reflection.name)
546
- target.respond_to?(:include?) ? target.include?(record) : target == record
472
+ assoc = owner.association(reflection.through_reflection.name)
473
+ assoc.reader.any? { |source|
474
+ target_reflection = source.send(reflection.source_reflection.name)
475
+ target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
547
476
  } || target.include?(record)
548
477
  else
549
478
  target.include?(record)
550
479
  end
551
480
  end
552
481
 
553
- # If using a custom finder_sql, #find scans the entire collection.
482
+ # If the :inverse_of option has been
483
+ # specified, then #find scans the entire collection.
554
484
  def find_by_scan(*args)
555
485
  expects_array = args.first.kind_of?(Array)
556
- ids = args.flatten.compact.uniq.map { |arg| arg.to_i }
486
+ ids = args.flatten.compact.map(&:to_s).uniq
557
487
 
558
488
  if ids.size == 1
559
489
  id = ids.first
560
- record = load_target.detect { |r| id == r.id }
490
+ record = load_target.detect { |r| id == r.id.to_s }
561
491
  expects_array ? [ record ] : record
562
492
  else
563
- load_target.select { |r| ids.include?(r.id) }
493
+ load_target.select { |r| ids.include?(r.id.to_s) }
564
494
  end
565
495
  end
566
-
567
- # Fetches the first/last using SQL if possible, otherwise from the target array.
568
- def first_or_last(type, *args)
569
- args.shift if args.first.is_a?(Hash) && args.first.empty?
570
-
571
- collection = fetch_first_or_last_using_find?(args) ? scoped : load_target
572
- collection.send(type, *args)
573
- end
574
496
  end
575
497
  end
576
498
  end