activerecord 4.2.8 → 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 (372) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +612 -1583
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +11 -6
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +125 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +134 -286
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +137 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -88
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +91 -37
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -244
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +460 -204
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -627
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +551 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +176 -41
  129. data/lib/active_record/core.rb +251 -231
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +5 -3
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +626 -283
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +314 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +147 -46
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +330 -197
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +518 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +277 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +575 -394
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +86 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +308 -99
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +216 -150
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +51 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +164 -60
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -58
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,28 +1,16 @@
1
- # This class is inherited by the has_many and has_many_and_belongs_to_many association classes
1
+ # frozen_string_literal: true
2
2
 
3
- require 'active_record/associations'
3
+ require "active_record/associations"
4
4
 
5
- module ActiveRecord::Associations::Builder
5
+ module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class CollectionAssociation < Association #:nodoc:
7
-
8
7
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
9
8
 
10
- def valid_options
9
+ def self.valid_options(options)
11
10
  super + [:table_name, :before_add,
12
11
  :after_add, :before_remove, :after_remove, :extend]
13
12
  end
14
13
 
15
- attr_reader :block_extension
16
-
17
- def initialize(model, name, scope, options)
18
- super
19
- @mod = nil
20
- if block_given?
21
- @mod = Module.new(&Proc.new)
22
- @scope = wrap_scope @scope, @mod
23
- end
24
- end
25
-
26
14
  def self.define_callbacks(model, reflection)
27
15
  super
28
16
  name = reflection.name
@@ -32,10 +20,11 @@ module ActiveRecord::Associations::Builder
32
20
  }
33
21
  end
34
22
 
35
- def define_extensions(model)
36
- if @mod
37
- extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
38
- model.parent.const_set(extension_module_name, @mod)
23
+ def self.define_extensions(model, name, &block)
24
+ if block_given?
25
+ extension_module_name = "#{name.to_s.camelize}AssociationExtension"
26
+ extension = Module.new(&block)
27
+ model.const_set(extension_module_name, extension)
39
28
  end
40
29
  end
41
30
 
@@ -78,18 +67,6 @@ module ActiveRecord::Associations::Builder
78
67
  CODE
79
68
  end
80
69
 
81
- private
82
-
83
- def wrap_scope(scope, mod)
84
- if scope
85
- if scope.arity > 0
86
- proc { |owner| instance_exec(owner, &scope).extending(mod) }
87
- else
88
- proc { instance_exec(&scope).extending(mod) }
89
- end
90
- else
91
- proc { extending(mod) }
92
- end
93
- end
70
+ private_class_method :valid_options, :define_callback, :define_extensions, :define_readers, :define_writers
94
71
  end
95
72
  end
@@ -1,38 +1,7 @@
1
- module ActiveRecord::Associations::Builder
2
- class HasAndBelongsToMany # :nodoc:
3
- class JoinTableResolver
4
- KnownTable = Struct.new :join_table
5
-
6
- class KnownClass
7
- def initialize(lhs_class, rhs_class_name)
8
- @lhs_class = lhs_class
9
- @rhs_class_name = rhs_class_name
10
- @join_table = nil
11
- end
12
-
13
- def join_table
14
- @join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
15
- end
16
-
17
- private
18
-
19
- def klass
20
- @lhs_class.send(:compute_type, @rhs_class_name)
21
- end
22
- end
23
-
24
- def self.build(lhs_class, name, options)
25
- if options[:join_table]
26
- KnownTable.new options[:join_table].to_s
27
- else
28
- class_name = options.fetch(:class_name) {
29
- name.to_s.camelize.singularize
30
- }
31
- KnownClass.new lhs_class, class_name
32
- end
33
- end
34
- end
1
+ # frozen_string_literal: true
35
2
 
3
+ module ActiveRecord::Associations::Builder # :nodoc:
4
+ class HasAndBelongsToMany # :nodoc:
36
5
  attr_reader :lhs_model, :association_name, :options
