activerecord 4.2.11.2 → 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 +4 -4
  2. data/CHANGELOG.md +613 -1638
  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 +131 -287
  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 -86
  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 -243
  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 -635
  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 -180
  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 +6 -4
  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 +621 -303
  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 +312 -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 +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +328 -185
  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 +307 -100
  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 -59
  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 -498
  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 -64
  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,103 +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
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) }
69
61
 
70
- objs = klass.where(pk_column => ids).index_by do |r|
71
- r.send(pk_column)
62
+ records = klass.where(primary_key => ids).index_by do |r|
63
+ r.public_send(primary_key)
72
64
  end.values_at(*ids).compact
73
65
 
74
- if objs.size == ids.size
75
- replace(objs.index_by { |r| r.send(pk_column) }.values_at(*ids))
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)
76
70
  else
77
- klass.all.raise_record_not_found_exception!(ids, objs.size, ids.size)
71
+ replace(records)
78
72
  end
79
73
  end
80
74
 
81
75
  def reset
82
76
  super
83
77
  @target = []
84
- end
85
-
86
- def select(*fields)
87
- if block_given?
88
- load_target.select.each { |e| yield e }
89
- else
90
- scope.select(*fields)
91
- end
78
+ @association_ids = nil
92
79
  end
93
80
 
94
81
  def find(*args)
95
- if block_given?
96
- load_target.find(*args) { |*block_args| yield(*block_args) }
97
- else
98
- if options[:inverse_of] && loaded?
99
- args_flatten = args.flatten
100
- raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
101
- result = find_by_scan(*args)
102
-
103
- result_size = Array(result).size
104
- if !result || result_size != args_flatten.size
105
- scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
106
- else
107
- result
108
- end
109
- else
110
- scope.find(*args)
111
- end
112
- end
113
- end
114
-
115
- def first(*args)
116
- first_nth_or_last(:first, *args)
117
- end
118
-
119
- def second(*args)
120
- first_nth_or_last(:second, *args)
121
- end
82
+ if options[:inverse_of] && loaded?
83
+ args_flatten = args.flatten
84
+ model = scope.klass
122
85
 
123
- def third(*args)
124
- first_nth_or_last(:third, *args)
125
- end
126
-
127
- def fourth(*args)
128
- first_nth_or_last(:fourth, *args)
129
- end
130
-
131
- def fifth(*args)
132
- first_nth_or_last(:fifth, *args)
133
- end
134
-
135
- def forty_two(*args)
136
- first_nth_or_last(:forty_two, *args)
137
- 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
138
90
 
139
- def last(*args)
140
- first_nth_or_last(:last, *args)
141
- end
91
+ result = find_by_scan(*args)
142
92
 
143
- def take(n = nil)
144
- if loaded?
145
- n ? target.take(n) : target.first
146
- else
147
- scope.take(n).tap do |record|
148
- 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
149
98
  end
99
+ else
100
+ scope.find(*args)
150
101
  end
151
102
  end
152
103
 
@@ -154,24 +105,14 @@ module ActiveRecord
154
105
  if attributes.is_a?(Array)
155
106
  attributes.collect { |attr| build(attr, &block) }
156
107
  else
157
- add_to_target(build_record(attributes)) do |record|
158
- yield(record) if block_given?
159
- end
108
+ add_to_target(build_record(attributes, &block))
160
109
  end
161
110
  end
162
111
 
163
- def create(attributes = {}, &block)
164
- _create_record(attributes, &block)
165
- end
166
-
167
- def create!(attributes = {}, &block)
168
- _create_record(attributes, true, &block)
169
- end
170
-
171
- # Add +records+ to this association. Returns +self+ so method calls may
172
- # be chained. Since << flattens its argument list and inserts each record,
173
- # +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.
174
114
  def concat(*records)
115
+ records = records.flatten
175
116
  if owner.new_record?
176
117
  load_target
177
118
  concat_records(records)
@@ -214,12 +155,12 @@ module ActiveRecord
214
155
  end
215
156
 
216
157
  dependent = if dependent
