activerecord 4.2.9 → 6.1.4.1

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 (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +964 -1382
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +266 -251
  8. data/lib/active_record/association_relation.rb +40 -15
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +162 -69
  11. data/lib/active_record/associations/association_scope.rb +105 -130
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -65
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -37
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +148 -287
  22. data/lib/active_record/associations/collection_proxy.rb +252 -150
  23. data/lib/active_record/associations/foreign_association.rb +23 -1
  24. data/lib/active_record/associations/has_many_association.rb +56 -98
  25. data/lib/active_record/associations/has_many_through_association.rb +68 -89
  26. data/lib/active_record/associations/has_one_association.rb +73 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  31. data/lib/active_record/associations/join_dependency.rb +174 -169
  32. data/lib/active_record/associations/preloader/association.rb +108 -115
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +97 -94
  35. data/lib/active_record/associations/singular_association.rb +18 -39
  36. data/lib/active_record/associations/through_association.rb +39 -19
  37. data/lib/active_record/associations.rb +1845 -1598
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -148
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
  46. data/lib/active_record/attribute_methods/write.rb +24 -55
  47. data/lib/active_record/attribute_methods.rb +149 -154
  48. data/lib/active_record/attributes.rb +234 -78
  49. data/lib/active_record/autosave_association.rb +133 -60
  50. data/lib/active_record/base.rb +46 -46
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +34 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -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 +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
  119. data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +458 -241
  133. data/lib/active_record/counter_cache.rb +70 -49
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +272 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +211 -92
  143. data/lib/active_record/errors.rb +224 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +10 -5
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +275 -500
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +62 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +27 -5
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +98 -92
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +295 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +673 -325
  170. data/lib/active_record/model_schema.rb +418 -113
  171. data/lib/active_record/nested_attributes.rb +263 -224
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +572 -136
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +170 -51
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +523 -199
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +454 -291
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +324 -249
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +316 -242
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +95 -103
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +136 -122
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -413
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +18 -20
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -343
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -23
  209. data/lib/active_record/scoping/default.rb +96 -83
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +128 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +364 -130
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +287 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +182 -163
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -45
  231. data/lib/active_record/type/date_time.rb +4 -49
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +27 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +63 -56
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +42 -29
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +172 -65
  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_decorators.rb +0 -66
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  353. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  354. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  355. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  356. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  357. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  358. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  359. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  360. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  361. data/lib/active_record/type/big_integer.rb +0 -13
  362. data/lib/active_record/type/binary.rb +0 -50
  363. data/lib/active_record/type/boolean.rb +0 -31
  364. data/lib/active_record/type/decimal.rb +0 -64
  365. data/lib/active_record/type/decorator.rb +0 -14
  366. data/lib/active_record/type/float.rb +0 -19
  367. data/lib/active_record/type/integer.rb +0 -59
  368. data/lib/active_record/type/mutable.rb +0 -16
  369. data/lib/active_record/type/numeric.rb +0 -36
  370. data/lib/active_record/type/string.rb +0 -40
  371. data/lib/active_record/type/time_value.rb +0 -38
  372. data/lib/active_record/type/value.rb +0 -110
  373. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  374. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/insert_all"
4
+
1
5
  module ActiveRecord
2
- # = Active Record Persistence
6
+ # = Active Record \Persistence
3
7
  module Persistence
4
8
  extend ActiveSupport::Concern
5
9
 
@@ -53,6 +57,192 @@ module ActiveRecord
53
57
  end
54
58
  end
55
59
 
60
+ # Inserts a single record into the database in a single SQL INSERT
61
+ # statement. It does not instantiate any models nor does it trigger
62
+ # Active Record callbacks or validations. Though passed values
63
+ # go through Active Record's type casting and serialization.
64
+ #
65
+ # See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
66
+ def insert(attributes, returning: nil, unique_by: nil)
67
+ insert_all([ attributes ], returning: returning, unique_by: unique_by)
68
+ end
69
+
70
+ # Inserts multiple records into the database in a single SQL INSERT
71
+ # statement. It does not instantiate any models nor does it trigger
72
+ # Active Record callbacks or validations. Though passed values
73
+ # go through Active Record's type casting and serialization.
74
+ #
75
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
76
+ # the attributes for a single row and must have the same keys.
77
+ #
78
+ # Rows are considered to be unique by every unique index on the table. Any
79
+ # duplicate rows are skipped.
80
+ # Override with <tt>:unique_by</tt> (see below).
81
+ #
82
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
83
+ # <tt>:returning</tt> (see below).
84
+ #
85
+ # ==== Options
86
+ #
87
+ # [:returning]
88
+ # (PostgreSQL only) An array of attributes to return for all successfully
89
+ # inserted records, which by default is the primary key.
90
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
91
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
92
+ # clause entirely.
93
+ #
94
+ # [:unique_by]
95
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
96
+ # by every unique index on the table. Any duplicate rows are skipped.
97
+ #
98
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
99
+ #
100
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
101
+ # row has an existing id, or is not unique by another unique index,
102
+ # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
103
+ #
104
+ # Unique indexes can be identified by columns or name:
105
+ #
106
+ # unique_by: :isbn
107
+ # unique_by: %i[ author_id name ]
108
+ # unique_by: :index_books_on_isbn
109
+ #
110
+ # Because it relies on the index information from the database
111
+ # <tt>:unique_by</tt> is recommended to be paired with
112
+ # Active Record's schema_cache.
113
+ #
114
+ # ==== Example
115
+ #
116
+ # # Insert records and skip inserting any duplicates.
117
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
118
+ #
119
+ # Book.insert_all([
120
+ # { id: 1, title: "Rework", author: "David" },
121
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
122
+ # ])
123
+ def insert_all(attributes, returning: nil, unique_by: nil)
124
+ InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
125
+ end
126
+
127
+ # Inserts a single record into the database in a single SQL INSERT
128
+ # statement. It does not instantiate any models nor does it trigger
129
+ # Active Record callbacks or validations. Though passed values
130
+ # go through Active Record's type casting and serialization.
131
+ #
132
+ # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
133
+ def insert!(attributes, returning: nil)
134
+ insert_all!([ attributes ], returning: returning)
135
+ end
136
+
137
+ # Inserts multiple records into the database in a single SQL INSERT
138
+ # statement. It does not instantiate any models nor does it trigger
139
+ # Active Record callbacks or validations. Though passed values
140
+ # go through Active Record's type casting and serialization.
141
+ #
142
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
143
+ # the attributes for a single row and must have the same keys.
144
+ #
145
+ # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
146
+ # unique index on the table. In that case, no rows are inserted.
147
+ #
148
+ # To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
149
+ # To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
150
+ #
151
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
152
+ # <tt>:returning</tt> (see below).
153
+ #
154
+ # ==== Options
155
+ #
156
+ # [:returning]
157
+ # (PostgreSQL only) An array of attributes to return for all successfully
158
+ # inserted records, which by default is the primary key.
159
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
160
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
161
+ # clause entirely.
162
+ #
163
+ # ==== Examples
164
+ #
165
+ # # Insert multiple records
166
+ # Book.insert_all!([
167
+ # { title: "Rework", author: "David" },
168
+ # { title: "Eloquent Ruby", author: "Russ" }
169
+ # ])
170
+ #
171
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
172
+ # # does not have a unique id.
173
+ # Book.insert_all!([
174
+ # { id: 1, title: "Rework", author: "David" },
175
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
176
+ # ])
177
+ def insert_all!(attributes, returning: nil)
178
+ InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
179
+ end
180
+
181
+ # Updates or inserts (upserts) a single record into the database in a
182
+ # single SQL INSERT statement. It does not instantiate any models nor does
183
+ # it trigger Active Record callbacks or validations. Though passed values
184
+ # go through Active Record's type casting and serialization.
185
+ #
186
+ # See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
187
+ def upsert(attributes, returning: nil, unique_by: nil)
188
+ upsert_all([ attributes ], returning: returning, unique_by: unique_by)
189
+ end
190
+
191
+ # Updates or inserts (upserts) multiple records into the database in a
192
+ # single SQL INSERT statement. It does not instantiate any models nor does
193
+ # it trigger Active Record callbacks or validations. Though passed values
194
+ # go through Active Record's type casting and serialization.
195
+ #
196
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
197
+ # the attributes for a single row and must have the same keys.
198
+ #
199
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
200
+ # <tt>:returning</tt> (see below).
201
+ #
202
+ # ==== Options
203
+ #
204
+ # [:returning]
205
+ # (PostgreSQL only) An array of attributes to return for all successfully
206
+ # inserted records, which by default is the primary key.
207
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
208
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
209
+ # clause entirely.
210
+ #
211
+ # [:unique_by]
212
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
213
+ # by every unique index on the table. Any duplicate rows are skipped.
214
+ #
215
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
216
+ #
217
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
218
+ # row has an existing id, or is not unique by another unique index,
219
+ # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
220
+ #
221
+ # Unique indexes can be identified by columns or name:
222
+ #
223
+ # unique_by: :isbn
224
+ # unique_by: %i[ author_id name ]
225
+ # unique_by: :index_books_on_isbn
226
+ #
227
+ # Because it relies on the index information from the database
228
+ # <tt>:unique_by</tt> is recommended to be paired with
229
+ # Active Record's schema_cache.
230
+ #
231
+ # ==== Examples
232
+ #
233
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
234
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
235
+ #
236
+ # Book.upsert_all([
237
+ # { title: "Rework", author: "David", isbn: "1" },
238
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
239
+ # ], unique_by: :isbn)
240
+ #
241
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
242
+ def upsert_all(attributes, returning: nil, unique_by: nil)
243
+ InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
244
+ end
245
+
56
246
  # Given an attributes hash, +instantiate+ returns a new instance of
57
247
  # the appropriate class. Accepts only keys as strings.
58
248
  #
@@ -61,15 +251,158 @@ module ActiveRecord
61
251
  # +instantiate+ instead of +new+, finder methods ensure they get new
62
252
  # instances of the appropriate class for each record.
63
253
  #
64
- # See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
254
+ # See <tt>ActiveRecord::Inheritance#discriminate_class_for_record</tt> to see
65
255
  # how this "single-table" inheritance mapping is implemented.
66
- def instantiate(attributes, column_types = {})
256
+ def instantiate(attributes, column_types = {}, &block)
67
257
  klass = discriminate_class_for_record(attributes)
68
- attributes = klass.attributes_builder.build_from_database(attributes, column_types)
69
- klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
258
+ instantiate_instance_of(klass, attributes, column_types, &block)
259
+ end
260
+
261
+ # Updates an object (or multiple objects) and saves it to the database, if validations pass.
262
+ # The resulting object is returned whether the object was saved successfully to the database or not.
263
+ #
264
+ # ==== Parameters
265
+ #
266
+ # * +id+ - This should be the id or an array of ids to be updated.
267
+ # * +attributes+ - This should be a hash of attributes or an array of hashes.
268
+ #
269
+ # ==== Examples
270
+ #
271
+ # # Updates one record
272
+ # Person.update(15, user_name: "Samuel", group: "expert")
273
+ #
274
+ # # Updates multiple records
275
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
276
+ # Person.update(people.keys, people.values)
277
+ #
278
+ # # Updates multiple records from the result of a relation
279
+ # people = Person.where(group: "expert")
280
+ # people.update(group: "masters")
281
+ #
282
+ # Note: Updating a large number of records will run an UPDATE
283
+ # query for each record, which may cause a performance issue.
284
+ # When running callbacks is not needed for each record update,
285
+ # it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
286
+ # for updating all records in a single query.
287
+ def update(id = :all, attributes)
288
+ if id.is_a?(Array)
289
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
290
+ object.update(attributes[idx])
291
+ }
292
+ elsif id == :all
293
+ all.each { |record| record.update(attributes) }
294
+ else
295
+ if ActiveRecord::Base === id
296
+ raise ArgumentError,
297
+ "You are passing an instance of ActiveRecord::Base to `update`. " \
298
+ "Please pass the id of the object by calling `.id`."
299
+ end
300
+ object = find(id)
301
+ object.update(attributes)
302
+ object
303
+ end
304
+ end
305
+
306
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
307
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
308
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
309
+ #
310
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
311
+ # from the attributes, and then calls destroy on it.
312
+ #
313
+ # ==== Parameters
314
+ #
315
+ # * +id+ - This should be the id or an array of ids to be destroyed.
316
+ #
317
+ # ==== Examples
318
+ #
319
+ # # Destroy a single object
320
+ # Todo.destroy(1)
321
+ #
322
+ # # Destroy multiple objects
323
+ # todos = [1,2,3]
324
+ # Todo.destroy(todos)
325
+ def destroy(id)
326
+ if id.is_a?(Array)
327
+ find(id).each(&:destroy)
328
+ else
329
+ find(id).destroy
330
+ end
331
+ end
332
+
333
+ # Deletes the row with a primary key matching the +id+ argument, using an
334
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
335
+ # Record objects are not instantiated, so the object's callbacks are not
336
+ # executed, including any <tt>:dependent</tt> association options.
337
+ #
338
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
339
+ #
340
+ # Note: Although it is often much faster than the alternative, #destroy,
341
+ # skipping callbacks might bypass business logic in your application
342
+ # that ensures referential integrity or performs other essential jobs.
343
+ #
344
+ # ==== Examples
345
+ #
346
+ # # Delete a single row
347
+ # Todo.delete(1)
348
+ #
349
+ # # Delete multiple rows
350
+ # Todo.delete([2,3,4])
351
+ def delete(id_or_array)
352
+ delete_by(primary_key => id_or_array)
353
+ end
354
+
355
+ def _insert_record(values) # :nodoc:
356
+ primary_key = self.primary_key
357
+ primary_key_value = nil
358
+
359
+ if primary_key && Hash === values
360
+ primary_key_value = values[primary_key]
361
+
362
+ if !primary_key_value && prefetch_primary_key?
363
+ primary_key_value = next_sequence_value
364
+ values[primary_key] = primary_key_value
365
+ end
366
+ end
367
+
368
+ if values.empty?
369
+ im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
370
+ im.into arel_table
371
+ else
372
+ im = arel_table.compile_insert(_substitute_values(values))
373
+ end
374
+
375
+ connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
376
+ end
377
+
378
+ def _update_record(values, constraints) # :nodoc:
379
+ constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
380
+
381
+ um = arel_table.where(
382
+ constraints.reduce(&:and)
383
+ ).compile_update(_substitute_values(values), primary_key)
384
+
385
+ connection.update(um, "#{self} Update")
386
+ end
387
+
388
+ def _delete_record(constraints) # :nodoc:
389
+ constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
390
+
391
+ dm = Arel::DeleteManager.new
392
+ dm.from(arel_table)
393
+ dm.wheres = constraints
394
+
395
+ connection.delete(dm, "#{self} Destroy")
70
396
  end
