activerecord 4.2.11.1 → 6.0.3.5

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 (373) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +721 -1522
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +266 -251
  9. data/lib/active_record/association_relation.rb +20 -13
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +128 -57
  12. data/lib/active_record/associations/association_scope.rb +103 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -66
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +136 -288
  23. data/lib/active_record/associations/collection_proxy.rb +241 -147
  24. data/lib/active_record/associations/foreign_association.rb +10 -1
  25. data/lib/active_record/associations/has_many_association.rb +34 -98
  26. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  27. data/lib/active_record/associations/has_one_association.rb +61 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +149 -166
  33. data/lib/active_record/associations/preloader/association.rb +90 -123
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +90 -93
  36. data/lib/active_record/associations/singular_association.rb +18 -39
  37. data/lib/active_record/associations/through_association.rb +38 -18
  38. data/lib/active_record/associations.rb +1737 -1597
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +57 -37
  48. data/lib/active_record/attribute_methods/write.rb +32 -55
  49. data/lib/active_record/attribute_methods.rb +120 -135
  50. data/lib/active_record/attributes.rb +213 -82
  51. data/lib/active_record/autosave_association.rb +97 -41
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +23 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +804 -297
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +240 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +371 -242
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +694 -256
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +473 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +507 -639
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -181
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -114
  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 +5 -1
  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 +9 -22
  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/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +51 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -296
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +299 -349
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +252 -230
  130. data/lib/active_record/counter_cache.rb +70 -49
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +227 -501
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +86 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +623 -305
  165. data/lib/active_record/model_schema.rb +313 -112
  166. data/lib/active_record/nested_attributes.rb +263 -223
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +557 -126
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +430 -281
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +268 -254
  182. data/lib/active_record/relation/delegation.rb +75 -84
  183. data/lib/active_record/relation/finder_methods.rb +285 -241
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +78 -88
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +110 -119
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +603 -397
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +11 -14
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +530 -341
  201. data/lib/active_record/result.rb +79 -43
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping/default.rb +98 -83
  208. data/lib/active_record/scoping/named.rb +86 -33
  209. data/lib/active_record/scoping.rb +45 -27
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -100
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +80 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +223 -157
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -45
  228. data/lib/active_record/type/date_time.rb +4 -49
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +23 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +11 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +42 -55
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +42 -22
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. data/lib/rails/generators/active_record.rb +7 -5
  341. metadata +168 -59
  342. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  343. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  344. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  345. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  346. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  347. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  348. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  349. data/lib/active_record/attribute.rb +0 -163
  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/mysql_adapter.rb +0 -498
  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 -31
  363. data/lib/active_record/type/decimal.rb +0 -64
  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 -59
  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 -40
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -110
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  373. 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,96 @@ 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_attribute(name)
418
+ bind = predicate_builder.build_bind_attribute(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
427
+ sync_with_transaction_state if @transaction_state&.finalized?
87
428
  @new_record
88
429
  end
89
430
 
90
431
  # Returns true if this object has been destroyed, otherwise returns false.
91
432
  def destroyed?
92
- sync_with_transaction_state
433
+ sync_with_transaction_state if @transaction_state&.finalized?
93
434
  @destroyed
94
435
  end
95
436
 
96
437
  # Returns true if the record is persisted, i.e. it's not a new record and it was
97
438
  # not destroyed, otherwise returns false.
98
439
  def persisted?
99
- !(new_record? || destroyed?)
440
+ sync_with_transaction_state if @transaction_state&.finalized?
441
+ !(@new_record || @destroyed)
100
442
  end
101
443
 
444
+ ##
445
+ # :call-seq:
446
+ # save(*args)
447
+ #
102
448
  # Saves the model.
103
449
  #
104
- # If the model is new a record gets created in the database, otherwise
450
+ # If the model is new, a record gets created in the database, otherwise
105
451
  # the existing record gets updated.
106
452
  #
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
453
+ # By default, save always runs validations. If any of them fail the action
454
+ # is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
455
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
110
456
  # ActiveRecord::Validations for more information.
111
457
  #
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
458
+ # By default, #save also sets the +updated_at+/+updated_on+ attributes to
459
+ # the current time. However, if you supply <tt>touch: false</tt>, these
460
+ # timestamps will not be updated.
461
+ #
462
+ # There's a series of callbacks associated with #save. If any of the
463
+ # <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled and
464
+ # #save returns +false+. See ActiveRecord::Callbacks for further
115
465
  # details.
116
466
  #
117
467
  # Attributes marked as readonly are silently ignored if the record is
118
468
  # being updated.
119
- def save(*)
120
- create_or_update
469
+ def save(*args, **options, &block)
470
+ create_or_update(*args, **options, &block)
121
471
  rescue ActiveRecord::RecordInvalid
122
472
  false
123
473
  end
124
474
 
475
+ ##
476
+ # :call-seq:
477
+ # save!(*args)
478
+ #
125
479
  # Saves the model.
126
480
  #
127
- # If the model is new a record gets created in the database, otherwise
481
+ # If the model is new, a record gets created in the database, otherwise
128
482
  # the existing record gets updated.
129
483
  #
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.
484
+ # By default, #save! always runs validations. If any of them fail
485
+ # ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
486
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
487
+ # ActiveRecord::Validations for more information.
488
+ #
489
+ # By default, #save! also sets the +updated_at+/+updated_on+ attributes to
490
+ # the current time. However, if you supply <tt>touch: false</tt>, these
491
+ # timestamps will not be updated.
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
+ # There's a series of callbacks associated with #save!. If any of
494
+ # the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
495
+ # and #save! raises ActiveRecord::RecordNotSaved. See
137
496
  # ActiveRecord::Callbacks for further details.
138
497
  #
139
498
  # Attributes marked as readonly are silently ignored if the record is
140
499
  # being updated.
141
- def save!(*)
142
- create_or_update || raise(RecordNotSaved.new("Failed to save the record", self))
500
+ #
501
+ # Unless an error is raised, returns true.
502
+ def save!(*args, **options, &block)
503
+ create_or_update(*args, **options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
143
504
  end
144
505
 
145
506
  # Deletes the record in the database and freezes this instance to
@@ -149,11 +510,13 @@ module ActiveRecord
149
510
  # The row is simply removed with an SQL +DELETE+ statement on the
150
511
  # record's primary key, and no callbacks are executed.
151
512
  #
513
+ # Note that this will also delete records marked as {#readonly?}[rdoc-ref:Core#readonly?].
514
+ #
152
515
  # To enforce the object's +before_destroy+ and +after_destroy+
153
516
  # callbacks or any <tt>:dependent</tt> association
154
517
  # options, use <tt>#destroy</tt>.
155
518
  def delete
156
- self.class.delete(id) if persisted?
519
+ _delete_row if persisted?
157
520
  @destroyed = true
158
521
  freeze
159
522
  end
@@ -161,15 +524,18 @@ module ActiveRecord
161
524
  # Deletes the record in the database and freezes this instance to reflect
162
525
  # that no changes should be made (since they can't be persisted).
163
526
  #
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.
527
+ # There's a series of callbacks associated with #destroy. If the
528
+ # <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
529
+ # and #destroy returns +false+.
530
+ # See ActiveRecord::Callbacks for further details.
168
531
  def destroy
169
- raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
532
+ _raise_readonly_record_error if readonly?
170
533
  destroy_associations
171
- self.class.connection.add_transaction_record(self)
172
- destroy_row if persisted?
534
+ @_trigger_destroy_callback = if persisted?
535
+ destroy_row > 0
536
+ else
537
+ true
538
+ end
173
539
  @destroyed = true
174
540
  freeze
175
541
  end
@@ -177,12 +543,12 @@ module ActiveRecord
177
543
  # Deletes the record in the database and freezes this instance to reflect
178
544
  # that no changes should be made (since they can't be persisted).
179
545
  #
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.
546
+ # There's a series of callbacks associated with #destroy!. If the
547
+ # <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
548
+ # and #destroy! raises ActiveRecord::RecordNotDestroyed.
549
+ # See ActiveRecord::Callbacks for further details.
184
550
  def destroy!
185
- destroy || raise(RecordNotDestroyed.new("Failed to destroy the record", self))
551
+ destroy || _raise_record_not_destroyed
186
552
  end
187
553
 
188
554
  # Returns an instance of the specified +klass+ with the attributes of the
@@ -194,19 +560,21 @@ module ActiveRecord
194
560
  # instance using the companies/company partial instead of clients/client.
195
561
  #
196
562
  # 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.
563
+ # Therefore the sti column value will still be the same.
564
+ # Any change to the attributes on either instance will affect both instances.
565
+ # If you want to change the sti column as well, use #becomes! instead.
198
566
  def becomes(klass)
199
- became = klass.new
567
+ became = klass.allocate
568
+ became.send(:initialize)
200
569
  became.instance_variable_set("@attributes", @attributes)
201
- changed_attributes = @changed_attributes if defined?(@changed_attributes)
202
- became.instance_variable_set("@changed_attributes", changed_attributes || {})
570
+ became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
203
571
  became.instance_variable_set("@new_record", new_record?)
204
572
  became.instance_variable_set("@destroyed", destroyed?)
205
- became.instance_variable_set("@errors", errors)
573
+ became.errors.copy!(errors)
206
574
  became
207
575
  end
208
576
 
209
- # Wrapper around +becomes+ that also changes the instance's sti column value.
577
+ # Wrapper around #becomes that also changes the instance's sti column value.
210
578
  # This is especially useful if you want to persist the changed class in your
211
579
  # database.
212
580
  #
@@ -226,18 +594,19 @@ module ActiveRecord
226
594
  # This is especially useful for boolean flags on existing records. Also note that
227
595
  #
228
596
  # * Validation is skipped.
229
- # * Callbacks are invoked.
597
+ # * \Callbacks are invoked.
230
598
  # * updated_at/updated_on column is updated if that column is available.
231
599
  # * Updates all the attributes that are dirty in this object.
232
600
  #
233
- # This method raises an +ActiveRecord::ActiveRecordError+ if the
601
+ # This method raises an ActiveRecord::ActiveRecordError if the
234
602
  # attribute is marked as readonly.
235
603
  #
236
- # See also +update_column+.
604
+ # Also see #update_column.
237
605
  def update_attribute(name, value)
238
606
  name = name.to_s
239
607
  verify_readonly_attribute(name)
240
- send("#{name}=", value)
608
+ public_send("#{name}=", value)
609
+
241
610
  save(validate: false)
242
611
  end
243
612
 
@@ -254,9 +623,10 @@ module ActiveRecord
254
623
  end
255
624
 
256
625
  alias update_attributes update
626
+ deprecate update_attributes: "please, use update instead"
257
627
 
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.
628
+ # Updates its receiver just like #update but calls #save! instead
629
+ # of +save+, so an exception is raised if the record is invalid and saving will fail.
260
630
  def update!(attributes)
261
631
  # The following transaction covers any possible database side-effects of the
262
632
  # attributes assignment. For example, setting the IDs of a child collection.
@@ -267,6 +637,7 @@ module ActiveRecord
267
637
  end
268
638
 
269
639
  alias update_attributes! update!
640
+ deprecate update_attributes!: "please, use update! instead"
270
641
 
271
642
  # Equivalent to <code>update_columns(name => value)</code>.
272
643
  def update_column(name, value)
@@ -282,27 +653,37 @@ module ActiveRecord
282
653
  # the database, but take into account that in consequence the regular update
283
654
  # procedures are totally bypassed. In particular:
284
655
  #
285
- # * Validations are skipped.
286
- # * Callbacks are skipped.
656
+ # * \Validations are skipped.
657
+ # * \Callbacks are skipped.
287
658
  # * +updated_at+/+updated_on+ are not updated.
659
+ # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
288
660
  #
289
- # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
661
+ # This method raises an ActiveRecord::ActiveRecordError when called on new
290
662
  # objects, or when at least one of the attributes is marked as readonly.
291
663
  def update_columns(attributes)
292
664
  raise ActiveRecordError, "cannot update a new record" if new_record?
293
665
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
294
666
 
295
- attributes.each_key do |key|
296
- verify_readonly_attribute(key.to_s)
667
+ attributes = attributes.transform_keys do |key|
668
+ name = key.to_s
669
+ self.class.attribute_aliases[name] || name
297
670
  end
298
671
 
299
- updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
672
+ attributes.each_key do |key|
673
+ verify_readonly_attribute(key)
674
+ end
300
675
 
676
+ id_in_database = self.id_in_database
301
677
  attributes.each do |k, v|
302
- raw_write_attribute(k, v)
678
+ write_attribute_without_type_cast(k, v)
303
679
  end
304
680
 
305
- updated_count == 1
681
+ affected_rows = self.class._update_record(
682
+ attributes,
683
+ @primary_key => id_in_database
684
+ )
685
+
686
+ affected_rows == 1
306
687
  end
307
688
 
308
689
  # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
@@ -314,42 +695,56 @@ module ActiveRecord
314
695
  self
315
696
  end
316
697
 
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])
698
+ # Wrapper around #increment that writes the update to the database.
699
+ # Only +attribute+ is updated; the record itself is not saved.
700
+ # This means that any other modified attributes will still be dirty.
701
+ # Validations and callbacks are skipped. Supports the +touch+ option from
702
+ # +update_counters+, see that for more.
703
+ # Returns +self+.
704
+ def increment!(attribute, by = 1, touch: nil)
705
+ increment(attribute, by)
706
+ change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0)
707
+ self.class.update_counters(id, attribute => change, touch: touch)
708
+ clear_attribute_change(attribute) # eww
709
+ self
323
710
  end