217
- dependent
218
- elsif options[:dependent] == :destroy
219
- :delete_all
220
- else
221
- options[:dependent]
222
- end
158
+ dependent
159
+ elsif options[:dependent] == :destroy
160
+ :delete_all
161
+ else
162
+ options[:dependent]
163
+ end
223
164
 
224
165
  delete_or_nullify_all_records(dependent).tap do
225
166
  reset
@@ -237,32 +178,6 @@ module ActiveRecord
237
178
  end
238
179
  end
239
180
 
240
- # Count all records using SQL. Construct options and pass them with
241
- # scope to the target class's +count+.
242
- def count(column_name = nil, count_options = {})
243
- # TODO: Remove count_options argument as soon we remove support to
244
- # activerecord-deprecated_finders.
245
- column_name, count_options = nil, column_name if column_name.is_a?(Hash)
246
-
247
- relation = scope
248
- if association_scope.distinct_value
249
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
250
- column_name ||= reflection.klass.primary_key
251
- relation = relation.distinct
252
- end
253
-
254
- value = relation.count(column_name)
255
-
256
- limit = options[:limit]
257
- offset = options[:offset]
258
-
259
- if limit || offset
260
- [ [value - offset.to_i, 0].max, limit.to_i ].min
261
- else
262
- value
263
- end
264
- end
265
-
266
181
  # Removes +records+ from this association calling +before_remove+ and
267
182
  # +after_remove+ callbacks.
268
183
  #
@@ -271,12 +186,7 @@ module ActiveRecord
271
186
  # are actually removed from the database, that depends precisely on
272
187
  # +delete_records+. They are in any case removed from the collection.
273
188
  def delete(*records)
274
- return if records.empty?
275
- _options = records.extract_options!
276
- dependent = _options[:dependent] || options[:dependent]
277
-
278
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
279
- delete_or_destroy(records, dependent)
189
+ delete_or_destroy(records, options[:dependent])
280
190
  end
281
191
 
282
192
  # Deletes the +records+ and removes them from this association calling
@@ -285,8 +195,6 @@ module ActiveRecord
285
195
  # Note that this method removes records from the database ignoring the
286
196
  # +:dependent+ option.
287
197
  def destroy(*records)
288
- return if records.empty?
289
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
290
198
  delete_or_destroy(records, :destroy)
291
199
  end
292
200
 
@@ -302,30 +210,19 @@ module ActiveRecord
302
210
  # +count_records+, which is a method descendants have to provide.
303
211
  def size
304
212
  if !find_target? || loaded?
305
- if association_scope.distinct_value
306
- target.uniq.size
307
- else
308
- target.size
309
- end
310
- 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?
311
217
  load_target.size
312
- elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
313
- unsaved_records = target.select { |r| r.new_record? }
218
+ elsif !association_scope.distinct_value && !target.empty?
219
+ unsaved_records = target.select(&:new_record?)
314
220
  unsaved_records.size + count_records
315
221
  else
316
222
  count_records
317
223
  end
318
224
  end
319
225
 
320
- # Returns the size of the collection calling +size+ on the target.
321
- #
322
- # If the collection has been already loaded +length+ and +size+ are
323
- # equivalent. If not and you are going to need the records anyway this
324
- # method will take one less query. Otherwise +size+ is more efficient.
325
- def length
326
- load_target.size
327
- end
328
-
329
226
  # Returns true if the collection is empty.
330
227
  #
331
228
  # If the collection has been loaded
@@ -335,41 +232,13 @@ module ActiveRecord
335
232
  # loaded and you are going to fetch the records anyway it is better to
336
233
  # check <tt>collection.length.zero?</tt>.
337
234
  def empty?
338
- if loaded?
235
+ if loaded? || @association_ids || reflection.has_cached_counter?
339
236
  size.zero?
340
237
  else
341
- @target.blank? && !scope.exists?
342
- end
343
- end
344
-
345
- # Returns true if the collections is not empty.
346
- # Equivalent to +!collection.empty?+.
347
- def any?
348
- if block_given?
349
- load_target.any? { |*block_args| yield(*block_args) }
350
- else
351
- !empty?
238
+ target.empty? && !scope.exists?
352
239
  end
353
240
  end
354
241
 