71
397
 
72
398
  private
399
+ # Given a class, an attributes hash, +instantiate_instance_of+ returns a
400
+ # new instance of the class. Accepts only keys as strings.
401
+ def instantiate_instance_of(klass, attributes, column_types = {}, &block)
402
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
403
+ klass.allocate.init_with_attributes(attributes, &block)
404
+ end
405
+
73
406
  # Called by +instantiate+ to decide which class to use for a new
74
407
  # record instance.
75
408
  #
@@ -78,68 +411,100 @@ module ActiveRecord
78
411
  def discriminate_class_for_record(record)
79
412
  self
80
413
  end
414
+
415
+ def _substitute_values(values)
416
+ values.map do |name, value|
417
+ attr = arel_table[name]
418
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
419
+ [attr, bind]
420
+ end
421
+ end
81
422
  end
82
423
 
83
424
  # Returns true if this object hasn't been saved yet -- that is, a record
84
425
  # for the object doesn't exist in the database yet; otherwise, returns false.
85
426
  def new_record?
86
- sync_with_transaction_state
87
427
  @new_record
88
428
  end
89
429
 
430
+ # Returns true if this object was just created -- that is, prior to the last
431
+ # save, the object didn't exist in the database and new_record? would have
432
+ # returned true.
433
+ def previously_new_record?
434
+ @previously_new_record
435
+ end
436
+
90
437
  # Returns true if this object has been destroyed, otherwise returns false.
