activerecord 4.2.0 → 6.1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1221 -796
  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 +267 -249
  8. data/lib/active_record/association_relation.rb +45 -7
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +172 -67
  11. data/lib/active_record/associations/association_scope.rb +105 -129
  12. data/lib/active_record/associations/belongs_to_association.rb +85 -59
  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 -33
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
  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 +168 -279
  22. data/lib/active_record/associations/collection_proxy.rb +263 -155
  23. data/lib/active_record/associations/foreign_association.rb +33 -0
  24. data/lib/active_record/associations/has_many_association.rb +57 -84
  25. data/lib/active_record/associations/has_many_through_association.rb +70 -82
  26. data/lib/active_record/associations/has_one_association.rb +74 -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 -73
  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 +175 -164
  32. data/lib/active_record/associations/preloader/association.rb +107 -112
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +99 -96
  35. data/lib/active_record/associations/singular_association.rb +18 -45
  36. data/lib/active_record/associations/through_association.rb +49 -24
  37. data/lib/active_record/associations.rb +1845 -1597
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -138
  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 +59 -36
  46. data/lib/active_record/attribute_methods/write.rb +25 -56
  47. data/lib/active_record/attribute_methods.rb +153 -162
  48. data/lib/active_record/attributes.rb +234 -70
  49. data/lib/active_record/autosave_association.rb +157 -69
  50. data/lib/active_record/base.rb +49 -50
  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 +46 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
  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 +196 -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 +271 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
  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 +78 -161
  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 -57
  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 +5 -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 +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  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 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  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 +171 -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 +499 -293
  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 +595 -382
  119. data/lib/active_record/connection_adapters/schema_cache.rb +191 -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 +146 -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 -389
  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 +488 -243
  133. data/lib/active_record/counter_cache.rb +71 -50
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 +273 -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 +212 -94
  143. data/lib/active_record/errors.rb +225 -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 +11 -6
  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 +273 -496
  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 +64 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +52 -0
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +103 -95
  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 +298 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +685 -309
  170. data/lib/active_record/model_schema.rb +420 -113
  171. data/lib/active_record/nested_attributes.rb +265 -216
  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 +574 -135
  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 +175 -54
  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 +533 -216
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +485 -310
  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 +326 -244
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +318 -256
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +99 -84
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
  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 +139 -96
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -409
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +23 -21
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -342
  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 -26
  209. data/lib/active_record/scoping/default.rb +96 -82
  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 +133 -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 +366 -129
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
  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 +291 -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 +181 -152
  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 -41
  231. data/lib/active_record/type/date_time.rb +4 -38
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  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 +33 -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 +65 -48
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +44 -28
  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 -10
  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.rb +7 -5
  339. metadata +175 -65
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  347. data/lib/active_record/attribute.rb +0 -149
  348. data/lib/active_record/attribute_decorators.rb +0 -66
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -30
  363. data/lib/active_record/type/decimal.rb +0 -40
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -55
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -36
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -101
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  374. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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(nil, 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,14 +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
- destroy_row if persisted?
538
+ @_trigger_destroy_callback = if persisted?
539
+ destroy_row > 0
540
+ else
541
+ true
542
+ end
172
543
  @destroyed = true
173
544
  freeze
174
545
  end
@@ -176,12 +547,12 @@ module ActiveRecord
176
547
  # Deletes the record in the database and freezes this instance to reflect
177
548
  # that no changes should be made (since they can't be persisted).
178
549
  #
179
- # There's a series of callbacks associated with <tt>destroy!</tt>. If
180
- # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
181
- # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
182
- # 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.
183
554
  def destroy!
184
- destroy || raise(ActiveRecord::RecordNotDestroyed, self)
555
+ destroy || _raise_record_not_destroyed
185
556
  end
186
557
 
187
558
  # Returns an instance of the specified +klass+ with the attributes of the
@@ -193,18 +564,24 @@ module ActiveRecord
193
564
  # instance using the companies/company partial instead of clients/client.
194
565
  #
195
566
  # Note: The new instance will share a link to the same attributes as the original class.
196
- # 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.
197
570
  def becomes(klass)
198
- became = klass.new
199
- became.instance_variable_set("@attributes", @attributes)
200
- became.instance_variable_set("@changed_attributes", @changed_attributes) if defined?(@changed_attributes)
201
- became.instance_variable_set("@new_record", new_record?)
202
- became.instance_variable_set("@destroyed", destroyed?)
203
- 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
+
204
581
  became
205
582
  end
206
583
 
207
- # 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.
208
585
  # This is especially useful if you want to persist the changed class in your
209
586
  # database.
210
587
  #
@@ -224,18 +601,19 @@ module ActiveRecord
224
601
  # This is especially useful for boolean flags on existing records. Also note that
225
602
  #
226
603
  # * Validation is skipped.
227
- # * Callbacks are invoked.
604
+ # * \Callbacks are invoked.
228
605
  # * updated_at/updated_on column is updated if that column is available.
229
606
  # * Updates all the attributes that are dirty in this object.
230
607
  #
231
- # This method raises an +ActiveRecord::ActiveRecordError+ if the
608
+ # This method raises an ActiveRecord::ActiveRecordError if the
232
609
  # attribute is marked as readonly.
233
610
  #
234
- # See also +update_column+.
611
+ # Also see #update_column.
235
612
  def update_attribute(name, value)
236
613
  name = name.to_s
237
614
  verify_readonly_attribute(name)
238
- send("#{name}=", value)
615
+ public_send("#{name}=", value)
616
+
239
617
  save(validate: false)
240
618
  end
241
619
 
@@ -251,10 +629,8 @@ module ActiveRecord
251
629
  end
252
630
  end
253
631
 
254
- alias update_attributes update
255
-
256
- # Updates its receiver just like +update+ but calls <tt>save!</tt> instead
257
- # 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.
258
634
  def update!(attributes)
259
635
  # The following transaction covers any possible database side-effects of the
260
636
  # attributes assignment. For example, setting the IDs of a child collection.
@@ -264,8 +640,6 @@ module ActiveRecord
264
640
  end
265
641
  end
266
642
 
267
- alias update_attributes! update!
268
-
269
643
  # Equivalent to <code>update_columns(name => value)</code>.
270
644
  def update_column(name, value)
271
645
  update_columns(name => value)
@@ -280,27 +654,34 @@ module ActiveRecord
280
654
  # the database, but take into account that in consequence the regular update
281
655
  # procedures are totally bypassed. In particular:
282
656
  #
283
- # * Validations are skipped.
284
- # * Callbacks are skipped.
657
+ # * \Validations are skipped.
658
+ # * \Callbacks are skipped.
285
659
  # * +updated_at+/+updated_on+ are not updated.
660
+ # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
286
661
  #
287
- # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
662
+ # This method raises an ActiveRecord::ActiveRecordError when called on new
288
663
  # objects, or when at least one of the attributes is marked as readonly.
289
664
  def update_columns(attributes)
290
665
  raise ActiveRecordError, "cannot update a new record" if new_record?
291
666
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
292
667
 
293
- attributes.each_key do |key|
294
- 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
295
672
  end
296
673
 
297
- updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
298
-
674
+ id_in_database = self.id_in_database
299
675
  attributes.each do |k, v|
300
- raw_write_attribute(k, v)
676
+ write_attribute_without_type_cast(k, v)
301
677
  end
302
678
 
303
- updated_count == 1
679
+ affected_rows = self.class._update_record(
680
+ attributes,
681
+ @primary_key => id_in_database
682
+ )
683
+
684
+ affected_rows == 1
304
685
  end
305
686
 
306
687
  # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
@@ -312,42 +693,56 @@ module ActiveRecord
312
693
  self
313
694
  end
314
695
 
315
- # Wrapper around +increment+ that saves the record. This method differs from
316
- # its non-bang version in that it passes through the attribute setter.
317
- # Saving is not subjected to validation checks. Returns +true+ if the
318
- # record could be saved.
319
- def increment!(attribute, by = 1)
320
- 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
321
708
  end
322
709
 
323
710
  # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
324
711
  # The decrement is performed directly on the underlying attribute, no setter is invoked.
325
712
  # Only makes sense for number-based attributes. Returns +self+.
326
713
  def decrement(attribute, by = 1)
327
- self[attribute] ||= 0
328
- self[attribute] -= by
329
- self
714
+ increment(attribute, -by)
330
715
  end
331
716
 
332
- # Wrapper around +decrement+ that saves the record. This method differs from
333
- # its non-bang version in that it passes through the attribute setter.
334
- # Saving is not subjected to validation checks. Returns +true+ if the
335
- # record could be saved.
336
- def decrement!(attribute, by = 1)
337
- 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)
338
725
  end
