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,10 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
- # Association proxies in Active Record are middlemen between the object that
4
- # holds the association, known as the <tt>@owner</tt>, and the actual associated
5
- # object, known as the <tt>@target</tt>. The kind of association any proxy is
6
- # about is available in <tt>@reflection</tt>. That's an instance of the class
7
- # ActiveRecord::Reflection::AssociationReflection.
5
+ # Collection proxies in Active Record are middlemen between an
6
+ # <tt>association</tt>, and its <tt>target</tt> result set.
8
7
  #
9
8
  # For example, given
10
9
  #
@@ -14,118 +13,1116 @@ module ActiveRecord
14
13
  #
15
14
  # blog = Blog.first
16
15
  #
17
- # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
18
- # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
19
- # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
20
- #
21
- # This class has most of the basic instance methods removed, and delegates
22
- # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
23
- # corner case, it even removes the +class+ method and that's why you get
24
- #
25
- # blog.posts.class # => Array
16
+ # The collection proxy returned by <tt>blog.posts</tt> is built from a
17
+ # <tt>:has_many</tt> <tt>association</tt>, and delegates to a collection
18
+ # of posts as the <tt>target</tt>.
26
19
  #
27
- # though the object behind <tt>blog.posts</tt> is not an Array, but an
28
- # ActiveRecord::Associations::HasManyAssociation.
20
+ # This class delegates unknown methods to the <tt>association</tt>'s
21
+ # relation class via a delegate cache.
29
22
  #
30
- # The <tt>@target</tt> object is not \loaded until needed. For example,
23
+ # The <tt>target</tt> result set is not loaded until needed. For example,
31
24
  #
32
25
  # blog.posts.count
33
26
  #
34
27
  # is computed directly through SQL and does not trigger by itself the
35
28
  # instantiation of the actual post records.
36
- class CollectionProxy # :nodoc:
37
- alias :proxy_extend :extend
29
+ class CollectionProxy < Relation
30
+ def initialize(klass, association) #:nodoc:
31
+ @association = association
32
+ super klass
33
+
34
+ extensions = association.extensions
35
+ extend(*extensions) if extensions.any?
36
+ end
38
37
 
39
- instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
38
+ def target
39
+ @association.target
40
+ end
40
41
 
41
- delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
42
- :lock, :readonly, :having, :pluck, :to => :scoped
42
+ def load_target
43
+ @association.load_target
44
+ end
43
45
 
44
- delegate :target, :load_target, :loaded?, :to => :@association
46
+ # Returns +true+ if the association has been loaded, otherwise +false+.
47
+ #
48
+ # person.pets.loaded? # => false
49
+ # person.pets
50
+ # person.pets.loaded? # => true
51
+ def loaded?
52
+ @association.loaded?
53
+ end
45
54
 
46
- delegate :select, :find, :first, :last,
47
- :build, :create, :create!,
48
- :concat, :replace, :delete_all, :destroy_all, :delete, :destroy, :uniq,
49
- :sum, :count, :size, :length, :empty?,
50
- :any?, :many?, :include?,
51
- :to => :@association
55
+ ##
56
+ # :method: select
57
+ #
58
+ # :call-seq:
59
+ # select(*fields, &block)
60
+ #
61
+ # Works in two ways.
62
+ #
63
+ # *First:* Specify a subset of fields to be selected from the result set.
64
+ #
65
+ # class Person < ActiveRecord::Base
66
+ # has_many :pets
67
+ # end
68
+ #
69
+ # person.pets
70
+ # # => [
71
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
72
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
73
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
74
+ # # ]
75
+ #
76
+ # person.pets.select(:name)
77
+ # # => [
78
+ # # #<Pet id: nil, name: "Fancy-Fancy">,
79
+ # # #<Pet id: nil, name: "Spook">,
80
+ # # #<Pet id: nil, name: "Choo-Choo">
81
+ # # ]
82
+ #
83
+ # person.pets.select(:id, :name)
84
+ # # => [
85
+ # # #<Pet id: 1, name: "Fancy-Fancy">,
86
+ # # #<Pet id: 2, name: "Spook">,
87
+ # # #<Pet id: 3, name: "Choo-Choo">
88
+ # # ]
89
+ #
90
+ # Be careful because this also means you're initializing a model
91
+ # object with only the fields that you've selected. If you attempt
92
+ # to access a field except +id+ that is not in the initialized record you'll
93
+ # receive:
94
+ #
95
+ # person.pets.select(:name).first.person_id
96
+ # # => ActiveModel::MissingAttributeError: missing attribute: person_id
97
+ #
98
+ # *Second:* You can pass a block so it can be used just like Array#select.
99
+ # This builds an array of objects from the database for the scope,
100
+ # converting them into an array and iterating through them using
101
+ # Array#select.
102
+ #
103
+ # person.pets.select { |pet| pet.name =~ /oo/ }
104
+ # # => [
105
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
106
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
107
+ # # ]
52
108
 