355
- # Returns true if the collection has more than 1 record.
356
- # Equivalent to +collection.size > 1+.
357
- def many?
358
- if block_given?
359
- load_target.many? { |*block_args| yield(*block_args) }
360
- else
361
- size > 1
362
- end
363
- end
364
-
365
- def distinct
366
- seen = {}
367
- load_target.find_all do |record|
368
- seen[record.id] = true unless seen.key?(record.id)
369
- end
370
- end
371
- alias uniq distinct
372
-
373
242
  # Replace this collection with +other_array+. This will perform a diff
374
243
  # and delete/add only records that have changed.
375
244
  def replace(other_array)
@@ -382,6 +251,8 @@ module ActiveRecord
382
251
  replace_common_records_in_memory(other_array, original_target)
383
252
  if other_array != original_target
384
253
  transaction { replace_records(other_array, original_target) }
254
+ else
255
+ other_array
385
256
  end
386
257
  end
387
258
  end
@@ -414,25 +285,9 @@ module ActiveRecord
414
285
  replace_on_target(record, index, skip_callbacks, &block)
415
286
  end
416
287
 
417
- def replace_on_target(record, index, skip_callbacks)
418
- callback(:before_add, record) unless skip_callbacks
419
- yield(record) if block_given?
420
-
421
- if index
422
- @target[index] = record
423
- else
424
- @target << record
425
- end
426
-
427
- callback(:after_add, record) unless skip_callbacks
428
- set_inverse_instance(record)
429
-
430
- record
431
- end
432
-
433
- def scope(opts = {})
434
- scope = super()
435
- scope.none! if opts.fetch(:nullify, true) && null_scope?
288
+ def scope
289
+ scope = super
290
+ scope.none! if null_scope?
436
291
  scope
437
292
  end
438
293
 
@@ -440,28 +295,13 @@ module ActiveRecord
440
295
  owner.new_record? && !foreign_key_present?
441
296
  end
442
297
 
443
- private
444
- def get_records
445
- return scope.to_a if skip_statement_cache?
446
-
447
- conn = klass.connection
448
- sc = reflection.association_scope_cache(conn, owner) do
449
- StatementCache.create(conn) { |params|
450
- as = AssociationScope.create { params.bind }
451
- target_scope.merge as.scope(self, conn)
452
- }
453
- end
454
-
455
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
456
- 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? }
457
302
  end
458
303
 
459
- def find_target
460
- records = get_records
461
- records.each { |record| set_inverse_instance(record) }
462
- records
463
- end
464
-
304
+ private
465
305
  # We have some records loaded from the database (persisted) and some that are
466
306
  # in-memory (memory). The same record may be represented in the persisted array
467
307
  # and in the memory array.
@@ -479,7 +319,7 @@ module ActiveRecord
479
319
  persisted.map! do |record|
480
320
  if mem_record = memory.delete(record)
481
321
 
482
- ((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|
483
323
  mem_record[name] = record[name]
484
324
  end
485
325
 
@@ -500,28 +340,35 @@ module ActiveRecord
500
340
  if attributes.is_a?(Array)
501
341
  attributes.collect { |attr| _create_record(attr, raise, &block) }
502
342
  else
343
+ record = build_record(attributes, &block)
503
344
  transaction do
504
- add_to_target(build_record(attributes)) do |record|
505
- yield(record) if block_given?
506
- 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
+ }
507
350
  end
351
+ raise ActiveRecord::Rollback unless result
508
352
  end
353
+ record
509
354
  end
510
355
  end
511
356
 
512
357
  # Do the relevant stuff to insert the given record into the association collection.
513
- def insert_record(record, validate = true, raise = false)
514
- raise NotImplementedError
515
- end
516
-
517
- def create_scope
518
- 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
519
364
  end
520
365
 
521
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) }
522
369
  records = records.flatten
523
370
  records.each { |record| raise_on_type_mismatch!(record) }
524
- existing_records = records.reject { |r| r.new_record? }
371
+ existing_records = records.reject(&:new_record?)
525
372
 
526
373
  if existing_records.empty?
527
374
  remove_records(existing_records, records, method)
@@ -534,21 +381,23 @@ module ActiveRecord
534
381
  records.each { |record| callback(:before_remove, record) }