324
711
 
325
712
  # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
326
713
  # The decrement is performed directly on the underlying attribute, no setter is invoked.
327
714
  # Only makes sense for number-based attributes. Returns +self+.
328
715
  def decrement(attribute, by = 1)
329
- self[attribute] ||= 0
330
- self[attribute] -= by
331
- self
716
+ increment(attribute, -by)
332
717
  end
333
718
 
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])
719
+ # Wrapper around #decrement that writes the update to the database.
720
+ # Only +attribute+ is updated; the record itself is not saved.
721
+ # This means that any other modified attributes will still be dirty.
722
+ # Validations and callbacks are skipped. Supports the +touch+ option from
723
+ # +update_counters+, see that for more.
724
+ # Returns +self+.
725
+ def decrement!(attribute, by = 1, touch: nil)
726
+ increment!(attribute, -by, touch: touch)
340
727
  end
341
728
 
342
729
  # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
343
730
  # if the predicate returns +true+ the attribute will become +false+. This
344
731
  # method toggles directly the underlying value without calling any setter.
345
732
  # Returns +self+.
733
+ #
734
+ # Example:
735
+ #
736
+ # user = User.first
737
+ # user.banned? # => false
738
+ # user.toggle(:banned)
739
+ # user.banned? # => true
740
+ #
346
741
  def toggle(attribute)