53
- def initialize(association)
54
- @association = association
55
- Array.wrap(association.options[:extend]).each { |ext| proxy_extend(ext) }
109
+ # Finds an object in the collection responding to the +id+. Uses the same
110
+ # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
111
+ # error if the object cannot be found.
112
+ #
113
+ # class Person < ActiveRecord::Base
114
+ # has_many :pets
115
+ # end
116
+ #
117
+ # person.pets
118
+ # # => [
119
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
120
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
121
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
122
+ # # ]
123
+ #
124
+ # person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
125
+ # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
126
+ #
127
+ # person.pets.find(2) { |pet| pet.name.downcase! }
128
+ # # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
129
+ #
130
+ # person.pets.find(2, 3)
131
+ # # => [
132
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
133
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
134
+ # # ]
135
+ def find(*args)
136
+ return super if block_given?
137
+ @association.find(*args)
56
138
  end
57
139
 
140
+ ##
141
+ # :method: first
142
+ #
143
+ # :call-seq:
144
+ # first(limit = nil)
145
+ #
146
+ # Returns the first record, or the first +n+ records, from the collection.
147
+ # If the collection is empty, the first form returns +nil+, and the second
148
+ # form returns an empty array.
149
+ #
150
+ # class Person < ActiveRecord::Base
151
+ # has_many :pets
152
+ # end
153
+ #
154
+ # person.pets
155
+ # # => [
156
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
157
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
158
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
159
+ # # ]
160
+ #
161
+ # person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
162
+ #
163
+ # person.pets.first(2)
164
+ # # => [
165
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
166
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
167
+ # # ]
168
+ #
169
+ # another_person_without.pets # => []
170
+ # another_person_without.pets.first # => nil
171
+ # another_person_without.pets.first(3) # => []
172
+
173
+ ##
174
+ # :method: second
175
+ #
176
+ # :call-seq:
177
+ # second()
178
+ #
179
+ # Same as #first except returns only the second record.
180
+
181
+ ##
182
+ # :method: third
183
+ #
184
+ # :call-seq:
185
+ # third()
186
+ #
187
+ # Same as #first except returns only the third record.
188
+
189
+ ##
190
+ # :method: fourth
191
+ #
192
+ # :call-seq:
193
+ # fourth()
194
+ #
195
+ # Same as #first except returns only the fourth record.
196
+
197
+ ##
198
+ # :method: fifth
199
+ #
200
+ # :call-seq:
201
+ # fifth()
202
+ #
203
+ # Same as #first except returns only the fifth record.
204
+
205
+ ##
206
+ # :method: forty_two
207
+ #
208
+ # :call-seq:
209
+ # forty_two()
210
+ #
211
+ # Same as #first except returns only the forty second record.
212
+ # Also known as accessing "the reddit".
213
+
214
+ ##
215
+ # :method: third_to_last
216
+ #
217
+ # :call-seq:
218
+ # third_to_last()
219
+ #
220
+ # Same as #first except returns only the third-to-last record.
221
+
222
+ ##
223
+ # :method: second_to_last
224
+ #
225
+ # :call-seq:
226
+ # second_to_last()
227
+ #
228
+ # Same as #first except returns only the second-to-last record.
229
+
230
+ # Returns the last record, or the last +n+ records, from the collection.
231
+ # If the collection is empty, the first form returns +nil+, and the second
232
+ # form returns an empty array.
233
+ #
234
+ # class Person < ActiveRecord::Base
235
+ # has_many :pets
236
+ # end
237
+ #
238
+ # person.pets
239
+ # # => [
240
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
241
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
242
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
243
+ # # ]
244
+ #
245
+ # person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1>
246
+ #
247
+ # person.pets.last(2)
248
+ # # => [
249
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
250
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
251
+ # # ]
252
+ #
253
+ # another_person_without.pets # => []
254
+ # another_person_without.pets.last # => nil
255
+ # another_person_without.pets.last(3) # => []
256
+ def last(limit = nil)
257
+ load_target if find_from_target?
258
+ super
259
+ end
260
+
261
+ # Gives a record (or N records if a parameter is supplied) from the collection
262
+ # using the same rules as <tt>ActiveRecord::Base.take</tt>.
263
+ #
264
+ # class Person < ActiveRecord::Base
265
+ # has_many :pets
266
+ # end
267
+ #
268
+ # person.pets
269
+ # # => [
270
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
271
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
272
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
273
+ # # ]
274
+ #
275
+ # person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
276
+ #
277
+ # person.pets.take(2)
278
+ # # => [
279
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
280
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
281
+ # # ]
282
+ #
283
+ # another_person_without.pets # => []
284
+ # another_person_without.pets.take # => nil
285
+ # another_person_without.pets.take(2) # => []
286
+ def take(limit = nil)
287
+ load_target if find_from_target?
288
+ super
289
+ end
290
+
291
+ # Returns a new object of the collection type that has been instantiated
292
+ # with +attributes+ and linked to this object, but have not yet been saved.
293
+ # You can pass an array of attributes hashes, this will return an array
294
+ # with the new objects.
295
+ #
296
+ # class Person
297
+ # has_many :pets
298
+ # end
299
+ #
300
+ # person.pets.build
301
+ # # => #<Pet id: nil, name: nil, person_id: 1>
302
+ #
303
+ # person.pets.build(name: 'Fancy-Fancy')
304
+ # # => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
305
+ #
306
+ # person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
307
+ # # => [
308
+ # # #<Pet id: nil, name: "Spook", person_id: 1>,
309
+ # # #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
310
+ # # #<Pet id: nil, name: "Brain", person_id: 1>
311
+ # # ]
312
+ #
313
+ # person.pets.size # => 5 # size of the collection
314
+ # person.pets.count # => 0 # count from database
315
+ def build(attributes = {}, &block)
316
+ @association.build(attributes, &block)
317
+ end
58
318
  alias_method :new, :build