535
382
 
536
383
  delete_records(existing_records, method) if existing_records.any?
537
- records.each { |record| target.delete(record) }
384
+ @target -= records
385
+ @association_ids = nil
538
386
 
539
387
  records.each { |record| callback(:after_remove, record) }
540
388
  end
541
389
 
542
- # Delete the given records from the association, using one of the methods :destroy,
543
- # :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).
544
393
  def delete_records(records, method)
545
394
  raise NotImplementedError
546
395
  end
547
396
 
548
397
  def replace_records(new_target, original_target)
549
- delete(target - new_target)
398
+ delete(difference(target, new_target))
550
399
 
551
- unless concat(new_target - target)
400
+ unless concat(difference(new_target, target))
552
401
  @target = original_target
553
402
  raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
554
403
  "new records could not be saved."
@@ -558,24 +407,53 @@ module ActiveRecord
558
407
  end
559
408
 
560
409
  def replace_common_records_in_memory(new_target, original_target)
561
- common_records = new_target & original_target
410
+ common_records = intersection(new_target, original_target)
562
411
  common_records.each do |record|
563
412
  skip_callbacks = true
564
413
  replace_on_target(record, @target.index(record), skip_callbacks)
565
414
  end
566
415
  end
567
416
 
568
- def concat_records(records, should_raise = false)
417
+ def concat_records(records, raise = false)
569
418
  result = true
570
419
 
571
- records.flatten.each do |record|
420
+ records.each do |record|
572
421
  raise_on_type_mismatch!(record)
573
- add_to_target(record) do |rec|
574
- 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
575
428
  end
576
429
  end
577
430
 
578
- 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
579
457
  end
580
458
 
581
459
  def callback(method, record)
@@ -589,36 +467,12 @@ module ActiveRecord
589
467
  owner.class.send(full_callback_name)
590
468
  end
591
469
 
592
- # Should we deal with assoc.first or assoc.last by issuing an independent query to
593
- # the database, or by getting the target, and then taking the first/last item from that?
594
- #
595
- # If the args is just a non-empty options hash, go to the database.
596
- #
597
- # Otherwise, go to the database only if none of the following are true:
598
- # * target already loaded
599
- # * owner is new record
600
- # * target contains new or changed record(s)
601
- def fetch_first_nth_or_last_using_find?(args)
602
- if args.first.is_a?(Hash)
603
- true
604
- else
605
- !(loaded? ||
606
- owner.new_record? ||
607
- target.any? { |record| record.new_record? || record.changed? })
608
- end
609
- end
610
-
611
470
  def include_in_memory?(record)
612
471
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
613
472
  assoc = owner.association(reflection.through_reflection.name)
614
473
  assoc.reader.any? { |source|
615
- target_association = source.send(reflection.source_reflection.name)
616
-
617
- if target_association.respond_to?(:include?)
618
- target_association.include?(record)
619
- else
620
- target_association == record
621
- end
474
+ target_reflection = source.send(reflection.source_reflection.name)
475
+ target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
622
476
  } || target.include?(record)
623
477
  else
624
478
  target.include?(record)
@@ -629,7 +483,7 @@ module ActiveRecord
629
483
  # specified, then #find scans the entire collection.
630
484
  def find_by_scan(*args)
631
485
  expects_array = args.first.kind_of?(Array)
632
- ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq
486
+ ids = args.flatten.compact.map(&:to_s).uniq
633
487
 
634
488
  if ids.size == 1
635
489
  id = ids.first
@@ -639,16 +493,6 @@ module ActiveRecord
639
493
  load_target.select { |r| ids.include?(r.id.to_s) }
640
494
  end
641
495
  end
642
-
643
- # Fetches the first/last using SQL if possible, otherwise from the target array.
644
- def first_nth_or_last(type, *args)
645
- args.shift if args.first.is_a?(Hash) && args.first.empty?
646
-
647
- collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
648
- collection.send(type, *args).tap do |record|
649
- set_inverse_instance record if record.is_a? ActiveRecord::Base
650
- end
651
- end
652
496
  end
653
497
  end
654
498
  end