37
6
 
38
7
  def initialize(association_name, lhs_model, options)
@@ -42,10 +11,8 @@ module ActiveRecord::Associations::Builder
42
11
  end
43
12
 
44
13
  def through_model
45
- habtm = JoinTableResolver.build lhs_model, association_name, options
46
-
47
14
  join_model = Class.new(ActiveRecord::Base) {
48
- class << self;
15
+ class << self
49
16
  attr_accessor :left_model
50
17
  attr_accessor :name
51
18
  attr_accessor :table_name_resolver
@@ -54,7 +21,9 @@ module ActiveRecord::Associations::Builder
54
21
  end
55
22
 
56
23
  def self.table_name
57
- table_name_resolver.join_table
24
+ # Table name needs to be resolved lazily
25
+ # because RHS class might not have been loaded
26
+ @table_name ||= table_name_resolver.call
58
27
  end
59
28
 
60
29
  def self.compute_type(class_name)
@@ -62,13 +31,13 @@ module ActiveRecord::Associations::Builder
62
31
  end
63
32
 
64
33
  def self.add_left_association(name, options)
65
- belongs_to name, options
34
+ belongs_to name, required: false, **options
66
35
  self.left_reflection = _reflect_on_association(name)
67
36
  end
68
37
 
69
38
  def self.add_right_association(name, options)
70
39
  rhs_name = name.to_s.singularize.to_sym
71
- belongs_to rhs_name, options
40
+ belongs_to rhs_name, required: false, **options
72
41
  self.right_reflection = _reflect_on_association(rhs_name)
73
42
  end
74
43
 
@@ -76,10 +45,15 @@ module ActiveRecord::Associations::Builder
76
45
  left_model.retrieve_connection
77
46
  end
78
47
 
48
+ private
49
+
50
+ def self.suppress_composite_primary_key(pk)
51
+ pk unless pk.is_a?(Array)
52
+ end
79
53
  }
80
54
 
81
55
  join_model.name = "HABTM_#{association_name.to_s.camelize}"
82
- join_model.table_name_resolver = habtm
56
+ join_model.table_name_resolver = -> { table_name }
83
57
  join_model.left_model = lhs_model
84
58
 
85
59
  join_model.add_left_association :left_side, anonymous_class: lhs_model
@@ -89,40 +63,52 @@ module ActiveRecord::Associations::Builder
89
63
 
90
64
  def middle_reflection(join_model)
91
65
  middle_name = [lhs_model.name.downcase.pluralize,
92
- association_name].join('_').gsub(/::/, '_').to_sym
66
+ association_name.to_s].sort.join("_").gsub("::", "_").to_sym
93
67
  middle_options = middle_options join_model
94
- hm_builder = HasMany.create_builder(lhs_model,
95
- middle_name,
96
- nil,
97
- middle_options)
98
- hm_builder.build lhs_model
68
+
69
+ HasMany.create_reflection(lhs_model,
70
+ middle_name,
71
+ nil,
72
+ middle_options)
99
73
  end
100
74
 
101
75
  private
102
76
 
103
- def middle_options(join_model)
104
- middle_options = {}
105
- middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
106
- middle_options[:source] = join_model.left_reflection.name
107
- if options.key? :foreign_key
108
- middle_options[:foreign_key] = options[:foreign_key]
77
+ def middle_options(join_model)
78
+ middle_options = {}
79
+ middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
80
+ middle_options[:source] = join_model.left_reflection.name
81
+ if options.key? :foreign_key
82
+ middle_options[:foreign_key] = options[:foreign_key]
83
+ end
84
+ middle_options
109
85
  end
110
- middle_options
111
- end
112
86
 