347
- self[attribute] = !send("#{attribute}?")
742
+ self[attribute] = !public_send("#{attribute}?")
348
743
  self
349
744
  end
350
745
 
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.
746
+ # Wrapper around #toggle that saves the record. This method differs from
747
+ # its non-bang version in the sense that it passes through the attribute setter.
353
748
  # Saving is not subjected to validation checks. Returns +true+ if the
354
749
  # record could be saved.
355
750
  def toggle!(attribute)
@@ -358,8 +753,8 @@ module ActiveRecord
358
753
 
359
754
  # Reloads the record from the database.
360
755
  #
361
- # This method finds record by its primary key (which could be assigned manually) and
362
- # modifies the receiver in-place:
756
+ # This method finds the record by its primary key (which could be assigned
757
+ # manually) and modifies the receiver in-place:
363
758
  #
364
759
  # account = Account.new
365
760
  # # => #<Account id: nil, email: nil>
@@ -371,7 +766,7 @@ module ActiveRecord
371
766
  # Attributes are reloaded from the database, and caches busted, in
372
767
  # particular the associations cache and the QueryCache.
373
768
  #
374
- # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
769
+ # If the record no longer exists in the database ActiveRecord::RecordNotFound
375
770
  # is raised. Otherwise, in addition to the in-place modification the method