91
438
  def destroyed?
92
- sync_with_transaction_state
93
439
  @destroyed
94
440
  end
95
441
 
96
442
  # Returns true if the record is persisted, i.e. it's not a new record and it was
97
443
  # not destroyed, otherwise returns false.
98
444
  def persisted?
99
- !(new_record? || destroyed?)
445
+ !(@new_record || @destroyed)
100
446
  end
101
447
 
448
+ ##
449
+ # :call-seq:
450
+ # save(**options)
451
+ #
102
452
  # Saves the model.
103
453
  #
104
- # If the model is new a record gets created in the database, otherwise
454
+ # If the model is new, a record gets created in the database, otherwise
105
455
  # the existing record gets updated.
106
456
  #
107
- # By default, save always run validations. If any of them fail the action
108
- # is cancelled and +save+ returns +false+. However, if you supply
109
- # validate: false, validations are bypassed altogether. See
457
+ # By default, save always runs validations. If any of them fail the action
458
+ # is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
459
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
110
460
  # ActiveRecord::Validations for more information.
111
461
  #
112
- # There's a series of callbacks associated with +save+. If any of the
113
- # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
114
- # +save+ returns +false+. See ActiveRecord::Callbacks for further
462
+ # By default, #save also sets the +updated_at+/+updated_on+ attributes to
463
+ # the current time. However, if you supply <tt>touch: false</tt>, these
464
+ # timestamps will not be updated.
465
+ #
466
+ # There's a series of callbacks associated with #save. If any of the
467
+ # <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled and
468
+ # #save returns +false+. See ActiveRecord::Callbacks for further
115
469
  # details.