339
726
 
340
727
  # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
341
728
  # if the predicate returns +true+ the attribute will become +false+. This
342
729
  # method toggles directly the underlying value without calling any setter.
343
730
  # Returns +self+.
731
+ #
732
+ # Example:
733
+ #
734
+ # user = User.first
735
+ # user.banned? # => false
736
+ # user.toggle(:banned)
737
+ # user.banned? # => true
738
+ #
344
739
  def toggle(attribute)
345
- self[attribute] = !send("#{attribute}?")
740
+ self[attribute] = !public_send("#{attribute}?")
346
741
  self
347
742
  end
348
743
 
349
- # Wrapper around +toggle+ that saves the record. This method differs from
350
- # 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.
351
746
  # Saving is not subjected to validation checks. Returns +true+ if the
352
747
  # record could be saved.
353
748
  def toggle!(attribute)
@@ -356,8 +751,8 @@ module ActiveRecord
356
751
 
357
752
  # Reloads the record from the database.
358
753
  #
359
- # This method finds record by its primary key (which could be assigned manually) and
360
- # 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:
361
756
  #
362
757
  # account = Account.new
363
758
  # # => #<Account id: nil, email: nil>
@@ -367,9 +762,9 @@ module ActiveRecord
367
762
  # # => #<Account id: 1, email: 'account@example.com'>