59
319
 
320
+ # Returns a new object of the collection type that has been instantiated with
321
+ # attributes, linked to this object and that has already been saved (if it
322
+ # passes the validations).
323
+ #
324
+ # class Person
325
+ # has_many :pets
326
+ # end
327
+ #
328
+ # person.pets.create(name: 'Fancy-Fancy')
329
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
330
+ #
331
+ # person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
332
+ # # => [
333
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
334
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
335
+ # # ]
336
+ #
337
+ # person.pets.size # => 3
338
+ # person.pets.count # => 3
339
+ #
340
+ # person.pets.find(1, 2, 3)
341
+ # # => [
342
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
343
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
344
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
345
+ # # ]
346
+ def create(attributes = {}, &block)
347
+ @association.create(attributes, &block)
348
+ end
349
+
350
+ # Like #create, except that if the record is invalid, raises an exception.
351
+ #
352
+ # class Person
353
+ # has_many :pets
354
+ # end
355
+ #
356
+ # class Pet
357
+ # validates :name, presence: true
358
+ # end
359
+ #
360
+ # person.pets.create!(name: nil)
361
+ # # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
362
+ def create!(attributes = {}, &block)
363
+ @association.create!(attributes, &block)
364
+ end
365
+
366
+ # Replaces this collection with +other_array+. This will perform a diff
367
+ # and delete/add only records that have changed.
368
+ #
369
+ # class Person < ActiveRecord::Base
370
+ # has_many :pets
371
+ # end
372
+ #
373
+ # person.pets
374
+ # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
375
+ #
376
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
377
+ #
378
+ # person.pets.replace(other_pets)
379
+ #
380
+ # person.pets
381
+ # # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
382
+ #
383
+ # If the supplied array has an incorrect association type, it raises
384
+ # an <tt>ActiveRecord::AssociationTypeMismatch</tt> error:
385
+ #
386
+ # person.pets.replace(["doo", "ggie", "gaga"])
387
+ # # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
388
+ def replace(other_array)
389
+ @association.replace(other_array)
390
+ end
391
+
392
+ # Deletes all the records from the collection according to the strategy
393
+ # specified by the +:dependent+ option. If no +:dependent+ option is given,
394
+ # then it will follow the default strategy.
395
+ #
396
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
397
+ # +:delete_all+.
398
+ #
399
+ # For +has_many+ associations, the default deletion strategy is +:nullify+.
400
+ # This sets the foreign keys to +NULL+.
401
+ #
402
+ # class Person < ActiveRecord::Base
403
+ # has_many :pets # dependent: :nullify option by default
404
+ # end
405
+ #
406
+ # person.pets.size # => 3
407
+ # person.pets
408
+ # # => [
409
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
410
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
411
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
412
+ # # ]
413
+ #
414
+ # person.pets.delete_all
415
+ # # => [
416
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
417
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
418
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
419
+ # # ]
420
+ #
421
+ # person.pets.size # => 0
422
+ # person.pets # => []
423
+ #
424
+ # Pet.find(1, 2, 3)
425
+ # # => [
426
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
427
+ # # #<Pet id: 2, name: "Spook", person_id: nil>,
428
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
429
+ # # ]
430
+ #
431
+ # Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
432
+ # +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
433
+ # Records are not instantiated and callbacks will not be fired.
434
+ #
435
+ # class Person < ActiveRecord::Base
436
+ # has_many :pets, dependent: :destroy
437
+ # end
438
+ #
439
+ # person.pets.size # => 3
440
+ # person.pets
441
+ # # => [
442
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
443
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
444
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
445
+ # # ]
446
+ #
447
+ # person.pets.delete_all
448
+ #
449
+ # Pet.find(1, 2, 3)
450
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
451
+ #
452
+ # If it is set to <tt>:delete_all</tt>, all the objects are deleted
453
+ # *without* calling their +destroy+ method.
454
+ #
455
+ # class Person < ActiveRecord::Base
456
+ # has_many :pets, dependent: :delete_all
457
+ # end
458
+ #
459
+ # person.pets.size # => 3
460
+ # person.pets
461
+ # # => [
462
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
463
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
464
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
465
+ # # ]
466
+ #
467
+ # person.pets.delete_all
468
+ #
469
+ # Pet.find(1, 2, 3)
470
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
471
+ def delete_all(dependent = nil)
472
+ @association.delete_all(dependent).tap { reset_scope }
473
+ end
474
+
475
+ # Deletes the records of the collection directly from the database
476
+ # ignoring the +:dependent+ option. Records are instantiated and it
477
+ # invokes +before_remove+, +after_remove+ , +before_destroy+ and
478
+ # +after_destroy+ callbacks.
479
+ #
480
+ # class Person < ActiveRecord::Base
481
+ # has_many :pets
482
+ # end
483
+ #
484
+ # person.pets.size # => 3
485
+ # person.pets
486
+ # # => [
487
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
488
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
489
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
490
+ # # ]
491
+ #
492
+ # person.pets.destroy_all
493
+ #
494
+ # person.pets.size # => 0
495
+ # person.pets # => []
496
+ #
497
+ # Pet.find(1) # => Couldn't find Pet with id=1
498
+ def destroy_all
499
+ @association.destroy_all.tap { reset_scope }
500
+ end
501
+
502
+ # Deletes the +records+ supplied from the collection according to the strategy
503
+ # specified by the +:dependent+ option. If no +:dependent+ option is given,
504
+ # then it will follow the default strategy. Returns an array with the
505
+ # deleted records.
506
+ #
507
+ # For <tt>has_many :through</tt> associations, the default deletion strategy is
508
+ # +:delete_all+.
509
+ #
510
+ # For +has_many+ associations, the default deletion strategy is +:nullify+.
511
+ # This sets the foreign keys to +NULL+.
512
+ #
513
+ # class Person < ActiveRecord::Base
514
+ # has_many :pets # dependent: :nullify option by default
515
+ # end
516
+ #
517
+ # person.pets.size # => 3
518
+ # person.pets
519
+ # # => [
520
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
521
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
522
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
523
+ # # ]
524
+ #
525
+ # person.pets.delete(Pet.find(1))
526
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
527
+ #
528
+ # person.pets.size # => 2
529
+ # person.pets
530
+ # # => [
531
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
532
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
533
+ # # ]
534
+ #
535
+ # Pet.find(1)
536
+ # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
537
+ #
538
+ # If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
539
+ # their +destroy+ method. See +destroy+ for more information.
540
+ #
541
+ # class Person < ActiveRecord::Base
542
+ # has_many :pets, dependent: :destroy
543
+ # end
544
+ #
545
+ # person.pets.size # => 3
546
+ # person.pets
547
+ # # => [
548
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
549
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
550
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
551
+ # # ]
552
+ #
553
+ # person.pets.delete(Pet.find(1), Pet.find(3))
554
+ # # => [
555
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
556
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
557
+ # # ]
558
+ #
559
+ # person.pets.size # => 1
560
+ # person.pets
561
+ # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
562
+ #
563
+ # Pet.find(1, 3)
564
+ # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
565
+ #
566
+ # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
567
+ # *without* calling their +destroy+ method.
568
+ #
569
+ # class Person < ActiveRecord::Base
570
+ # has_many :pets, dependent: :delete_all
571
+ # end
572
+ #
573
+ # person.pets.size # => 3
574
+ # person.pets
575
+ # # => [
576
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
577
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
578
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
579
+ # # ]
580
+ #
581
+ # person.pets.delete(Pet.find(1))
582
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
583
+ #
584
+ # person.pets.size # => 2
585
+ # person.pets
586
+ # # => [
587
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
588
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
589
+ # # ]
590
+ #
591
+ # Pet.find(1)
592
+ # # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
593
+ #
594
+ # You can pass +Integer+ or +String+ values, it finds the records
595
+ # responding to the +id+ and executes delete on them.
596
+ #
597
+ # class Person < ActiveRecord::Base
598
+ # has_many :pets
599
+ # end
600
+ #
601
+ # person.pets.size # => 3
602
+ # person.pets
603
+ # # => [
604
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
605
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
606
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
607
+ # # ]
608
+ #
609
+ # person.pets.delete("1")
610
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
611
+ #
612
+ # person.pets.delete(2, 3)
613
+ # # => [
614
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
615
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
616
+ # # ]
617
+ def delete(*records)
618
+ @association.delete(*records).tap { reset_scope }
619
+ end
620
+
621
+ # Destroys the +records+ supplied and removes them from the collection.
622
+ # This method will _always_ remove record from the database ignoring
623
+ # the +:dependent+ option. Returns an array with the removed records.
624
+ #
625
+ # class Person < ActiveRecord::Base
626
+ # has_many :pets
627
+ # end
628
+ #
629
+ # person.pets.size # => 3
630
+ # person.pets
631
+ # # => [
632
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
633
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
634
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
635
+ # # ]
636
+ #
637
+ # person.pets.destroy(Pet.find(1))
638
+ # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
639
+ #
640
+ # person.pets.size # => 2
641
+ # person.pets
642
+ # # => [
643
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
644
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
645
+ # # ]
646
+ #
647
+ # person.pets.destroy(Pet.find(2), Pet.find(3))
648
+ # # => [
649
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
650
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
651
+ # # ]
652
+ #
653
+ # person.pets.size # => 0
654
+ # person.pets # => []
655
+ #
656
+ # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
657
+ #
658
+ # You can pass +Integer+ or +String+ values, it finds the records
659
+ # responding to the +id+ and then deletes them from the database.
660
+ #
661
+ # person.pets.size # => 3
662
+ # person.pets
663
+ # # => [
664
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
665
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
666
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
667
+ # # ]
668
+ #
669
+ # person.pets.destroy("4")
670
+ # # => #<Pet id: 4, name: "Benny", person_id: 1>
671
+ #
672
+ # person.pets.size # => 2
673
+ # person.pets
674
+ # # => [
675
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
676
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
677
+ # # ]
678
+ #
679
+ # person.pets.destroy(5, 6)
680
+ # # => [
681
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
682
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
683
+ # # ]
684
+ #
685
+ # person.pets.size # => 0
686
+ # person.pets # => []
687
+ #
688
+ # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
689
+ def destroy(*records)
690
+ @association.destroy(*records).tap { reset_scope }
691
+ end
692
+
693
+ ##
694
+ # :method: distinct
695
+ #
696
+ # :call-seq:
697
+ # distinct(value = true)
698
+ #
699
+ # Specifies whether the records should be unique or not.
700
+ #
701
+ # class Person < ActiveRecord::Base
702
+ # has_many :pets
703
+ # end
704
+ #
705
+ # person.pets.select(:name)
706
+ # # => [
707
+ # # #<Pet name: "Fancy-Fancy">,
708
+ # # #<Pet name: "Fancy-Fancy">
709
+ # # ]
710
+ #
711
+ # person.pets.select(:name).distinct
712
+ # # => [#<Pet name: "Fancy-Fancy">]
713
+ #
714
+ # person.pets.select(:name).distinct.distinct(false)
715
+ # # => [
716
+ # # #<Pet name: "Fancy-Fancy">,
717
+ # # #<Pet name: "Fancy-Fancy">
718
+ # # ]
719
+
720
+ #--
721
+ def calculate(operation, column_name)
722
+ null_scope? ? scope.calculate(operation, column_name) : super
723
+ end
724
+
725
+ def pluck(*column_names)
726
+ null_scope? ? scope.pluck(*column_names) : super
727
+ end
728
+
729
+ ##
730
+ # :method: count
731
+ #
732
+ # :call-seq:
733
+ # count(column_name = nil, &block)
734
+ #
735
+ # Count all records.
736
+ #
737
+ # class Person < ActiveRecord::Base
738
+ # has_many :pets
739
+ # end
740
+ #
741
+ # # This will perform the count using SQL.
742
+ # person.pets.count # => 3
743
+ # person.pets
744
+ # # => [
745
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
746
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
747
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
748
+ # # ]
749
+ #
750
+ # Passing a block will select all of a person's pets in SQL and then
751
+ # perform the count using Ruby.
752
+ #
753
+ # person.pets.count { |pet| pet.name.include?('-') } # => 2
754
+
755
+ # Returns the size of the collection. If the collection hasn't been loaded,
756
+ # it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
757
+ #
758
+ # If the collection has been already loaded +size+ and +length+ are
759
+ # equivalent. If not and you are going to need the records anyway
760
+ # +length+ will take one less query. Otherwise +size+ is more efficient.
761
+ #
762
+ # class Person < ActiveRecord::Base
763
+ # has_many :pets
764
+ # end
765
+ #
766
+ # person.pets.size # => 3
767
+ # # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1
768
+ #
769
+ # person.pets # This will execute a SELECT * FROM query
770
+ # # => [
771
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
772
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
773
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
774
+ # # ]
775
+ #
776
+ # person.pets.size # => 3
777
+ # # Because the collection is already loaded, this will behave like
778
+ # # collection.size and no SQL count query is executed.
779
+ def size
780
+ @association.size
781
+ end
782
+
783
+ ##
784
+ # :method: length
785
+ #
786
+ # :call-seq:
787
+ # length()
788
+ #
789
+ # Returns the size of the collection calling +size+ on the target.
790
+ # If the collection has been already loaded, +length+ and +size+ are
791
+ # equivalent. If not and you are going to need the records anyway this
792
+ # method will take one less query. Otherwise +size+ is more efficient.
793
+ #
794
+ # class Person < ActiveRecord::Base
795
+ # has_many :pets
796
+ # end
797
+ #
798
+ # person.pets.length # => 3
799
+ # # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1
800
+ #
801
+ # # Because the collection is loaded, you can
802
+ # # call the collection with no additional queries:
803
+ # person.pets
804
+ # # => [
805
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
806
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
807
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
808
+ # # ]
809
+
810
+ # Returns +true+ if the collection is empty. If the collection has been
811
+ # loaded it is equivalent
812
+ # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
813
+ # it is equivalent to <tt>!collection.exists?</tt>. If the collection has
814
+ # not already been loaded and you are going to fetch the records anyway it
815
+ # is better to check <tt>collection.length.zero?</tt>.
816
+ #
817
+ # class Person < ActiveRecord::Base
818
+ # has_many :pets
819
+ # end
820
+ #
821
+ # person.pets.count # => 1
822
+ # person.pets.empty? # => false
823
+ #
824
+ # person.pets.delete_all
825
+ #
826
+ # person.pets.count # => 0
827
+ # person.pets.empty? # => true
828
+ def empty?
829
+ @association.empty?
830
+ end
831
+
832
+ ##
833
+ # :method: any?
834
+ #
835
+ # :call-seq:
836
+ # any?()
837
+ #
838
+ # Returns +true+ if the collection is not empty.
839
+ #
840
+ # class Person < ActiveRecord::Base
841
+ # has_many :pets
842
+ # end
843
+ #
844
+ # person.pets.count # => 0
845
+ # person.pets.any? # => false
846
+ #
847
+ # person.pets << Pet.new(name: 'Snoop')
848
+ # person.pets.count # => 1
849
+ # person.pets.any? # => true
850
+ #
851
+ # You can also pass a +block+ to define criteria. The behavior
852
+ # is the same, it returns true if the collection based on the
853
+ # criteria is not empty.
854
+ #
855
+ # person.pets
856
+ # # => [#<Pet name: "Snoop", group: "dogs">]
857
+ #
858
+ # person.pets.any? do |pet|
859
+ # pet.group == 'cats'
860
+ # end
861
+ # # => false
862
+ #
863
+ # person.pets.any? do |pet|
864
+ # pet.group == 'dogs'
865
+ # end
866
+ # # => true
867
+
868
+ ##
869
+ # :method: many?
870
+ #
871
+ # :call-seq:
872
+ # many?()
873
+ #
874
+ # Returns true if the collection has more than one record.
875
+ # Equivalent to <tt>collection.size > 1</tt>.
876
+ #
877
+ # class Person < ActiveRecord::Base
878
+ # has_many :pets
879
+ # end
880
+ #
881
+ # person.pets.count # => 1
882
+ # person.pets.many? # => false
883
+ #
884
+ # person.pets << Pet.new(name: 'Snoopy')
885
+ # person.pets.count # => 2
886
+ # person.pets.many? # => true
887
+ #
888
+ # You can also pass a +block+ to define criteria. The
889
+ # behavior is the same, it returns true if the collection
890
+ # based on the criteria has more than one record.
891
+ #
892
+ # person.pets
893
+ # # => [
894
+ # # #<Pet name: "Gorby", group: "cats">,
895
+ # # #<Pet name: "Puff", group: "cats">,
896
+ # # #<Pet name: "Snoop", group: "dogs">
897
+ # # ]
898
+ #
899
+ # person.pets.many? do |pet|
900
+ # pet.group == 'dogs'
901
+ # end
902
+ # # => false
903
+ #
904
+ # person.pets.many? do |pet|
905
+ # pet.group == 'cats'
906
+ # end
907
+ # # => true
908
+
909
+ # Returns +true+ if the given +record+ is present in the collection.
910
+ #
911
+ # class Person < ActiveRecord::Base
912
+ # has_many :pets
913
+ # end
914
+ #
915
+ # person.pets # => [#<Pet id: 20, name: "Snoop">]
916
+ #
917
+ # person.pets.include?(Pet.find(20)) # => true
918
+ # person.pets.include?(Pet.find(21)) # => false
919
+ def include?(record)
920
+ !!@association.include?(record)
921
+ end
922
+
60
923
  def proxy_association