116
470
  #
117
471
  # Attributes marked as readonly are silently ignored if the record is
118
472
  # being updated.
119
- def save(*)
120
- create_or_update
473
+ def save(**options, &block)
474
+ create_or_update(**options, &block)
121
475
  rescue ActiveRecord::RecordInvalid
122
476
  false
123
477
  end
124
478
 
479
+ ##
480
+ # :call-seq:
481
+ # save!(**options)
482
+ #
125
483
  # Saves the model.
126
484
  #
127
- # If the model is new a record gets created in the database, otherwise
485
+ # If the model is new, a record gets created in the database, otherwise
128
486
  # the existing record gets updated.
129
487
  #
130
- # With <tt>save!</tt> validations always run. If any of them fail
131
- # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
132
- # for more information.
488
+ # By default, #save! always runs validations. If any of them fail
489
+ # ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
490
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
491
+ # ActiveRecord::Validations for more information.
133
492
  #
134
- # There's a series of callbacks associated with <tt>save!</tt>. If any of
135
- # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
136
- # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
493
+ # By default, #save! also sets the +updated_at+/+updated_on+ attributes to
494
+ # the current time. However, if you supply <tt>touch: false</tt>, these
495
+ # timestamps will not be updated.
496
+ #
497
+ # There's a series of callbacks associated with #save!. If any of
498
+ # the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
499
+ # and #save! raises ActiveRecord::RecordNotSaved. See
137
500
  # ActiveRecord::Callbacks for further details.
138
501
  #