368
763
  #
369
764
  # Attributes are reloaded from the database, and caches busted, in
370
- # particular the associations cache.
765
+ # particular the associations cache and the QueryCache.
371
766
  #
372
- # 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
373
768
  # is raised. Otherwise, in addition to the in-place modification the method
374
769
  # returns +self+ for convenience.
375
770
  #
@@ -403,8 +798,7 @@ module ActiveRecord
403
798
  # end
404
799
  #
405
800
  def reload(options = nil)
406
- clear_aggregation_cache
407
- clear_association_cache
801
+ self.class.connection.clear_query_cache
408
802
 
409
803
  fresh_object =
410
804
  if options && options[:lock]
@@ -413,24 +807,28 @@ module ActiveRecord
413
807
  self.class.unscoped { self.class.find(id) }
414
808
  end
415
809
 
416
- @attributes = fresh_object.instance_variable_get('@attributes')
810
+ @attributes = fresh_object.instance_variable_get(:@attributes)
417
811
  @new_record = false
812
+ @previously_new_record = false
418
813
  self
419
814
  end
420
815
 
421
- # 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.
422
818
  # Please note that no validation is performed and only the +after_touch+,
423
819
  # +after_commit+ and +after_rollback+ callbacks are executed.
424
820
  #
821
+ # This method can be passed attribute names and an optional time argument.
425
822
  # If attribute names are passed, they are updated along with updated_at/on
426
- # attributes.
823
+ # attributes. If no time argument is passed, the current time is used as default.
427
824
  #
428
- # 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
429
827
  # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
430
828
  # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
431
829
  #
432
- # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on
433
- # 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.
434
832
  #
435
833
  # class Brake < ActiveRecord::Base
436
834
  # belongs_to :car, touch: true
@@ -449,84 +847,125 @@ module ActiveRecord
449
847
  # ball = Ball.new
450
848
  # ball.touch(:updated_at) # => raises ActiveRecordError
451
849
  #
452
- def touch(*names)
453
- raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
454
-
455
- attributes = timestamp_attributes_for_update_in_model
456
- attributes.concat(names)
457
-
458
- unless attributes.empty?
459
- current_time = current_time_from_proper_timezone
460
- changes = {}
461
-
462
- attributes.each do |column|
463
- column = column.to_s
464
- changes[column] = write_attribute(column, current_time)
465
- end
850
+ def touch(*names, time: nil)
851
+ _raise_record_not_touched_error unless persisted?
466
852
 
467
- 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?
468
858
 
469
- clear_attribute_changes(changes.keys)
470
- primary_key = self.class.primary_key
471
- 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
472
862
  else
473
863
  true
474
864
  end
475
865
  end
476
866
 
477
867
  private
478
-
479
868
  # A hook to be overridden by association modules.
480
869
  def destroy_associations
481
870
  end
482
871
 
483
872
  def destroy_row
484
- 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)
485
878
  end
486
879
 
487
- def relation_for_destroy
488
- pk = self.class.primary_key
489
- column = self.class.columns_hash[pk]
490
- substitute = self.class.connection.substitute_at(column)
880
+ def _touch_row(attribute_names, time)
881
+ time ||= current_time_from_proper_timezone
491
882
 
492
- relation = self.class.unscoped.where(
493
- 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
494
889
 
495
- relation.bind_values = [[column, id]]
496
- 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
+ )
497
895
  end
498
896
 
499
- def create_or_update
500
- raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
501
- 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)
502
901
  result != false
503
902
  end
504
903
 
505
904
  # Updates the associated record with values matching those of the instance attributes.
506
905
  # Returns the number of affected rows.
507
906
  def _update_record(attribute_names = self.attribute_names)
508
- attributes_values = arel_attributes_with_values_for_update(attribute_names)
509
- if attributes_values.empty?
510
- 0
907
+ attribute_names = attributes_for_update(attribute_names)
908
+
909
+ if attribute_names.empty?
910
+ affected_rows = 0
911
+ @_trigger_update_callback = true
511
912
  else
512
- self.class.unscoped._update_record attributes_values, id, id_was
913
+ affected_rows = _update_row(attribute_names)
914
+ @_trigger_update_callback = affected_rows == 1
513
915
  end
916
+
917
+ @previously_new_record = false
918
+
919
+ yield(self) if block_given?
920
+
921
+ affected_rows
514
922
  end
515
923
 
516
924
  # Creates a record with values matching those of the instance attributes
517
925
  # and returns its id.
518
926
  def _create_record(attribute_names = self.attribute_names)
519
- 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
+ )
520
932
 
521
- new_id = self.class.unscoped.insert attributes_values
522
- self.id ||= new_id if self.class.primary_key
933
+ self.id ||= new_id if @primary_key
523
934
 
524
935
  @new_record = false
936
+ @previously_new_record = true
937
+
938
+ yield(self) if block_given?
939
+
525
940
  id
526
941
  end
527
942
 
528
943
  def verify_readonly_attribute(name)
529
- 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
530
969
  end
531
970
  end
532
971
  end