113
- def belongs_to_options(options)
114
- rhs_options = {}
115
-
116
- if options.key? :class_name
117
- rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
118
- rhs_options[:class_name] = options[:class_name]
87
+ def table_name
88
+ if options[:join_table]
89
+ options[:join_table].to_s
90
+ else
91
+ class_name = options.fetch(:class_name) {
92
+ association_name.to_s.camelize.singularize
93
+ }
94
+ klass = lhs_model.send(:compute_type, class_name.to_s)
95
+ [lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
96
+ end
119
97
  end
120
98
 
121
- if options.key? :association_foreign_key
122
- rhs_options[:foreign_key] = options[:association_foreign_key]
123
- end
99
+ def belongs_to_options(options)
100
+ rhs_options = {}
124
101
 
125
- rhs_options
126
- end
102
+ if options.key? :class_name
103
+ rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
104
+ rhs_options[:class_name] = options[:class_name]
105
+ end
106
+
107
+ if options.key? :association_foreign_key
108
+ rhs_options[:foreign_key] = options[:association_foreign_key]
109
+ end
110
+
111
+ rhs_options
112
+ end
127
113
  end
128
114
  end
@@ -1,15 +1,19 @@
1
- module ActiveRecord::Associations::Builder
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasMany < CollectionAssociation #:nodoc:
3
- def macro
5
+ def self.macro
4
6
  :has_many
5
7
  end
6
8
 
7
- def valid_options
8
- super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type]
9
+ def self.valid_options(options)
10
+ super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
9
11
  end
10
12
 
11
13
  def self.valid_dependent_options