139
502
  # Attributes marked as readonly are silently ignored if the record is
140
503
  # being updated.
141
- def save!(*)
142
- create_or_update || raise(RecordNotSaved.new("Failed to save the record", self))
504
+ #
505
+ # Unless an error is raised, returns true.
506
+ def save!(**options, &block)
507
+ create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
143
508
  end
144
509
 
145
510
  # Deletes the record in the database and freezes this instance to
@@ -149,11 +514,13 @@ module ActiveRecord
149
514
  # The row is simply removed with an SQL +DELETE+ statement on the
150
515
  # record's primary key, and no callbacks are executed.
151
516
  #
517
+ # Note that this will also delete records marked as {#readonly?}[rdoc-ref:Core#readonly?].
518
+ #
152
519
  # To enforce the object's +before_destroy+ and +after_destroy+
153
520
  # callbacks or any <tt>:dependent</tt> association
154
- # options, use <tt>#destroy</tt>.
521
+ # options, use #destroy.
155
522
  def delete
156
- self.class.delete(id) if persisted?
523
+ _delete_row if persisted?
157
524
  @destroyed = true
158
525
  freeze
159
526
  end
@@ -161,15 +528,18 @@ module ActiveRecord
161
528
  # Deletes the record in the database and freezes this instance to reflect
162
529
  # that no changes should be made (since they can't be persisted).
163
530
  #
164
- # There's a series of callbacks associated with <tt>destroy</tt>. If
165
- # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
166
- # and <tt>destroy</tt> returns +false+. See
167
- # ActiveRecord::Callbacks for further details.
531
+ # There's a series of callbacks associated with #destroy. If the
532
+ # <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
533
+ # and #destroy returns +false+.
534
+ # See ActiveRecord::Callbacks for further details.
168
535
  def destroy
169
- raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
536
+ _raise_readonly_record_error if readonly?
170
537
  destroy_associations
171
- self.class.connection.add_transaction_record(self)
172
- destroy_row if persisted?
538
+ @_trigger_destroy_callback = if persisted?
539
+ destroy_row > 0
540
+ else
541
+ true
542
+ end
173
543
  @destroyed = true
174
544
  freeze
175
545
  end
@@ -177,12 +547,12 @@ module ActiveRecord
177
547
  # Deletes the record in the database and freezes this instance to reflect
178
548
  # that no changes should be made (since they can't be persisted).
179
549
  #
180
- # There's a series of callbacks associated with <tt>destroy!</tt>. If
181
- # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
182
- # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
183
- # ActiveRecord::Callbacks for further details.
550
+ # There's a series of callbacks associated with #destroy!. If the
551
+ # <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
552
+ # and #destroy! raises ActiveRecord::RecordNotDestroyed.
553
+ # See ActiveRecord::Callbacks for further details.
184
554
  def destroy!
185
- destroy || raise(RecordNotDestroyed.new("Failed to destroy the record", self))
555
+ destroy || _raise_record_not_destroyed
186
556
  end
187
557
 
188
558
  # Returns an instance of the specified +klass+ with the attributes of the
@@ -194,19 +564,24 @@ module ActiveRecord
194
564
  # instance using the companies/company partial instead of clients/client.
195
565
  #
196
566
  # Note: The new instance will share a link to the same attributes as the original class.
197
- # So any change to the attributes in either instance will affect the other.
567
+ # Therefore the sti column value will still be the same.
568
+ # Any change to the attributes on either instance will affect both instances.
569
+ # If you want to change the sti column as well, use #becomes! instead.
198
570
  def becomes(klass)
199
- became = klass.new
200
- became.instance_variable_set("@attributes", @attributes)
201
- changed_attributes = @changed_attributes if defined?(@changed_attributes)
202
- became.instance_variable_set("@changed_attributes", changed_attributes || {})
203
- became.instance_variable_set("@new_record", new_record?)
204
- became.instance_variable_set("@destroyed", destroyed?)
205
- became.instance_variable_set("@errors", errors)
571
+ became = klass.allocate
572
+
573
+ became.send(:initialize) do |becoming|
574
+ becoming.instance_variable_set(:@attributes, @attributes)
575
+ becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
576
+ becoming.instance_variable_set(:@new_record, new_record?)
577
+ becoming.instance_variable_set(:@destroyed, destroyed?)
578
+ becoming.errors.copy!(errors)
579
+ end
580
+
206
581
  became
207
582
  end
208
583
 
209
- # Wrapper around +becomes+ that also changes the instance's sti column value.
584
+ # Wrapper around #becomes that also changes the instance's sti column value.
210
585
  # This is especially useful if you want to persist the changed class in your
211
586
  # database.
212
587
  #
@@ -226,18 +601,19 @@ module ActiveRecord
226
601
  # This is especially useful for boolean flags on existing records. Also note that
227
602
  #
228
603
  # * Validation is skipped.
229
- # * Callbacks are invoked.
604
+ # * \Callbacks are invoked.
230
605
  # * updated_at/updated_on column is updated if that column is available.