61
924
  @association
62
925
  end
63
926
 
64
- def scoped
65
- association = @association
66
- association.scoped.extending do
67
- define_method(:proxy_association) { association }
68
- end
927
+ # Returns a <tt>Relation</tt> object for the records in this association
928
+ def scope
929
+ @scope ||= @association.scope
69
930
  end
70
931
 
71
- def respond_to?(name, include_private = false)
72
- super ||
73
- (load_target && target.respond_to?(name, include_private)) ||
74
- proxy_association.klass.respond_to?(name, include_private)
75
- end
76
-
77
- def method_missing(method, *args, &block)
78
- match = DynamicFinderMatch.match(method)
79
- if match && match.instantiator?
80
- send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
81
- proxy_association.send :set_owner_attributes, r
82
- proxy_association.send :add_to_target, r
83
- yield(r) if block_given?
84
- end
85
-
86
- elsif target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
87
- if load_target
88
- if target.respond_to?(method)
89
- target.send(method, *args, &block)
90
- else
91
- begin
92
- super
93
- rescue NoMethodError => e
94
- raise e, e.message.sub(/ for #<.*$/, " via proxy for #{target}")
95
- end
96
- end
97
- end
98
-
99
- else
100
- scoped.readonly(nil).send(method, *args, &block)
101
- end
932
+ # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
933
+ # contain the same number of elements and if each element is equal
934
+ # to the corresponding element in the +other+ array, otherwise returns
935
+ # +false+.
936
+ #
937
+ # class Person < ActiveRecord::Base
938
+ # has_many :pets
939
+ # end
940
+ #
941
+ # person.pets
942
+ # # => [
943
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
944
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
945
+ # # ]
946
+ #
947
+ # other = person.pets.to_ary
948
+ #
949
+ # person.pets == other
950
+ # # => true
951
+ #
952
+ # other = [Pet.new(id: 1), Pet.new(id: 2)]
953
+ #
954
+ # person.pets == other
955
+ # # => false
956
+ def ==(other)
957
+ load_target == other
102
958
  end
103
959
 
104
- # Forwards <tt>===</tt> explicitly to the \target because the instance method
105
- # removal above doesn't catch it. Loads the \target if needed.
106
- def ===(other)
107
- other === load_target
108
- end
960
+ ##
961
+ # :method: to_ary
962
+ #
963
+ # :call-seq:
964
+ # to_ary()
965
+ #
966
+ # Returns a new array of objects from the collection. If the collection
967
+ # hasn't been loaded, it fetches the records from the database.
968
+ #
969
+ # class Person < ActiveRecord::Base
970
+ # has_many :pets
971
+ # end
972
+ #
973
+ # person.pets
974
+ # # => [
975
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
976
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
977
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
978
+ # # ]
979
+ #
980
+ # other_pets = person.pets.to_ary
981
+ # # => [
982
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
983
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
984
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
985
+ # # ]
986
+ #
987
+ # other_pets.replace([Pet.new(name: 'BooGoo')])
988
+ #
989
+ # other_pets
990
+ # # => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
991
+ #
992
+ # person.pets
993
+ # # This is not affected by replace
994
+ # # => [
995
+ # # #<Pet id: 4, name: "Benny", person_id: 1>,
996
+ # # #<Pet id: 5, name: "Brain", person_id: 1>,
997
+ # # #<Pet id: 6, name: "Boss", person_id: 1>
998
+ # # ]
109
999
 
110
- def to_ary
111
- load_target.dup
1000
+ def records # :nodoc:
1001
+ load_target
112
1002
  end
113
- alias_method :to_a, :to_ary
114
1003
 
1004
+ # Adds one or more +records+ to the collection by setting their foreign keys
1005
+ # to the association's primary key. Since <tt><<</tt> flattens its argument list and
1006
+ # inserts each record, +push+ and +concat+ behave identically. Returns +self+
1007
+ # so several appends may be chained together.
1008
+ #
1009
+ # class Person < ActiveRecord::Base
1010
+ # has_many :pets
1011
+ # end
1012
+ #
1013
+ # person.pets.size # => 0
1014
+ # person.pets << Pet.new(name: 'Fancy-Fancy')
1015
+ # person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
1016
+ # person.pets.size # => 3
1017
+ #
1018
+ # person.id # => 1
1019
+ # person.pets
1020
+ # # => [
1021
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
1022
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
1023
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
1024
+ # # ]
115
1025
  def <<(*records)
116
1026
  proxy_association.concat(records) && self
117
1027
  end
118
1028
  alias_method :push, :<<
1029
+ alias_method :append, :<<
1030
+ alias_method :concat, :<<
119
1031
 
1032
+ def prepend(*args) # :nodoc:
1033
+ raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
1034
+ end
1035
+
1036
+ # Equivalent to +delete_all+. The difference is that returns +self+, instead
1037
+ # of an array with the deleted objects, so methods can be chained. See
1038
+ # +delete_all+ for more information.
1039
+ # Note that because +delete_all+ removes records by directly
1040
+ # running an SQL query into the database, the +updated_at+ column of
1041
+ # the object is not changed.
120
1042
  def clear
121
1043
  delete_all
122
1044
  self
123
1045
  end
124
1046
 
1047
+ # Reloads the collection from the database. Returns +self+.
1048
+ #
1049
+ # class Person < ActiveRecord::Base
1050
+ # has_many :pets
1051
+ # end
1052
+ #
1053
+ # person.pets # fetches pets from the database
1054
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1055
+ #
1056
+ # person.pets # uses the pets cache
1057
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1058
+ #
1059
+ # person.pets.reload # fetches pets from the database
1060
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
125
1061
  def reload
126
- proxy_association.reload
1062
+ proxy_association.reload(true)
1063
+ reset_scope
1064
+ end
1065
+
1066
+ # Unloads the association. Returns +self+.
1067
+ #
1068
+ # class Person < ActiveRecord::Base
1069
+ # has_many :pets
1070
+ # end
1071
+ #
1072
+ # person.pets # fetches pets from the database
1073
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1074
+ #
1075
+ # person.pets # uses the pets cache
1076
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1077
+ #
1078
+ # person.pets.reset # clears the pets cache
1079
+ #
1080
+ # person.pets # fetches pets from the database
1081
+ # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1082
+ def reset
1083
+ proxy_association.reset
1084
+ proxy_association.reset_scope
1085
+ reset_scope
1086
+ end
1087
+
1088
+ def reset_scope # :nodoc:
1089
+ @offsets = {}
1090
+ @scope = nil
127
1091
  self
128
1092
  end
1093
+
1094
+ delegate_methods = [
1095
+ QueryMethods,
1096
+ SpawnMethods,
1097
+ ].flat_map { |klass|
1098
+ klass.public_instance_methods(false)
1099
+ } - self.public_instance_methods(false) - [:select] + [:scoping, :values]
1100
+
1101
+ delegate(*delegate_methods, to: :scope)
1102
+
1103
+ private
1104
+
1105
+ def find_nth_with_limit(index, limit)
1106
+ load_target if find_from_target?
1107
+ super
1108
+ end
1109
+
1110
+ def find_nth_from_last(index)
1111
+ load_target if find_from_target?
1112
+ super
1113
+ end
1114
+
1115
+ def null_scope?
1116
+ @association.null_scope?
1117
+ end
1118
+
1119
+ def find_from_target?
1120
+ @association.find_from_target?
1121
+ end
1122
+
1123
+ def exec_queries
1124
+ load_target
1125
+ end
129
1126
  end
130
1127
  end
131
1128
  end