12
14
  [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
13
15
  end
16
+
17
+ private_class_method :macro, :valid_options, :valid_dependent_options
14
18
  end
15
19
  end
@@ -1,11 +1,13 @@
1
- module ActiveRecord::Associations::Builder
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasOne < SingularAssociation #:nodoc:
3
- def macro
5
+ def self.macro
4
6
  :has_one
5
7
  end
6
8
 
7
- def valid_options
8
- valid = super + [:as, :foreign_type]
9
+ def self.valid_options(options)
10
+ valid = super + [:as, :touch]
9
11
  valid += [:through, :source, :source_type] if options[:through]
10
12
  valid
11
13
  end
@@ -14,10 +16,49 @@ module ActiveRecord::Associations::Builder
14
16
  [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
15
17
  end
16
18
 
17
- private
19
+ def self.define_callbacks(model, reflection)
20
+ super
21
+ add_touch_callbacks(model, reflection) if reflection.options[:touch]
22
+ end
18
23
 
19
24
  def self.add_destroy_callbacks(model, reflection)
20
25
  super unless reflection.options[:through]
21
26
  end
27
+
28
+ def self.define_validations(model, reflection)
29
+ super
30
+ if reflection.options[:required]
31
+ model.validates_presence_of reflection.name, message: :required
32
+ end
33
+ end
34
+
35
+ def self.touch_record(o, name, touch)
36
+ record = o.send name
37
+
38
+ return unless record && record.persisted?
39
+
40
+ if touch != true
41
+ record.touch(touch)
42
+ else
43
+ record.touch
44
+ end
45
+ end
46
+
47
+ def self.add_touch_callbacks(model, reflection)
48
+ name = reflection.name
49
+ touch = reflection.options[:touch]
50
+
51
+ callback = lambda { |record|
52
+ HasOne.touch_record(record, name, touch)
53
+ }
54
+
55
+ model.after_create callback, if: :saved_changes?
56
+ model.after_update callback, if: :saved_changes?
57
+ model.after_destroy callback
58
+ model.after_touch callback
59
+ end
60
+
61
+ private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
62
+ :define_callbacks, :define_validations, :add_touch_callbacks
22
63
  end
23
64
  end
@@ -1,14 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class is inherited by the has_one and belongs_to association classes
2
4
 
3
- module ActiveRecord::Associations::Builder
5
+ module ActiveRecord::Associations::Builder # :nodoc:
4
6
  class SingularAssociation < Association #:nodoc:
5
- def valid_options
6
- super + [:dependent, :primary_key, :inverse_of, :required]
7
+ def self.valid_options(options)
8
+ super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
7
9
  end
8
10
 
9
11
  def self.define_accessors(model, reflection)
10
12
  super
11
- define_constructors(model.generated_association_methods, reflection.name) if reflection.constructable?
13
+ mixin = model.generated_association_methods
14
+ name = reflection.name
15
+
16
+ define_constructors(mixin, name) if reflection.constructable?
17
+
18
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
19
+ def reload_#{name}
20
+ association(:#{name}).force_reload_reader
21
+ end
22
+ CODE
12
23
  end
13
24
 
14
25
  # Defines the (build|create)_association methods for belongs_to or has_one association
@@ -28,11 +39,6 @@ module ActiveRecord::Associations::Builder
28
39
  CODE
29
40
  end
30
41
 
31
- def self.define_validations(model, reflection)
32
- super
33
- if reflection.options[:required]
34
- model.validates_presence_of reflection.name
35
- end
36
- end
42
+ private_class_method :valid_options, :define_accessors, :define_constructors
37
43
  end
38
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  # = Active Record Association Collection
@@ -10,9 +12,9 @@ module ActiveRecord
10
12
  # HasManyAssociation => has_many
11
13
  # HasManyThroughAssociation + ThroughAssociation => has_many :through
12
14
  #
13
- # CollectionAssociation class provides common methods to the collections
15
+ # The CollectionAssociation class provides common methods to the collections
14
16
  # defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
15
- # +:through association+ option.
17
+ # the +:through association+ option.
16
18
  #
17
19
  # You need to be careful with assumptions regarding the target: The proxy
18
20
  # does not fetch records from the database until it needs them, but new
@@ -24,22 +26,14 @@ module ActiveRecord
24
26
  # If you need to work on all current children, new and existing records,
25
27
  # +load_target+ and the +loaded+ flag are your friends.
26
28
  class CollectionAssociation < Association #:nodoc:
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
- if owner.new_record?
37
- # Cache the proxy separately before the owner has an id
38
- # or else a post-save proxy will still lack the id
39
- @new_record_proxy ||= CollectionProxy.create(klass, self)
40
- else
41
- @proxy ||= CollectionProxy.create(klass, self)
42
- end
35
+ @proxy ||= CollectionProxy.create(klass, self)
36
+ @proxy.reset_scope
43
37
  end
44
38
 
45
39
  # Implements the writer method, e.g. foo.items= for Foo.has_many :items
@@ -50,99 +44,60 @@ module ActiveRecord
50
44
  # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
51
45
  def ids_reader
52
46
  if loaded?
53
- load_target.map do |record|
54
- record.send(reflection.association_primary_key)
55
- end
47
+ target.pluck(reflection.association_primary_key)
48
+ elsif !target.empty?
49
+ load_target.pluck(reflection.association_primary_key)
56
50
  else
57
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
58
- scope.pluck(column)
51
+ @association_ids ||= scope.pluck(reflection.association_primary_key)
59
52
  end
60
53
  end
61
54
 
62
55
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
63
56
  def ids_writer(ids)
64
- pk_column = reflection.association_primary_key
65
- pk_type = klass.type_for_attribute(pk_column)
66
- ids = Array(ids).reject(&:blank?).map do |i|
67
- pk_type.type_cast_from_user(i)
68
- end
69
-
70
- if (objs = klass.where(pk_column => ids)).size == ids.size
71
- replace(objs.index_by { |r| r.send(pk_column) }.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)
72
70
  else
73
- objs.raise_record_not_found_exception!(ids, objs.size, ids.size)
71
+ replace(records)
74
72
  end
75
73
  end
76
74
 
77
75
  def reset
78
76
  super
79
77
  @target = []
80
- end
81
-
82
- def select(*fields)
83
- if block_given?
84
- load_target.select.each { |e| yield e }
85
- else
86
- scope.select(*fields)
87
- end
78
+ @association_ids = nil
88
79
  end
89
80
 
90
81
  def find(*args)
91
- if block_given?
92
- load_target.find(*args) { |*block_args| yield(*block_args) }
93
- else
94
- if options[:inverse_of] && loaded?
95
- args_flatten = args.flatten
96
- raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
97
- result = find_by_scan(*args)
98
-
99
- result_size = Array(result).size
100
- if !result || result_size != args_flatten.size
101
- scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
102
- else
103
- result
104
- end
105
- else
106
- scope.find(*args)
107
- end
108
- end
109
- end
110
-
111
- def first(*args)
112
- first_nth_or_last(:first, *args)
113
- end
114
-
115
- def second(*args)
116
- first_nth_or_last(:second, *args)
117
- end
118
-
119
- def third(*args)
120
- first_nth_or_last(:third, *args)
121
- end
122
-
123
- def fourth(*args)
124
- first_nth_or_last(:fourth, *args)
125
- end
126
-
127
- def fifth(*args)
128
- first_nth_or_last(:fifth, *args)
129
- end
82
+ if options[:inverse_of] && loaded?
83
+ args_flatten = args.flatten
84
+ model = scope.klass
130
85
 
131
- def forty_two(*args)
132
- first_nth_or_last(:forty_two, *args)
133
- end
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)
89
+ end
134
90
 
135
- def last(*args)
136
- first_nth_or_last(:last, *args)
137
- end
91
+ result = find_by_scan(*args)
138
92
 
139
- def take(n = nil)
140
- if loaded?
141
- n ? target.take(n) : target.first
142
- else
143
- scope.take(n).tap do |record|
144
- set_inverse_instance record if record.is_a? ActiveRecord::Base
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
145
98
  end
99
+ else
100
+ scope.find(*args)
146
101
  end
147
102
  end
148
103
 
@@ -150,24 +105,14 @@ module ActiveRecord
150
105
  if attributes.is_a?(Array)
151
106
  attributes.collect { |attr| build(attr, &block) }
152
107
  else
153
- add_to_target(build_record(attributes)) do |record|
154
- yield(record) if block_given?
155
- end
108
+ add_to_target(build_record(attributes, &block))
156
109
  end
157
110
  end
158
111
 
159
- def create(attributes = {}, &block)
160
- _create_record(attributes, &block)
161
- end
162
-
163
- def create!(attributes = {}, &block)
164
- _create_record(attributes, true, &block)
165
- end
166
-
167
- # Add +records+ to this association. Returns +self+ so method calls may
168
- # be chained. Since << flattens its argument list and inserts each record,
169
- # +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.
170
114
  def concat(*records)
115
+ records = records.flatten
171
116
  if owner.new_record?
172
117
  load_target
173
118
  concat_records(records)
@@ -210,12 +155,12 @@ module ActiveRecord
210
155
  end
211
156
 
212
157
  dependent = if dependent
213
- dependent
214
- elsif options[:dependent] == :destroy
215
- :delete_all
216
- else
217
- options[:dependent]
218
- end
158
+ dependent
159
+ elsif options[:dependent] == :destroy
160
+ :delete_all
161
+ else
162
+ options[:dependent]
163
+ end
219
164
 
220
165
  delete_or_nullify_all_records(dependent).tap do
221
166
  reset
@@ -233,32 +178,6 @@ module ActiveRecord
233
178
  end
234
179
  end
235
180
 
236
- # Count all records using SQL. Construct options and pass them with
237
- # scope to the target class's +count+.
238
- def count(column_name = nil, count_options = {})
239
- # TODO: Remove count_options argument as soon we remove support to
240
- # activerecord-deprecated_finders.
241
- column_name, count_options = nil, column_name if column_name.is_a?(Hash)
242
-
243
- relation = scope
244
- if association_scope.distinct_value
245
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
246
- column_name ||= reflection.klass.primary_key
247
- relation = relation.distinct
248
- end
249
-
250
- value = relation.count(column_name)
251
-
252
- limit = options[:limit]
253
- offset = options[:offset]
254
-
255
- if limit || offset
256
- [ [value - offset.to_i, 0].max, limit.to_i ].min
257
- else
258
- value
259
- end
260
- end
261
-
262
181
  # Removes +records+ from this association calling +before_remove+ and
263
182
  # +after_remove+ callbacks.
264
183
  #
@@ -267,12 +186,7 @@ module ActiveRecord
267
186
  # are actually removed from the database, that depends precisely on
268
187
  # +delete_records+. They are in any case removed from the collection.
269
188
  def delete(*records)
270
- return if records.empty?
271
- _options = records.extract_options!
272
- dependent = _options[:dependent] || options[:dependent]
273
-
274
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
275
- delete_or_destroy(records, dependent)
189
+ delete_or_destroy(records, options[:dependent])
276
190
  end
277
191
 
278
192
  # Deletes the +records+ and removes them from this association calling
@@ -281,8 +195,6 @@ module ActiveRecord
281
195
  # Note that this method removes records from the database ignoring the
282
196
  # +:dependent+ option.
283
197
  def destroy(*records)
284
- return if records.empty?
285
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
286
198
  delete_or_destroy(records, :destroy)
287
199
  end
288
200
 
@@ -298,30 +210,19 @@ module ActiveRecord
298
210
  # +count_records+, which is a method descendants have to provide.
299
211
  def size
300
212
  if !find_target? || loaded?
301
- if association_scope.distinct_value
302
- target.uniq.size
303
- else
304
- target.size
305
- end
306
- elsif !loaded? && !association_scope.group_values.empty?
213
+ target.size
214
+ elsif @association_ids
215
+ @association_ids.size
216
+ elsif !association_scope.group_values.empty?
307
217
  load_target.size
308
- elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
309
- unsaved_records = target.select { |r| r.new_record? }
218
+ elsif !association_scope.distinct_value && !target.empty?
219
+ unsaved_records = target.select(&:new_record?)
310
220
  unsaved_records.size + count_records
311
221
  else
312
222
  count_records
313
223
  end
314
224
  end
315
225
 
316
- # Returns the size of the collection calling +size+ on the target.
317
- #
318
- # If the collection has been already loaded +length+ and +size+ are
319
- # equivalent. If not and you are going to need the records anyway this
320
- # method will take one less query. Otherwise +size+ is more efficient.
321
- def length
322
- load_target.size
323
- end
324
-
325
226
  # Returns true if the collection is empty.
326
227
  #
327
228
  # If the collection has been loaded
@@ -331,41 +232,13 @@ module ActiveRecord
331
232
  # loaded and you are going to fetch the records anyway it is better to
332
233
  # check <tt>collection.length.zero?</tt>.
333
234
  def empty?
334
- if loaded?
235
+ if loaded? || @association_ids || reflection.has_cached_counter?
335
236
  size.zero?
336
237
  else
337
- @target.blank? && !scope.exists?
338
- end
339
- end
340
-
341
- # Returns true if the collections is not empty.
342
- # Equivalent to +!collection.empty?+.
343
- def any?
344
- if block_given?
345
- load_target.any? { |*block_args| yield(*block_args) }
346
- else
347
- !empty?
238
+ target.empty? && !scope.exists?
348
239
  end
349
240
  end
350
241
 
351
- # Returns true if the collection has more than 1 record.
352
- # Equivalent to +collection.size > 1+.
353
- def many?
354
- if block_given?
355
- load_target.many? { |*block_args| yield(*block_args) }
356
- else
357
- size > 1
358
- end
359
- end
360
-
361
- def distinct
362
- seen = {}
363
- load_target.find_all do |record|
364
- seen[record.id] = true unless seen.key?(record.id)
365
- end
366
- end
367
- alias uniq distinct
368
-
369
242
  # Replace this collection with +other_array+. This will perform a diff
370
243
  # and delete/add only records that have changed.
371
244
  def replace(other_array)
@@ -378,6 +251,8 @@ module ActiveRecord
378
251
  replace_common_records_in_memory(other_array, original_target)
379
252
  if other_array != original_target
380
253
  transaction { replace_records(other_array, original_target) }
254
+ else
255
+ other_array
381
256
  end
382
257
  end
383
258
  end
@@ -410,25 +285,9 @@ module ActiveRecord
410
285
  replace_on_target(record, index, skip_callbacks, &block)
411
286
  end
412
287
 
413
- def replace_on_target(record, index, skip_callbacks)
414
- callback(:before_add, record) unless skip_callbacks
415
- yield(record) if block_given?
416
-
417
- if index
418
- @target[index] = record
419
- else
420
- @target << record
421
- end
422
-
423
- callback(:after_add, record) unless skip_callbacks
424
- set_inverse_instance(record)
425
-
426
- record
427
- end
428
-
429
- def scope(opts = {})
430
- scope = super()
431
- scope.none! if opts.fetch(:nullify, true) && null_scope?
288
+ def scope
289
+ scope = super
290
+ scope.none! if null_scope?
432
291
  scope
433
292
  end
434
293
 
@@ -436,28 +295,13 @@ module ActiveRecord
436
295
  owner.new_record? && !foreign_key_present?
437
296
  end
438
297
 
439
- private
440
- def get_records
441
- return scope.to_a if skip_statement_cache?
442
-
443
- conn = klass.connection
444
- sc = reflection.association_scope_cache(conn, owner) do
445
- StatementCache.create(conn) { |params|
446
- as = AssociationScope.create { params.bind }
447
- target_scope.merge as.scope(self, conn)
448
- }
449
- end
450
-
451
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
452
- sc.execute binds, klass, klass.connection
298
+ def find_from_target?
299
+ loaded? ||
300
+ owner.new_record? ||
301
+ target.any? { |record| record.new_record? || record.changed? }
453
302
  end
454
303
 
455
- def find_target
456
- records = get_records
457
- records.each { |record| set_inverse_instance(record) }
458
- records
459
- end
460
-
304
+ private
461
305
  # We have some records loaded from the database (persisted) and some that are
462
306
  # in-memory (memory). The same record may be represented in the persisted array
463
307
  # and in the memory array.
@@ -475,7 +319,7 @@ module ActiveRecord
475
319
  persisted.map! do |record|
476
320
  if mem_record = memory.delete(record)
477
321
 
478
- ((record.attribute_names & mem_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|
479
323
  mem_record[name] = record[name]
480
324
  end
481
325
 
@@ -496,28 +340,35 @@ module ActiveRecord
496
340
  if attributes.is_a?(Array)
497
341
  attributes.collect { |attr| _create_record(attr, raise, &block) }
498
342
  else
343
+ record = build_record(attributes, &block)
499
344
  transaction do
500
- add_to_target(build_record(attributes)) do |record|
501
- yield(record) if block_given?
502
- 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
+ }
503
350
  end
351
+ raise ActiveRecord::Rollback unless result
504
352
  end
353
+ record
505
354
  end
506
355
  end
507
356
 
508
357
  # Do the relevant stuff to insert the given record into the association collection.
509
- def insert_record(record, validate = true, raise = false)
510
- raise NotImplementedError
511
- end
512
-
513
- def create_scope
514
- scope.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
515
364
  end
516
365
 
517
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) }
518
369
  records = records.flatten
519
370
  records.each { |record| raise_on_type_mismatch!(record) }
520
- existing_records = records.reject { |r| r.new_record? }
371
+ existing_records = records.reject(&:new_record?)
521
372
 
522
373
  if existing_records.empty?
523
374
  remove_records(existing_records, records, method)
@@ -530,21 +381,23 @@ module ActiveRecord
530
381
  records.each { |record| callback(:before_remove, record) }
531
382
 
532
383
  delete_records(existing_records, method) if existing_records.any?
533
- records.each { |record| target.delete(record) }
384
+ @target -= records
385
+ @association_ids = nil
534
386
 
535
387
  records.each { |record| callback(:after_remove, record) }
536
388
  end
537
389
 
538
- # Delete the given records from the association, using one of the methods :destroy,
539
- # :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).
540
393
  def delete_records(records, method)
541
394
  raise NotImplementedError
542
395
  end
543
396
 
544
397
  def replace_records(new_target, original_target)
545
- delete(target - new_target)
398
+ delete(difference(target, new_target))
546
399
 
547
- unless concat(new_target - target)
400
+ unless concat(difference(new_target, target))
548
401
  @target = original_target
549
402
  raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
550
403
  "new records could not be saved."
@@ -554,24 +407,53 @@ module ActiveRecord
554
407
  end
555
408
 
556
409
  def replace_common_records_in_memory(new_target, original_target)
557
- common_records = new_target & original_target
410
+ common_records = intersection(new_target, original_target)
558
411
  common_records.each do |record|
559
412
  skip_callbacks = true
560
413
  replace_on_target(record, @target.index(record), skip_callbacks)
561
414
  end
562
415
  end
563
416
 
564
- def concat_records(records, should_raise = false)
417
+ def concat_records(records, raise = false)
565
418
  result = true
566
419
 
567
- records.flatten.each do |record|
420
+ records.each do |record|
568
421
  raise_on_type_mismatch!(record)
569
- add_to_target(record) do |rec|
570
- result &&= insert_record(rec, true, should_raise) unless owner.new_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
571
428
  end
572
429
  end
573
430
 
574
- 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
575
457
  end
576
458
 
577
459
  def callback(method, record)
@@ -585,36 +467,12 @@ module ActiveRecord
585
467
  owner.class.send(full_callback_name)
586
468
  end
587
469
 
588
- # Should we deal with assoc.first or assoc.last by issuing an independent query to
589
- # the database, or by getting the target, and then taking the first/last item from that?
590
- #
591
- # If the args is just a non-empty options hash, go to the database.
592
- #
593
- # Otherwise, go to the database only if none of the following are true:
594
- # * target already loaded
595
- # * owner is new record
596
- # * target contains new or changed record(s)
597
- def fetch_first_nth_or_last_using_find?(args)
598
- if args.first.is_a?(Hash)
599
- true
600
- else
601
- !(loaded? ||
602
- owner.new_record? ||
603
- target.any? { |record| record.new_record? || record.changed? })
604
- end
605
- end
606
-
607
470
  def include_in_memory?(record)
608
471
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
609
472
  assoc = owner.association(reflection.through_reflection.name)
610
473
  assoc.reader.any? { |source|
611
- target_association = source.send(reflection.source_reflection.name)
612
-
613
- if target_association.respond_to?(:include?)
614
- target_association.include?(record)
615
- else
616
- target_association == record
617
- end
474
+ target_reflection = source.send(reflection.source_reflection.name)
475
+ target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
618
476
  } || target.include?(record)
619
477
  else
620
478
  target.include?(record)
@@ -625,7 +483,7 @@ module ActiveRecord
625
483
  # specified, then #find scans the entire collection.
626
484
  def find_by_scan(*args)
627
485
  expects_array = args.first.kind_of?(Array)
628
- ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq
486
+ ids = args.flatten.compact.map(&:to_s).uniq
629
487
 
630
488
  if ids.size == 1
631
489
  id = ids.first
@@ -635,16 +493,6 @@ module ActiveRecord
635
493
  load_target.select { |r| ids.include?(r.id.to_s) }
636
494
  end
637
495
  end
638
-
639
- # Fetches the first/last using SQL if possible, otherwise from the target array.
640
- def first_nth_or_last(type, *args)
641
- args.shift if args.first.is_a?(Hash) && args.first.empty?
642
-
643
- collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
644
- collection.send(type, *args).tap do |record|
645
- set_inverse_instance record if record.is_a? ActiveRecord::Base
646
- end
647
- end
648
496
  end
649
497
  end
650
498
  end