231
606
  # * Updates all the attributes that are dirty in this object.
232
607
  #
233
- # This method raises an +ActiveRecord::ActiveRecordError+ if the
608
+ # This method raises an ActiveRecord::ActiveRecordError if the
234
609
  # attribute is marked as readonly.
235
610
  #
236
- # See also +update_column+.
611
+ # Also see #update_column.
237
612
  def update_attribute(name, value)
238
613
  name = name.to_s
239
614
  verify_readonly_attribute(name)
240
- send("#{name}=", value)
615
+ public_send("#{name}=", value)
616
+
241
617
  save(validate: false)
242
618
  end
243
619
 
@@ -253,10 +629,8 @@ module ActiveRecord
253
629
  end
254
630
  end
255
631
 
256
- alias update_attributes update
257
-
258
- # Updates its receiver just like +update+ but calls <tt>save!</tt> instead
259
- # of +save+, so an exception is raised if the record is invalid.
632
+ # Updates its receiver just like #update but calls #save! instead
633
+ # of +save+, so an exception is raised if the record is invalid and saving will fail.
260
634
  def update!(attributes)
261
635
  # The following transaction covers any possible database side-effects of the
262
636
  # attributes assignment. For example, setting the IDs of a child collection.
@@ -266,8 +640,6 @@ module ActiveRecord
266
640
  end
267
641
  end
268
642
 
269
- alias update_attributes! update!
270
-
271
643
  # Equivalent to <code>update_columns(name => value)</code>.
272
644
  def update_column(name, value)
273
645
  update_columns(name => value)
@@ -282,27 +654,34 @@ module ActiveRecord
282
654
  # the database, but take into account that in consequence the regular update
283
655
  # procedures are totally bypassed. In particular:
284
656
  #
285
- # * Validations are skipped.
286
- # * Callbacks are skipped.
657
+ # * \Validations are skipped.
658
+ # * \Callbacks are skipped.
287
659
  # * +updated_at+/+updated_on+ are not updated.
660
+ # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
288
661
  #
289
- # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
662
+ # This method raises an ActiveRecord::ActiveRecordError when called on new
290
663
  # objects, or when at least one of the attributes is marked as readonly.
291
664
  def update_columns(attributes)
292
665
  raise ActiveRecordError, "cannot update a new record" if new_record?
293
666
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
294
667
 
295
- attributes.each_key do |key|
296
- verify_readonly_attribute(key.to_s)
668
+ attributes = attributes.transform_keys do |key|
669
+ name = key.to_s
670
+ name = self.class.attribute_aliases[name] || name
671
+ verify_readonly_attribute(name) || name
297
672
  end
298
673
 
299
- updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
300
-
674
+ id_in_database = self.id_in_database
301
675
  attributes.each do |k, v|
302
- raw_write_attribute(k, v)
676
+ write_attribute_without_type_cast(k, v)
303
677
  end
304
678
 
305
- updated_count == 1
679
+ affected_rows = self.class._update_record(
680
+ attributes,
681
+ @primary_key => id_in_database
682
+ )
683
+
684
+ affected_rows == 1
306
685
  end
307
686
 
308
687
  # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
@@ -314,42 +693,56 @@ module ActiveRecord
314
693
  self
315
694
  end
316
695
 
317
- # Wrapper around +increment+ that saves the record. This method differs from
318
- # its non-bang version in that it passes through the attribute setter.
319
- # Saving is not subjected to validation checks. Returns +true+ if the
320
- # record could be saved.
321
- def increment!(attribute, by = 1)
322
- increment(attribute, by).update_attribute(attribute, self[attribute])
696
+ # Wrapper around #increment that writes the update to the database.
697
+ # Only +attribute+ is updated; the record itself is not saved.
698
+ # This means that any other modified attributes will still be dirty.
699
+ # Validations and callbacks are skipped. Supports the +touch+ option from
700
+ # +update_counters+, see that for more.
701
+ # Returns +self+.
702
+ def increment!(attribute, by = 1, touch: nil)
703
+ increment(attribute, by)
704
+ change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
705
+ self.class.update_counters(id, attribute => change, touch: touch)
706
+ public_send(:"clear_#{attribute}_change")
707
+ self
323
708
  end
324
709
 
325
710
  # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
326
711
  # The decrement is performed directly on the underlying attribute, no setter is invoked.
327
712
  # Only makes sense for number-based attributes. Returns +self+.
328
713
  def decrement(attribute, by = 1)
329
- self[attribute] ||= 0
330
- self[attribute] -= by
331
- self
714
+ increment(attribute, -by)
332
715
  end
333
716
 