376
771
  # returns +self+ for convenience.
377
772
  #
@@ -405,8 +800,6 @@ module ActiveRecord
405
800
  # end
406
801
  #
407
802
  def reload(options = nil)
408
- clear_aggregation_cache
409
- clear_association_cache
410
803
  self.class.connection.clear_query_cache
411
804
 
412
805
  fresh_object =
@@ -416,24 +809,27 @@ module ActiveRecord
416
809
  self.class.unscoped { self.class.find(id) }
417
810
  end
418
811
 
419
- @attributes = fresh_object.instance_variable_get('@attributes')
812
+ @attributes = fresh_object.instance_variable_get("@attributes")
420
813
  @new_record = false
421
814
  self
422
815
  end
423
816
 
424
- # Saves the record with the updated_at/on attributes set to the current time.
817
+ # Saves the record with the updated_at/on attributes set to the current time
818
+ # or the time specified.
425
819
  # Please note that no validation is performed and only the +after_touch+,
426
820
  # +after_commit+ and +after_rollback+ callbacks are executed.
427
821
  #
822
+ # This method can be passed attribute names and an optional time argument.
428
823
  # If attribute names are passed, they are updated along with updated_at/on
429
- # attributes.
824
+ # attributes. If no time argument is passed, the current time is used as default.
430
825
  #
431
- # product.touch # updates updated_at/on
826
+ # product.touch # updates updated_at/on with current time
827
+ # product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time
432
828
  # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
433
829
  # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
434
830
  #
435
- # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on
436
- # associated object.
831
+ # If used along with {belongs_to}[rdoc-ref:Associations::ClassMethods#belongs_to]
832
+ # then +touch+ will invoke +touch+ method on associated object.
437
833
  #
438
834
  # class Brake < ActiveRecord::Base