334
- # Wrapper around +decrement+ that saves the record. This method differs from
335
- # its non-bang version in that it passes through the attribute setter.
336
- # Saving is not subjected to validation checks. Returns +true+ if the
337
- # record could be saved.
338
- def decrement!(attribute, by = 1)
339
- decrement(attribute, by).update_attribute(attribute, self[attribute])
717
+ # Wrapper around #decrement that writes the update to the database.
718
+ # Only +attribute+ is updated; the record itself is not saved.
719
+ # This means that any other modified attributes will still be dirty.
720
+ # Validations and callbacks are skipped. Supports the +touch+ option from
721
+ # +update_counters+, see that for more.
722
+ # Returns +self+.
723
+ def decrement!(attribute, by = 1, touch: nil)
724
+ increment!(attribute, -by, touch: touch)
340
725
  end
341
726
 
342
727
  # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
343
728
  # if the predicate returns +true+ the attribute will become +false+. This
344
729
  # method toggles directly the underlying value without calling any setter.
345
730
  # Returns +self+.
731
+ #
732
+ # Example:
733
+ #
734
+ # user = User.first
735
+ # user.banned? # => false
736
+ # user.toggle(:banned)
737
+ # user.banned? # => true
738
+ #
346
739
  def toggle(attribute)
347
- self[attribute] = !send("#{attribute}?")
740
+ self[attribute] = !public_send("#{attribute}?")
348
741
  self
349
742
  end
350
743
 
351
- # Wrapper around +toggle+ that saves the record. This method differs from
352
- # its non-bang version in that it passes through the attribute setter.
744
+ # Wrapper around #toggle that saves the record. This method differs from
745
+ # its non-bang version in the sense that it passes through the attribute setter.
353
746
  # Saving is not subjected to validation checks. Returns +true+ if the
354
747
  # record could be saved.
355
748
  def toggle!(attribute)
@@ -358,8 +751,8 @@ module ActiveRecord
358
751
 
359
752
  # Reloads the record from the database.
360
753
  #
361
- # This method finds record by its primary key (which could be assigned manually) and
362
- # modifies the receiver in-place:
754
+ # This method finds the record by its primary key (which could be assigned
755
+ # manually) and modifies the receiver in-place:
363
756
  #
364
757
  # account = Account.new
365
758
  # # => #<Account id: nil, email: nil>
@@ -371,7 +764,7 @@ module ActiveRecord
371
764
  # Attributes are reloaded from the database, and caches busted, in
372
765
  # particular the associations cache and the QueryCache.
373
766
  #
374
- # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
767
+ # If the record no longer exists in the database ActiveRecord::RecordNotFound
375
768
  # is raised. Otherwise, in addition to the in-place modification the method
376
769
  # returns +self+ for convenience.
377
770
  #
@@ -405,8 +798,6 @@ module ActiveRecord
405
798
  # end
406
799
  #
407
800
  def reload(options = nil)
408
- clear_aggregation_cache
409
- clear_association_cache
410
801
  self.class.connection.clear_query_cache
411
802
 
412
803
  fresh_object =
@@ -416,24 +807,28 @@ module ActiveRecord
416
807
  self.class.unscoped { self.class.find(id) }
417
808
  end
418
809
 
419
- @attributes = fresh_object.instance_variable_get('@attributes')
810
+ @attributes = fresh_object.instance_variable_get(:@attributes)
420
811
  @new_record = false
812
+ @previously_new_record = false
421
813
  self
422
814
  end
423
815
 
424
- # Saves the record with the updated_at/on attributes set to the current time.
816
+ # Saves the record with the updated_at/on attributes set to the current time
817
+ # or the time specified.
425
818
  # Please note that no validation is performed and only the +after_touch+,
426
819
  # +after_commit+ and +after_rollback+ callbacks are executed.
427
820
  #
821
+ # This method can be passed attribute names and an optional time argument.
428
822
  # If attribute names are passed, they are updated along with updated_at/on
429
- # attributes.
823
+ # attributes. If no time argument is passed, the current time is used as default.
430
824
  #
431
- # product.touch # updates updated_at/on
825
+ # product.touch # updates updated_at/on with current time
826
+ # product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time
432
827
  # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
433
828
  # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
434
829
  #
435
- # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on
436
- # associated object.
830
+ # If used along with {belongs_to}[rdoc-ref:Associations::ClassMethods#belongs_to]
831
+ # then +touch+ will invoke +touch+ method on associated object.
437
832
  #
438
833
  # class Brake < ActiveRecord::Base
439
834
  # belongs_to :car, touch: true
@@ -452,84 +847,125 @@ module ActiveRecord
452
847
  # ball = Ball.new
453
848
  # ball.touch(:updated_at) # => raises ActiveRecordError
454
849
  #
455
- def touch(*names)
456
- raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
457
-
458
- attributes = timestamp_attributes_for_update_in_model
459
- attributes.concat(names)
460
-
461
- unless attributes.empty?
462
- current_time = current_time_from_proper_timezone
463
- changes = {}
464
-
465
- attributes.each do |column|
466
- column = column.to_s
467
- changes[column] = write_attribute(column, current_time)
468
- end
850
+ def touch(*names, time: nil)
851
+ _raise_record_not_touched_error unless persisted?
469
852
 
470
- changes[self.class.locking_column] = increment_lock if locking_enabled?
853
+ attribute_names = timestamp_attributes_for_update_in_model
854
+ attribute_names |= names.map! do |name|
855
+ name = name.to_s
856
+ self.class.attribute_aliases[name] || name
857
+ end unless names.empty?
471
858
 
472
- clear_attribute_changes(changes.keys)
473
- primary_key = self.class.primary_key
474
- self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
859
+ unless attribute_names.empty?
860
+ affected_rows = _touch_row(attribute_names, time)
861
+ @_trigger_update_callback = affected_rows == 1
475
862
  else
476
863
  true
477
864
  end
478
865
  end
479
866
 
480
867
  private
481
-
482
868
  # A hook to be overridden by association modules.
483
869
  def destroy_associations
484
870
  end
485
871
 
486
872
  def destroy_row
487
- relation_for_destroy.delete_all
873
+ _delete_row
874
+ end
875
+
876
+ def _delete_row
877
+ self.class._delete_record(@primary_key => id_in_database)
488
878
  end
489
879
 
490
- def relation_for_destroy
491
- pk = self.class.primary_key
492
- column = self.class.columns_hash[pk]
493
- substitute = self.class.connection.substitute_at(column)
880
+ def _touch_row(attribute_names, time)
881
+ time ||= current_time_from_proper_timezone
494
882
 
495
- relation = self.class.unscoped.where(
496
- self.class.arel_table[pk].eq(substitute))
883
+ attribute_names.each do |attr_name|
884
+ _write_attribute(attr_name, time)
885
+ end
886
+
887
+ _update_row(attribute_names, "touch")
888
+ end
497
889
 
498
- relation.bind_values = [[column, id]]
499
- relation
890
+ def _update_row(attribute_names, attempted_action = "update")
891
+ self.class._update_record(
892
+ attributes_with_values(attribute_names),
893
+ @primary_key => id_in_database
894
+ )
500
895
  end
501
896
 
502
- def create_or_update
503
- raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
504
- result = new_record? ? _create_record : _update_record
897
+ def create_or_update(**, &block)
898
+ _raise_readonly_record_error if readonly?
899
+ return false if destroyed?
900
+ result = new_record? ? _create_record(&block) : _update_record(&block)
505
901
  result != false
506
902
  end
507
903
 
508
904
  # Updates the associated record with values matching those of the instance attributes.
509
905
  # Returns the number of affected rows.
510
906
  def _update_record(attribute_names = self.attribute_names)
511
- attributes_values = arel_attributes_with_values_for_update(attribute_names)
512
- if attributes_values.empty?
513
- 0
907
+ attribute_names = attributes_for_update(attribute_names)
908
+
909
+ if attribute_names.empty?
910
+ affected_rows = 0
911
+ @_trigger_update_callback = true
514
912
  else
515
- self.class.unscoped._update_record attributes_values, id, id_was
913
+ affected_rows = _update_row(attribute_names)
914
+ @_trigger_update_callback = affected_rows == 1
516
915
  end
916
+
917
+ @previously_new_record = false
918
+
919
+ yield(self) if block_given?
920
+
921
+ affected_rows
517
922
  end
518
923
 
519
924
  # Creates a record with values matching those of the instance attributes
520
925
  # and returns its id.
521
926
  def _create_record(attribute_names = self.attribute_names)
522
- attributes_values = arel_attributes_with_values_for_create(attribute_names)
927
+ attribute_names = attributes_for_create(attribute_names)
928
+
929
+ new_id = self.class._insert_record(
930
+ attributes_with_values(attribute_names)
931
+ )
523
932
 
524
- new_id = self.class.unscoped.insert attributes_values
525
- self.id ||= new_id if self.class.primary_key
933
+ self.id ||= new_id if @primary_key
526
934
 
527
935
  @new_record = false
936
+ @previously_new_record = true
937
+
938
+ yield(self) if block_given?
939
+
528
940
  id
529
941
  end
530
942
 
531
943
  def verify_readonly_attribute(name)
532
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
944
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
945
+ end
946
+
947
+ def _raise_record_not_destroyed
948
+ @_association_destroy_exception ||= nil
949
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
950
+ ensure
951
+ @_association_destroy_exception = nil
952
+ end
953
+
954
+ def _raise_readonly_record_error
955
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly"
956
+ end
957
+
958
+ def _raise_record_not_touched_error
959
+ raise ActiveRecordError, <<~MSG.squish
960
+ Cannot touch on a new or destroyed record object. Consider using
961
+ persisted?, new_record?, or destroyed? before touching.
962
+ MSG
963
+ end
964
+
965
+ # The name of the method used to touch a +belongs_to+ association when the
966
+ # +:touch+ option is used.
967
+ def belongs_to_touch_method
968
+ :touch
533
969
  end
534
970
  end
535
971
  end