439
835
  # belongs_to :car, touch: true
@@ -452,84 +848,119 @@ module ActiveRecord
452
848
  # ball = Ball.new
453
849
  # ball.touch(:updated_at) # => raises ActiveRecordError
454
850
  #
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
851
+ def touch(*names, time: nil)
852
+ unless persisted?
853
+ raise ActiveRecordError, <<-MSG.squish
854
+ cannot touch on a new or destroyed record object. Consider using
855
+ persisted?, new_record?, or destroyed? before touching
856
+ MSG
857
+ end
469
858
 
470
- changes[self.class.locking_column] = increment_lock if locking_enabled?
859
+ attribute_names = timestamp_attributes_for_update_in_model
860
+ attribute_names |= names.map!(&:to_s).map! { |name|
861
+ self.class.attribute_aliases[name] || name
862
+ }
471
863
 
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
864
+ unless attribute_names.empty?
865
+ affected_rows = _touch_row(attribute_names, time)
866
+ @_trigger_update_callback = affected_rows == 1
475
867
  else
476
868
  true
477
869
  end
478
870
  end
479
871
 
480
872
  private
481
-
482
873
  # A hook to be overridden by association modules.
483
874
  def destroy_associations
484
875
  end
485
876
 
486
877
  def destroy_row
487
- relation_for_destroy.delete_all
878
+ _delete_row
879
+ end
880
+
881
+ def _delete_row
882
+ self.class._delete_record(@primary_key => id_in_database)
488
883
  end
489
884
 
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)
885
+ def _touch_row(attribute_names, time)
886
+ time ||= current_time_from_proper_timezone
494
887
 
495
- relation = self.class.unscoped.where(
496
- self.class.arel_table[pk].eq(substitute))
888
+ attribute_names.each do |attr_name|
889
+ _write_attribute(attr_name, time)
890
+ end
891
+
892
+ _update_row(attribute_names, "touch")
893
+ end
497
894
 
498
- relation.bind_values = [[column, id]]
499
- relation
895
+ def _update_row(attribute_names, attempted_action = "update")
896
+ self.class._update_record(
897
+ attributes_with_values(attribute_names),
898
+ @primary_key => id_in_database
899
+ )
500
900
  end
501
901
 
502
- def create_or_update
503
- raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
504
- result = new_record? ? _create_record : _update_record
902
+ def create_or_update(**, &block)
903
+ _raise_readonly_record_error if readonly?
904
+ return false if destroyed?
905
+ result = new_record? ? _create_record(&block) : _update_record(&block)
505
906
  result != false
506
907
  end
507
908
 
508
909
  # Updates the associated record with values matching those of the instance attributes.
509
910
  # Returns the number of affected rows.
510
911
  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
912
+ attribute_names = attributes_for_update(attribute_names)
913
+
914
+ if attribute_names.empty?
915
+ affected_rows = 0
916
+ @_trigger_update_callback = true
514
917
  else
515
- self.class.unscoped._update_record attributes_values, id, id_was
918
+ affected_rows = _update_row(attribute_names)
919
+ @_trigger_update_callback = affected_rows == 1
516
920
  end
921
+
922
+ yield(self) if block_given?
923
+
924
+ affected_rows
517
925
  end
518
926
 
519
927
  # Creates a record with values matching those of the instance attributes
520
928
  # and returns its id.
521
929
  def _create_record(attribute_names = self.attribute_names)
522
- attributes_values = arel_attributes_with_values_for_create(attribute_names)
930
+ attribute_names = attributes_for_create(attribute_names)
931
+
932
+ new_id = self.class._insert_record(
933
+ attributes_with_values(attribute_names)
934
+ )
523
935
 
524
- new_id = self.class.unscoped.insert attributes_values
525
- self.id ||= new_id if self.class.primary_key
936
+ self.id ||= new_id if @primary_key
526
937
 
527
938
  @new_record = false
939
+
940
+ yield(self) if block_given?
941
+
528
942
  id
529
943
  end
530
944
 
531
945
  def verify_readonly_attribute(name)
532
946
  raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
533
947
  end
948
+
949
+ def _raise_record_not_destroyed
950
+ @_association_destroy_exception ||= nil
951
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
952
+ ensure
953
+ @_association_destroy_exception = nil
954
+ end
955
+
956
+ # The name of the method used to touch a +belongs_to+ association when the
957
+ # +:touch+ option is used.
958
+ def belongs_to_touch_method
959
+ :touch
960
+ end
961
+
962
+ def _raise_readonly_record_error
963
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly"
964
+ end
534
965
  end
535
966
  end