activerecord 5.2.8 → 7.0.2

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 (364) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1393 -587
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +10 -9
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +122 -47
  10. data/lib/active_record/associations/association_scope.rb +24 -24
  11. data/lib/active_record/associations/belongs_to_association.rb +67 -49
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
  13. data/lib/active_record/associations/builder/association.rb +52 -23
  14. data/lib/active_record/associations/builder/belongs_to.rb +44 -61
  15. data/lib/active_record/associations/builder/collection_association.rb +17 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +10 -3
  18. data/lib/active_record/associations/builder/has_one.rb +35 -3
  19. data/lib/active_record/associations/builder/singular_association.rb +5 -3
  20. data/lib/active_record/associations/collection_association.rb +59 -50
  21. data/lib/active_record/associations/collection_proxy.rb +32 -23
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +27 -14
  25. data/lib/active_record/associations/has_many_through_association.rb +26 -19
  26. data/lib/active_record/associations/has_one_association.rb +52 -37
  27. data/lib/active_record/associations/has_one_through_association.rb +6 -6
  28. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +97 -62
  31. data/lib/active_record/associations/preloader/association.rb +220 -60
  32. data/lib/active_record/associations/preloader/batch.rb +48 -0
  33. data/lib/active_record/associations/preloader/branch.rb +147 -0
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -40
  35. data/lib/active_record/associations/preloader.rb +44 -105
  36. data/lib/active_record/associations/singular_association.rb +9 -17
  37. data/lib/active_record/associations/through_association.rb +4 -4
  38. data/lib/active_record/associations.rb +207 -66
  39. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  40. data/lib/active_record/attribute_assignment.rb +17 -19
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
  42. data/lib/active_record/attribute_methods/dirty.rb +141 -47
  43. data/lib/active_record/attribute_methods/primary_key.rb +22 -27
  44. data/lib/active_record/attribute_methods/query.rb +6 -10
  45. data/lib/active_record/attribute_methods/read.rb +15 -55
  46. data/lib/active_record/attribute_methods/serialization.rb +77 -18
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
  48. data/lib/active_record/attribute_methods/write.rb +18 -37
  49. data/lib/active_record/attribute_methods.rb +90 -153
  50. data/lib/active_record/attributes.rb +38 -12
  51. data/lib/active_record/autosave_association.rb +50 -50
  52. data/lib/active_record/base.rb +23 -18
  53. data/lib/active_record/callbacks.rb +159 -44
  54. data/lib/active_record/coders/yaml_column.rb +12 -3
  55. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
  63. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
  71. data/lib/active_record/connection_adapters/column.rb +33 -11
  72. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  73. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  74. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  75. data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
  76. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  77. data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
  78. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  80. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
  81. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
  82. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  83. data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
  84. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  85. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
  95. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
  116. data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
  122. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
  123. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  124. data/lib/active_record/connection_adapters.rb +53 -0
  125. data/lib/active_record/connection_handling.rb +292 -38
  126. data/lib/active_record/core.rb +385 -158
  127. data/lib/active_record/counter_cache.rb +8 -30
  128. data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
  129. data/lib/active_record/database_configurations/database_config.rb +83 -0
  130. data/lib/active_record/database_configurations/hash_config.rb +154 -0
  131. data/lib/active_record/database_configurations/url_config.rb +53 -0
  132. data/lib/active_record/database_configurations.rb +256 -0
  133. data/lib/active_record/delegated_type.rb +250 -0
  134. data/lib/active_record/destroy_association_async_job.rb +36 -0
  135. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  136. data/lib/active_record/dynamic_matchers.rb +4 -5
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +44 -0
  140. data/lib/active_record/encryption/configurable.rb +61 -0
  141. data/lib/active_record/encryption/context.rb +35 -0
  142. data/lib/active_record/encryption/contexts.rb +72 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +155 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +42 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_serializer.rb +90 -0
  159. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  160. data/lib/active_record/encryption/properties.rb +76 -0
  161. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  162. data/lib/active_record/encryption/scheme.rb +99 -0
  163. data/lib/active_record/encryption.rb +55 -0
  164. data/lib/active_record/enum.rb +130 -51
  165. data/lib/active_record/errors.rb +129 -23
  166. data/lib/active_record/explain.rb +10 -6
  167. data/lib/active_record/explain_registry.rb +11 -6
  168. data/lib/active_record/explain_subscriber.rb +1 -1
  169. data/lib/active_record/fixture_set/file.rb +22 -15
  170. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  171. data/lib/active_record/fixture_set/render_context.rb +17 -0
  172. data/lib/active_record/fixture_set/table_row.rb +187 -0
  173. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  174. data/lib/active_record/fixtures.rb +206 -490
  175. data/lib/active_record/future_result.rb +139 -0
  176. data/lib/active_record/gem_version.rb +3 -3
  177. data/lib/active_record/inheritance.rb +104 -37
  178. data/lib/active_record/insert_all.rb +278 -0
  179. data/lib/active_record/integration.rb +69 -18
  180. data/lib/active_record/internal_metadata.rb +24 -9
  181. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  182. data/lib/active_record/locking/optimistic.rb +41 -26
  183. data/lib/active_record/locking/pessimistic.rb +18 -8
  184. data/lib/active_record/log_subscriber.rb +46 -35
  185. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  186. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  187. data/lib/active_record/middleware/database_selector.rb +82 -0
  188. data/lib/active_record/middleware/shard_selector.rb +60 -0
  189. data/lib/active_record/migration/command_recorder.rb +96 -44
  190. data/lib/active_record/migration/compatibility.rb +246 -64
  191. data/lib/active_record/migration/join_table.rb +1 -2
  192. data/lib/active_record/migration.rb +266 -187
  193. data/lib/active_record/model_schema.rb +165 -52
  194. data/lib/active_record/nested_attributes.rb +17 -19
  195. data/lib/active_record/no_touching.rb +11 -4
  196. data/lib/active_record/null_relation.rb +2 -7
  197. data/lib/active_record/persistence.rb +467 -92
  198. data/lib/active_record/query_cache.rb +21 -4
  199. data/lib/active_record/query_logs.rb +138 -0
  200. data/lib/active_record/querying.rb +51 -24
  201. data/lib/active_record/railtie.rb +224 -57
  202. data/lib/active_record/railties/console_sandbox.rb +2 -4
  203. data/lib/active_record/railties/controller_runtime.rb +31 -36
  204. data/lib/active_record/railties/databases.rake +369 -101
  205. data/lib/active_record/readonly_attributes.rb +15 -0
  206. data/lib/active_record/reflection.rb +170 -137
  207. data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
  208. data/lib/active_record/relation/batches.rb +46 -37
  209. data/lib/active_record/relation/calculations.rb +168 -96
  210. data/lib/active_record/relation/delegation.rb +37 -52
  211. data/lib/active_record/relation/finder_methods.rb +79 -58
  212. data/lib/active_record/relation/from_clause.rb +5 -1
  213. data/lib/active_record/relation/merger.rb +50 -51
  214. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  215. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  216. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  217. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  218. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  219. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  220. data/lib/active_record/relation/predicate_builder.rb +58 -46
  221. data/lib/active_record/relation/query_attribute.rb +9 -10
  222. data/lib/active_record/relation/query_methods.rb +685 -208
  223. data/lib/active_record/relation/record_fetch_warning.rb +9 -11
  224. data/lib/active_record/relation/spawn_methods.rb +10 -10
  225. data/lib/active_record/relation/where_clause.rb +108 -64
  226. data/lib/active_record/relation.rb +515 -151
  227. data/lib/active_record/result.rb +78 -42
  228. data/lib/active_record/runtime_registry.rb +9 -13
  229. data/lib/active_record/sanitization.rb +29 -44
  230. data/lib/active_record/schema.rb +37 -31
  231. data/lib/active_record/schema_dumper.rb +74 -23
  232. data/lib/active_record/schema_migration.rb +7 -9
  233. data/lib/active_record/scoping/default.rb +62 -17
  234. data/lib/active_record/scoping/named.rb +17 -32
  235. data/lib/active_record/scoping.rb +70 -41
  236. data/lib/active_record/secure_token.rb +16 -8
  237. data/lib/active_record/serialization.rb +6 -4
  238. data/lib/active_record/signed_id.rb +116 -0
  239. data/lib/active_record/statement_cache.rb +49 -6
  240. data/lib/active_record/store.rb +88 -9
  241. data/lib/active_record/suppressor.rb +13 -17
  242. data/lib/active_record/table_metadata.rb +42 -43
  243. data/lib/active_record/tasks/database_tasks.rb +352 -94
  244. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  245. data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
  246. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  247. data/lib/active_record/test_databases.rb +24 -0
  248. data/lib/active_record/test_fixtures.rb +287 -0
  249. data/lib/active_record/timestamp.rb +44 -34
  250. data/lib/active_record/touch_later.rb +23 -22
  251. data/lib/active_record/transactions.rb +67 -128
  252. data/lib/active_record/translation.rb +3 -3
  253. data/lib/active_record/type/adapter_specific_registry.rb +34 -19
  254. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  255. data/lib/active_record/type/internal/timezone.rb +2 -2
  256. data/lib/active_record/type/serialized.rb +7 -4
  257. data/lib/active_record/type/time.rb +10 -0
  258. data/lib/active_record/type/type_map.rb +17 -21
  259. data/lib/active_record/type/unsigned_integer.rb +0 -1
  260. data/lib/active_record/type.rb +9 -5
  261. data/lib/active_record/type_caster/connection.rb +15 -15
  262. data/lib/active_record/type_caster/map.rb +8 -8
  263. data/lib/active_record/validations/associated.rb +2 -3
  264. data/lib/active_record/validations/numericality.rb +35 -0
  265. data/lib/active_record/validations/uniqueness.rb +39 -31
  266. data/lib/active_record/validations.rb +4 -3
  267. data/lib/active_record.rb +209 -32
  268. data/lib/arel/alias_predication.rb +9 -0
  269. data/lib/arel/attributes/attribute.rb +33 -0
  270. data/lib/arel/collectors/bind.rb +29 -0
  271. data/lib/arel/collectors/composite.rb +39 -0
  272. data/lib/arel/collectors/plain_string.rb +20 -0
  273. data/lib/arel/collectors/sql_string.rb +27 -0
  274. data/lib/arel/collectors/substitute_binds.rb +35 -0
  275. data/lib/arel/crud.rb +48 -0
  276. data/lib/arel/delete_manager.rb +32 -0
  277. data/lib/arel/errors.rb +9 -0
  278. data/lib/arel/expressions.rb +29 -0
  279. data/lib/arel/factory_methods.rb +49 -0
  280. data/lib/arel/filter_predications.rb +9 -0
  281. data/lib/arel/insert_manager.rb +48 -0
  282. data/lib/arel/math.rb +45 -0
  283. data/lib/arel/nodes/and.rb +32 -0
  284. data/lib/arel/nodes/ascending.rb +23 -0
  285. data/lib/arel/nodes/binary.rb +126 -0
  286. data/lib/arel/nodes/bind_param.rb +44 -0
  287. data/lib/arel/nodes/case.rb +55 -0
  288. data/lib/arel/nodes/casted.rb +62 -0
  289. data/lib/arel/nodes/comment.rb +29 -0
  290. data/lib/arel/nodes/count.rb +12 -0
  291. data/lib/arel/nodes/delete_statement.rb +44 -0
  292. data/lib/arel/nodes/descending.rb +23 -0
  293. data/lib/arel/nodes/equality.rb +15 -0
  294. data/lib/arel/nodes/extract.rb +24 -0
  295. data/lib/arel/nodes/false.rb +16 -0
  296. data/lib/arel/nodes/filter.rb +10 -0
  297. data/lib/arel/nodes/full_outer_join.rb +8 -0
  298. data/lib/arel/nodes/function.rb +45 -0
  299. data/lib/arel/nodes/grouping.rb +11 -0
  300. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  301. data/lib/arel/nodes/in.rb +15 -0
  302. data/lib/arel/nodes/infix_operation.rb +92 -0
  303. data/lib/arel/nodes/inner_join.rb +8 -0
  304. data/lib/arel/nodes/insert_statement.rb +37 -0
  305. data/lib/arel/nodes/join_source.rb +20 -0
  306. data/lib/arel/nodes/matches.rb +18 -0
  307. data/lib/arel/nodes/named_function.rb +23 -0
  308. data/lib/arel/nodes/node.rb +51 -0
  309. data/lib/arel/nodes/node_expression.rb +13 -0
  310. data/lib/arel/nodes/ordering.rb +27 -0
  311. data/lib/arel/nodes/outer_join.rb +8 -0
  312. data/lib/arel/nodes/over.rb +15 -0
  313. data/lib/arel/nodes/regexp.rb +16 -0
  314. data/lib/arel/nodes/right_outer_join.rb +8 -0
  315. data/lib/arel/nodes/select_core.rb +67 -0
  316. data/lib/arel/nodes/select_statement.rb +41 -0
  317. data/lib/arel/nodes/sql_literal.rb +19 -0
  318. data/lib/arel/nodes/string_join.rb +11 -0
  319. data/lib/arel/nodes/table_alias.rb +31 -0
  320. data/lib/arel/nodes/terminal.rb +16 -0
  321. data/lib/arel/nodes/true.rb +16 -0
  322. data/lib/arel/nodes/unary.rb +44 -0
  323. data/lib/arel/nodes/unary_operation.rb +20 -0
  324. data/lib/arel/nodes/unqualified_column.rb +22 -0
  325. data/lib/arel/nodes/update_statement.rb +46 -0
  326. data/lib/arel/nodes/values_list.rb +9 -0
  327. data/lib/arel/nodes/window.rb +126 -0
  328. data/lib/arel/nodes/with.rb +11 -0
  329. data/lib/arel/nodes.rb +71 -0
  330. data/lib/arel/order_predications.rb +13 -0
  331. data/lib/arel/predications.rb +258 -0
  332. data/lib/arel/select_manager.rb +276 -0
  333. data/lib/arel/table.rb +117 -0
  334. data/lib/arel/tree_manager.rb +60 -0
  335. data/lib/arel/update_manager.rb +48 -0
  336. data/lib/arel/visitors/dot.rb +298 -0
  337. data/lib/arel/visitors/mysql.rb +99 -0
  338. data/lib/arel/visitors/postgresql.rb +110 -0
  339. data/lib/arel/visitors/sqlite.rb +38 -0
  340. data/lib/arel/visitors/to_sql.rb +955 -0
  341. data/lib/arel/visitors/visitor.rb +45 -0
  342. data/lib/arel/visitors.rb +13 -0
  343. data/lib/arel/window_predications.rb +9 -0
  344. data/lib/arel.rb +55 -0
  345. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  346. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  347. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  348. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  349. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  350. data/lib/rails/generators/active_record/migration.rb +19 -2
  351. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  352. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  353. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  354. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  355. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  356. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  357. metadata +162 -32
  358. data/lib/active_record/attribute_decorators.rb +0 -90
  359. data/lib/active_record/collection_cache_key.rb +0 -53
  360. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  361. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  362. data/lib/active_record/define_callbacks.rb +0 -22
  363. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  364. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/insert_all"
4
+
3
5
  module ActiveRecord
4
6
  # = Active Record \Persistence
5
7
  module Persistence
@@ -55,6 +57,283 @@ module ActiveRecord
55
57
  end
56
58
  end
57
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, record_timestamps: nil)
67
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
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
+ # You can also pass an SQL string if you need more control on the return values
95
+ # (for example, <tt>returning: "id, name as new_name"</tt>).
96
+ #
97
+ # [:unique_by]
98
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
99
+ # by every unique index on the table. Any duplicate rows are skipped.
100
+ #
101
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
102
+ #
103
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
104
+ # row has an existing id, or is not unique by another unique index,
105
+ # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
106
+ #
107
+ # Unique indexes can be identified by columns or name:
108
+ #
109
+ # unique_by: :isbn
110
+ # unique_by: %i[ author_id name ]
111
+ # unique_by: :index_books_on_isbn
112
+ #
113
+ # [:record_timestamps]
114
+ # By default, automatic setting of timestamp columns is controlled by
115
+ # the model's <tt>record_timestamps</tt> config, matching typical
116
+ # behavior.
117
+ #
118
+ # To override this and force automatic setting of timestamp columns one
119
+ # way or the other, pass <tt>:record_timestamps</tt>:
120
+ #
121
+ # record_timestamps: true # Always set timestamps automatically
122
+ # record_timestamps: false # Never set timestamps automatically
123
+ #
124
+ # Because it relies on the index information from the database
125
+ # <tt>:unique_by</tt> is recommended to be paired with
126
+ # Active Record's schema_cache.
127
+ #
128
+ # ==== Example
129
+ #
130
+ # # Insert records and skip inserting any duplicates.
131
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
132
+ #
133
+ # Book.insert_all([
134
+ # { id: 1, title: "Rework", author: "David" },
135
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
136
+ # ])
137
+ #
138
+ # # insert_all works on chained scopes, and you can use create_with
139
+ # # to set default attributes for all inserted records.
140
+ #
141
+ # author.books.create_with(created_at: Time.now).insert_all([
142
+ # { id: 1, title: "Rework" },
143
+ # { id: 2, title: "Eloquent Ruby" }
144
+ # ])
145
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
146
+ InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
147
+ end
148
+
149
+ # Inserts a single record into the database in a single SQL INSERT
150
+ # statement. It does not instantiate any models nor does it trigger
151
+ # Active Record callbacks or validations. Though passed values
152
+ # go through Active Record's type casting and serialization.
153
+ #
154
+ # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
155
+ def insert!(attributes, returning: nil, record_timestamps: nil)
156
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
157
+ end
158
+
159
+ # Inserts multiple records into the database in a single SQL INSERT
160
+ # statement. It does not instantiate any models nor does it trigger
161
+ # Active Record callbacks or validations. Though passed values
162
+ # go through Active Record's type casting and serialization.
163
+ #
164
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
165
+ # the attributes for a single row and must have the same keys.
166
+ #
167
+ # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
168
+ # unique index on the table. In that case, no rows are inserted.
169
+ #
170
+ # To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
171
+ # To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
172
+ #
173
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
174
+ # <tt>:returning</tt> (see below).
175
+ #
176
+ # ==== Options
177
+ #
178
+ # [:returning]
179
+ # (PostgreSQL only) An array of attributes to return for all successfully
180
+ # inserted records, which by default is the primary key.
181
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
182
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
183
+ # clause entirely.
184
+ #
185
+ # You can also pass an SQL string if you need more control on the return values
186
+ # (for example, <tt>returning: "id, name as new_name"</tt>).
187
+ #
188
+ # [:record_timestamps]
189
+ # By default, automatic setting of timestamp columns is controlled by
190
+ # the model's <tt>record_timestamps</tt> config, matching typical
191
+ # behavior.
192
+ #
193
+ # To override this and force automatic setting of timestamp columns one
194
+ # way or the other, pass <tt>:record_timestamps</tt>:
195
+ #
196
+ # record_timestamps: true # Always set timestamps automatically
197
+ # record_timestamps: false # Never set timestamps automatically
198
+ #
199
+ # ==== Examples
200
+ #
201
+ # # Insert multiple records
202
+ # Book.insert_all!([
203
+ # { title: "Rework", author: "David" },
204
+ # { title: "Eloquent Ruby", author: "Russ" }
205
+ # ])
206
+ #
207
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
208
+ # # does not have a unique id.
209
+ # Book.insert_all!([
210
+ # { id: 1, title: "Rework", author: "David" },
211
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
212
+ # ])
213
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
214
+ InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
215
+ end
216
+
217
+ # Updates or inserts (upserts) a single record into the database in a
218
+ # single SQL INSERT statement. It does not instantiate any models nor does
219
+ # it trigger Active Record callbacks or validations. Though passed values
220
+ # go through Active Record's type casting and serialization.
221
+ #
222
+ # See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
223
+ def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
224
+ upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
225
+ end
226
+
227
+ # Updates or inserts (upserts) multiple records into the database in a
228
+ # single SQL INSERT statement. It does not instantiate any models nor does
229
+ # it trigger Active Record callbacks or validations. Though passed values
230
+ # go through Active Record's type casting and serialization.
231
+ #
232
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
233
+ # the attributes for a single row and must have the same keys.
234
+ #
235
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
236
+ # <tt>:returning</tt> (see below).
237
+ #
238
+ # By default, +upsert_all+ will update all the columns that can be updated when
239
+ # there is a conflict. These are all the columns except primary keys, read-only
240
+ # columns, and columns covered by the optional +unique_by+.
241
+ #
242
+ # ==== Options
243
+ #
244
+ # [:returning]
245
+ # (PostgreSQL only) An array of attributes to return for all successfully
246
+ # inserted records, which by default is the primary key.
247
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
248
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
249
+ # clause entirely.
250
+ #
251
+ # You can also pass an SQL string if you need more control on the return values
252
+ # (for example, <tt>returning: "id, name as new_name"</tt>).
253
+ #
254
+ # [:unique_by]
255
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
256
+ # by every unique index on the table. Any duplicate rows are skipped.
257
+ #
258
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
259
+ #
260
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
261
+ # row has an existing id, or is not unique by another unique index,
262
+ # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
263
+ #
264
+ # Unique indexes can be identified by columns or name:
265
+ #
266
+ # unique_by: :isbn
267
+ # unique_by: %i[ author_id name ]
268
+ # unique_by: :index_books_on_isbn
269
+ #
270
+ # Because it relies on the index information from the database
271
+ # <tt>:unique_by</tt> is recommended to be paired with
272
+ # Active Record's schema_cache.
273
+ #
274
+ # [:on_duplicate]
275
+ # Configure the SQL update sentence that will be used in case of conflict.
276
+ #
277
+ # NOTE: If you use this option you must provide all the columns you want to update
278
+ # by yourself.
279
+ #
280
+ # Example:
281
+ #
282
+ # Commodity.upsert_all(
283
+ # [
284
+ # { id: 2, name: "Copper", price: 4.84 },
285
+ # { id: 4, name: "Gold", price: 1380.87 },
286
+ # { id: 6, name: "Aluminium", price: 0.35 }
287
+ # ],
288
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
289
+ # )
290
+ #
291
+ # See the related +:update_only+ option. Both options can't be used at the same time.
292
+ #
293
+ # [:update_only]
294
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
295
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
296
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
297
+ #
298
+ # Example:
299
+ #
300
+ # Commodity.upsert_all(
301
+ # [
302
+ # { id: 2, name: "Copper", price: 4.84 },
303
+ # { id: 4, name: "Gold", price: 1380.87 },
304
+ # { id: 6, name: "Aluminium", price: 0.35 }
305
+ # ],
306
+ # update_only: [:price] # Only prices will be updated
307
+ # )
308
+ #
309
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
310
+ #
311
+ # [:record_timestamps]
312
+ # By default, automatic setting of timestamp columns is controlled by
313
+ # the model's <tt>record_timestamps</tt> config, matching typical
314
+ # behavior.
315
+ #
316
+ # To override this and force automatic setting of timestamp columns one
317
+ # way or the other, pass <tt>:record_timestamps</tt>:
318
+ #
319
+ # record_timestamps: true # Always set timestamps automatically
320
+ # record_timestamps: false # Never set timestamps automatically
321
+ #
322
+ # ==== Examples
323
+ #
324
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
325
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
326
+ #
327
+ # Book.upsert_all([
328
+ # { title: "Rework", author: "David", isbn: "1" },
329
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
330
+ # ], unique_by: :isbn)
331
+ #
332
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
333
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
334
+ InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
335
+ end
336
+
58
337
  # Given an attributes hash, +instantiate+ returns a new instance of
59
338
  # the appropriate class. Accepts only keys as strings.
60
339
  #
@@ -67,8 +346,7 @@ module ActiveRecord
67
346
  # how this "single-table" inheritance mapping is implemented.
68
347
  def instantiate(attributes, column_types = {}, &block)
69
348
  klass = discriminate_class_for_record(attributes)
70
- attributes = klass.attributes_builder.build_from_database(attributes, column_types)
71
- klass.allocate.init_with("attributes" => attributes, "new_record" => false, &block)
349
+ instantiate_instance_of(klass, attributes, column_types, &block)
72
350
  end
73
351
 
74
352
  # Updates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -77,6 +355,7 @@ module ActiveRecord
77
355
  # ==== Parameters
78
356
  #
79
357
  # * +id+ - This should be the id or an array of ids to be updated.
358
+ # Optional argument, defaults to all records in the relation.
80
359
  # * +attributes+ - This should be a hash of attributes or an array of hashes.
81
360
  #
82
361
  # ==== Examples
@@ -99,6 +378,11 @@ module ActiveRecord
99
378
  # for updating all records in a single query.
100
379
  def update(id = :all, attributes)
101
380
  if id.is_a?(Array)
381
+ if id.any?(ActiveRecord::Base)
382
+ raise ArgumentError,
383
+ "You are passing an array of ActiveRecord::Base instances to `update`. " \
384
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
385
+ end
102
386
  id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
103
387
  object.update(attributes[idx])
104
388
  }
@@ -116,6 +400,32 @@ module ActiveRecord
116
400
  end
117
401
  end
118
402
 
403
+ # Updates the object (or multiple objects) just like #update but calls #update! instead
404
+ # of +update+, so an exception is raised if the record is invalid and saving will fail.
405
+ def update!(id = :all, attributes)
406
+ if id.is_a?(Array)
407
+ if id.any?(ActiveRecord::Base)
408
+ raise ArgumentError,
409
+ "You are passing an array of ActiveRecord::Base instances to `update!`. " \
410
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
411
+ end
412
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
413
+ object.update!(attributes[idx])
414
+ }
415
+ elsif id == :all
416
+ all.each { |record| record.update!(attributes) }
417
+ else
418
+ if ActiveRecord::Base === id
419
+ raise ArgumentError,
420
+ "You are passing an instance of ActiveRecord::Base to `update!`. " \
421
+ "Please pass the id of the object by calling `.id`."
422
+ end
423
+ object = find(id)
424
+ object.update!(attributes)
425
+ object
426
+ end
427
+ end
428
+
119
429
  # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
120
430
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
121
431
  # less efficient than #delete but allows cleanup methods and other actions to be run.
@@ -143,7 +453,7 @@ module ActiveRecord
143
453
  end
144
454
  end
145
455
 
146
- # Deletes the row with a primary key matching the +id+ argument, using a
456
+ # Deletes the row with a primary key matching the +id+ argument, using an
147
457
  # SQL +DELETE+ statement, and returns the number of rows deleted. Active
148
458
  # Record objects are not instantiated, so the object's callbacks are not
149
459
  # executed, including any <tt>:dependent</tt> association options.
@@ -162,52 +472,72 @@ module ActiveRecord
162
472
  # # Delete multiple rows
163
473
  # Todo.delete([2,3,4])
164
474
  def delete(id_or_array)
165
- where(primary_key => id_or_array).delete_all
475
+ delete_by(primary_key => id_or_array)
166
476
  end
167
477
 
168
478
  def _insert_record(values) # :nodoc:
479
+ primary_key = self.primary_key
169
480
  primary_key_value = nil
170
481
 
171
- if primary_key && Hash === values
172
- primary_key_value = values[primary_key]
173
-
174
- if !primary_key_value && prefetch_primary_key?
482
+ if prefetch_primary_key? && primary_key
483
+ values[primary_key] ||= begin
175
484
  primary_key_value = next_sequence_value
176
- values[primary_key] = primary_key_value
485
+ _default_attributes[primary_key].with_cast_value(primary_key_value)
177
486
  end
178
487
  end
179
488
 
489
+ im = Arel::InsertManager.new(arel_table)
490
+
180
491
  if values.empty?
181
- im = arel_table.compile_insert(connection.empty_insert_statement_value)
182
- im.into arel_table
492
+ im.insert(connection.empty_insert_statement_value(primary_key))
183
493
  else
184
- im = arel_table.compile_insert(_substitute_values(values))
494
+ im.insert(values.transform_keys { |name| arel_table[name] })
185
495
  end
186
496
 
187
497
  connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
188
498
  end
189
499
 
190
500
  def _update_record(values, constraints) # :nodoc:
191
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
501
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
502
+
503
+ default_constraint = build_default_constraint
504
+ constraints << default_constraint if default_constraint
192
505
 
193
- um = arel_table.where(
194
- constraints.reduce(&:and)
195
- ).compile_update(_substitute_values(values), primary_key)
506
+ if current_scope = self.global_current_scope
507
+ constraints << current_scope.where_clause.ast
508
+ end
509
+
510
+ um = Arel::UpdateManager.new(arel_table)
511
+ um.set(values.transform_keys { |name| arel_table[name] })
512
+ um.wheres = constraints
196
513
 
197
514
  connection.update(um, "#{self} Update")
198
515
  end
199
516
 
200
517
  def _delete_record(constraints) # :nodoc:
201
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
518
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
519
+
520
+ default_constraint = build_default_constraint
521
+ constraints << default_constraint if default_constraint
522
+
523
+ if current_scope = self.global_current_scope
524
+ constraints << current_scope.where_clause.ast
525
+ end
202
526
 
203
- dm = Arel::DeleteManager.new
204
- dm.from(arel_table)
527
+ dm = Arel::DeleteManager.new(arel_table)
205
528
  dm.wheres = constraints
206
529
 
207
530
  connection.delete(dm, "#{self} Destroy")
208
531
  end
209
532
 
210
533
  private
534
+ # Given a class, an attributes hash, +instantiate_instance_of+ returns a
535
+ # new instance of the class. Accepts only keys as strings.
536
+ def instantiate_instance_of(klass, attributes, column_types = {}, &block)
537
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
538
+ klass.allocate.init_with_attributes(attributes, &block)
539
+ end
540
+
211
541
  # Called by +instantiate+ to decide which class to use for a new
212
542
  # record instance.
213
543
  #
@@ -217,38 +547,49 @@ module ActiveRecord
217
547
  self
218
548
  end
219
549
 
220
- def _substitute_values(values)
221
- values.map do |name, value|
222
- attr = arel_attribute(name)
223
- bind = predicate_builder.build_bind_attribute(name, value)
224
- [attr, bind]
225
- end
550
+ # Called by +_update_record+ and +_delete_record+
551
+ # to build `where` clause from default scopes.
552
+ # Skips empty scopes.
553
+ def build_default_constraint
554
+ return unless default_scopes?(all_queries: true)
555
+
556
+ default_where_clause = default_scoped(all_queries: true).where_clause
557
+ default_where_clause.ast unless default_where_clause.empty?
226
558
  end
227
559
  end
228
560
 
229
561
  # Returns true if this object hasn't been saved yet -- that is, a record
230
562
  # for the object doesn't exist in the database yet; otherwise, returns false.
231
563
  def new_record?
232
- sync_with_transaction_state
233
564
  @new_record
234
565
  end
235
566
 
567
+ # Returns true if this object was just created -- that is, prior to the last
568
+ # save, the object didn't exist in the database and new_record? would have
569
+ # returned true.
570
+ def previously_new_record?
571
+ @previously_new_record
572
+ end
573
+
574
+ # Returns true if this object was previously persisted but now it has been deleted.
575
+ def previously_persisted?
576
+ !new_record? && destroyed?
577
+ end
578
+
236
579
  # Returns true if this object has been destroyed, otherwise returns false.
237
580
  def destroyed?
238
- sync_with_transaction_state
239
581
  @destroyed
240
582
  end
241
583
 
242
584
  # Returns true if the record is persisted, i.e. it's not a new record and it was
243
585
  # not destroyed, otherwise returns false.
244
586
  def persisted?
245
- sync_with_transaction_state
246
587
  !(@new_record || @destroyed)
247
588
  end
248
589
 
249
590
  ##
250
591
  # :call-seq:
251
- # save(*args)
592
+ # save(**options)
252
593
  #
253
594
  # Saves the model.
254
595
  #
@@ -271,15 +612,15 @@ module ActiveRecord
271
612
  #
272
613
  # Attributes marked as readonly are silently ignored if the record is
273
614
  # being updated.
274
- def save(*args, &block)
275
- create_or_update(*args, &block)
615
+ def save(**options, &block)
616
+ create_or_update(**options, &block)
276
617
  rescue ActiveRecord::RecordInvalid
277
618
  false
278
619
  end
279
620
 
280
621
  ##
281
622
  # :call-seq:
282
- # save!(*args)
623
+ # save!(**options)
283
624
  #
284
625
  # Saves the model.
285
626
  #
@@ -304,8 +645,8 @@ module ActiveRecord
304
645
  # being updated.
305
646
  #
306
647
  # Unless an error is raised, returns true.
307
- def save!(*args, &block)
308
- create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
648
+ def save!(**options, &block)
649
+ create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
309
650
  end
310
651
 
311
652
  # Deletes the record in the database and freezes this instance to
@@ -319,7 +660,7 @@ module ActiveRecord
319
660
  #
320
661
  # To enforce the object's +before_destroy+ and +after_destroy+
321
662
  # callbacks or any <tt>:dependent</tt> association
322
- # options, use <tt>#destroy</tt>.
663
+ # options, use #destroy.
323
664
  def delete
324
665
  _delete_row if persisted?
325
666
  @destroyed = true
@@ -336,7 +677,6 @@ module ActiveRecord
336
677
  def destroy
337
678
  _raise_readonly_record_error if readonly?
338
679
  destroy_associations
339
- self.class.connection.add_transaction_record(self)
340
680
  @_trigger_destroy_callback = if persisted?
341
681
  destroy_row > 0
342
682
  else
@@ -358,34 +698,36 @@ module ActiveRecord
358
698
  end
359
699
 
360
700
  # Returns an instance of the specified +klass+ with the attributes of the
361
- # current record. This is mostly useful in relation to single-table
362
- # inheritance structures where you want a subclass to appear as the
701
+ # current record. This is mostly useful in relation to single table
702
+ # inheritance (STI) structures where you want a subclass to appear as the
363
703
  # superclass. This can be used along with record identification in
364
704
  # Action Pack to allow, say, <tt>Client < Company</tt> to do something
365
705
  # like render <tt>partial: @client.becomes(Company)</tt> to render that
366
706
  # instance using the companies/company partial instead of clients/client.
367
707
  #
368
708
  # Note: The new instance will share a link to the same attributes as the original class.
369
- # Therefore the sti column value will still be the same.
709
+ # Therefore the STI column value will still be the same.
370
710
  # Any change to the attributes on either instance will affect both instances.
371
- # If you want to change the sti column as well, use #becomes! instead.
711
+ # If you want to change the STI column as well, use #becomes! instead.
372
712
  def becomes(klass)
373
713
  became = klass.allocate
374
- became.send(:initialize)
375
- became.instance_variable_set("@attributes", @attributes)
376
- became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
377
- became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
378
- became.instance_variable_set("@new_record", new_record?)
379
- became.instance_variable_set("@destroyed", destroyed?)
380
- became.errors.copy!(errors)
714
+
715
+ became.send(:initialize) do |becoming|
716
+ becoming.instance_variable_set(:@attributes, @attributes)
717
+ becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
718
+ becoming.instance_variable_set(:@new_record, new_record?)
719
+ becoming.instance_variable_set(:@destroyed, destroyed?)
720
+ becoming.errors.copy!(errors)
721
+ end
722
+
381
723
  became
382
724
  end
383
725
 
384
- # Wrapper around #becomes that also changes the instance's sti column value.
726
+ # Wrapper around #becomes that also changes the instance's STI column value.
385
727
  # This is especially useful if you want to persist the changed class in your
386
728
  # database.
387
729
  #
388
- # Note: The old instance's sti column value will be changed too, as both objects
730
+ # Note: The old instance's STI column value will be changed too, as both objects
389
731
  # share the same set of attributes.
390
732
  def becomes!(klass)
391
733
  became = becomes(klass)
@@ -429,8 +771,6 @@ module ActiveRecord
429
771
  end
430
772
  end
431
773
 
432
- alias update_attributes update
433
-
434
774
  # Updates its receiver just like #update but calls #save! instead
435
775
  # of +save+, so an exception is raised if the record is invalid and saving will fail.
436
776
  def update!(attributes)
@@ -442,8 +782,6 @@ module ActiveRecord
442
782
  end
443
783
  end
444
784
 
445
- alias update_attributes! update!
446
-
447
785
  # Equivalent to <code>update_columns(name => value)</code>.
448
786
  def update_column(name, value)
449
787
  update_columns(name => value)
@@ -469,18 +807,21 @@ module ActiveRecord
469
807
  raise ActiveRecordError, "cannot update a new record" if new_record?
470
808
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
471
809
 
472
- attributes.each_key do |key|
473
- verify_readonly_attribute(key.to_s)
810
+ attributes = attributes.transform_keys do |key|
811
+ name = key.to_s
812
+ name = self.class.attribute_aliases[name] || name
813
+ verify_readonly_attribute(name) || name
474
814
  end
475
815
 
476
- id_in_database = self.id_in_database
477
- attributes.each do |k, v|
478
- write_attribute_without_type_cast(k, v)
816
+ update_constraints = _primary_key_constraints_hash
817
+ attributes = attributes.each_with_object({}) do |(k, v), h|
818
+ h[k] = @attributes.write_cast_value(k, v)
819
+ clear_attribute_change(k)
479
820
  end
480
821
 
481
822
  affected_rows = self.class._update_record(
482
823
  attributes,
483
- self.class.primary_key => id_in_database
824
+ update_constraints
484
825
  )
485
826
 
486
827
  affected_rows == 1
@@ -503,9 +844,9 @@ module ActiveRecord
503
844
  # Returns +self+.
504
845
  def increment!(attribute, by = 1, touch: nil)
505
846
  increment(attribute, by)
506
- change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0)
847
+ change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
507
848
  self.class.update_counters(id, attribute => change, touch: touch)
508
- clear_attribute_change(attribute) # eww
849
+ public_send(:"clear_#{attribute}_change")
509
850
  self
510
851
  end
511
852
 
@@ -602,15 +943,16 @@ module ActiveRecord
602
943
  def reload(options = nil)
603
944
  self.class.connection.clear_query_cache
604
945
 
605
- fresh_object =
606
- if options && options[:lock]
607
- self.class.unscoped { self.class.lock(options[:lock]).find(id) }
608
- else
609
- self.class.unscoped { self.class.find(id) }
610
- end
946
+ fresh_object = if apply_scoping?(options)
947
+ _find_record(options)
948
+ else
949
+ self.class.unscoped { _find_record(options) }
950
+ end
611
951
 
612
- @attributes = fresh_object.instance_variable_get("@attributes")
952
+ @association_cache = fresh_object.instance_variable_get(:@association_cache)
953
+ @attributes = fresh_object.instance_variable_get(:@attributes)
613
954
  @new_record = false
955
+ @previously_new_record = false
614
956
  self
615
957
  end
616
958
 
@@ -649,15 +991,13 @@ module ActiveRecord
649
991
  # ball.touch(:updated_at) # => raises ActiveRecordError
650
992
  #
651
993
  def touch(*names, time: nil)
652
- unless persisted?
653
- raise ActiveRecordError, <<-MSG.squish
654
- cannot touch on a new or destroyed record object. Consider using
655
- persisted?, new_record?, or destroyed? before touching
656
- MSG
657
- end
994
+ _raise_record_not_touched_error unless persisted?
658
995
 
659
996
  attribute_names = timestamp_attributes_for_update_in_model
660
- attribute_names |= names.map(&:to_s)
997
+ attribute_names |= names.map! do |name|
998
+ name = name.to_s
999
+ self.class.attribute_aliases[name] || name
1000
+ end unless names.empty?
661
1001
 
662
1002
  unless attribute_names.empty?
663
1003
  affected_rows = _touch_row(attribute_names, time)
@@ -668,6 +1008,28 @@ module ActiveRecord
668
1008
  end
669
1009
 
670
1010
  private
1011
+ def strict_loaded_associations
1012
+ @association_cache.find_all do |_, assoc|
1013
+ assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
1014
+ end.map(&:first)
1015
+ end
1016
+
1017
+ def _find_record(options)
1018
+ if options && options[:lock]
1019
+ self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
1020
+ else
1021
+ self.class.preload(strict_loaded_associations).find(id)
1022
+ end
1023
+ end
1024
+
1025
+ def apply_scoping?(options)
1026
+ !(options && options[:unscoped]) &&
1027
+ (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
1028
+ end
1029
+
1030
+ def _primary_key_constraints_hash
1031
+ { @primary_key => id_in_database }
1032
+ end
671
1033
 
672
1034
  # A hook to be overridden by association modules.
673
1035
  def destroy_associations
@@ -678,15 +1040,14 @@ module ActiveRecord
678
1040
  end
679
1041
 
680
1042
  def _delete_row
681
- self.class._delete_record(self.class.primary_key => id_in_database)
1043
+ self.class._delete_record(_primary_key_constraints_hash)
682
1044
  end
683
1045
 
684
1046
  def _touch_row(attribute_names, time)
685
1047
  time ||= current_time_from_proper_timezone
686
1048
 
687
1049
  attribute_names.each do |attr_name|
688
- write_attribute(attr_name, time)
689
- clear_attribute_change(attr_name)
1050
+ _write_attribute(attr_name, time)
690
1051
  end
691
1052
 
692
1053
  _update_row(attribute_names, "touch")
@@ -695,21 +1056,20 @@ module ActiveRecord
695
1056
  def _update_row(attribute_names, attempted_action = "update")
696
1057
  self.class._update_record(
697
1058
  attributes_with_values(attribute_names),
698
- self.class.primary_key => id_in_database
1059
+ _primary_key_constraints_hash
699
1060
  )
700
1061
  end
701
1062
 
702
- def create_or_update(*args, &block)
1063
+ def create_or_update(**, &block)
703
1064
  _raise_readonly_record_error if readonly?
704
1065
  return false if destroyed?
705
- result = new_record? ? _create_record(&block) : _update_record(*args, &block)
1066
+ result = new_record? ? _create_record(&block) : _update_record(&block)
706
1067
  result != false
707
1068
  end
708
1069
 
709
1070
  # Updates the associated record with values matching those of the instance attributes.
710
1071
  # Returns the number of affected rows.
711
1072
  def _update_record(attribute_names = self.attribute_names)
712
- attribute_names &= self.class.column_names
713
1073
  attribute_names = attributes_for_update(attribute_names)
714
1074
 
715
1075
  if attribute_names.empty?
@@ -720,6 +1080,8 @@ module ActiveRecord
720
1080
  @_trigger_update_callback = affected_rows == 1
721
1081
  end
722
1082
 
1083
+ @previously_new_record = false
1084
+
723
1085
  yield(self) if block_given?
724
1086
 
725
1087
  affected_rows
@@ -728,13 +1090,16 @@ module ActiveRecord
728
1090
  # Creates a record with values matching those of the instance attributes
729
1091
  # and returns its id.
730
1092
  def _create_record(attribute_names = self.attribute_names)
731
- attribute_names &= self.class.column_names
732
- attributes_values = attributes_with_values_for_create(attribute_names)
1093
+ attribute_names = attributes_for_create(attribute_names)
1094
+
1095
+ new_id = self.class._insert_record(
1096
+ attributes_with_values(attribute_names)
1097
+ )
733
1098
 
734
- new_id = self.class._insert_record(attributes_values)
735
- self.id ||= new_id if self.class.primary_key
1099
+ self.id ||= new_id if @primary_key
736
1100
 
737
1101
  @new_record = false
1102
+ @previously_new_record = true
738
1103
 
739
1104
  yield(self) if block_given?
740
1105
 
@@ -742,22 +1107,32 @@ module ActiveRecord
742
1107
  end
743
1108
 
744
1109
  def verify_readonly_attribute(name)
745
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
1110
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
746
1111
  end
747
1112
 
748
1113
  def _raise_record_not_destroyed
749
1114
  @_association_destroy_exception ||= nil
750
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
1115
+ key = self.class.primary_key
1116
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
751
1117
  ensure
752
1118
  @_association_destroy_exception = nil
753
1119
  end
754
1120
 
755
- def belongs_to_touch_method
756
- :touch
757
- end
758
-
759
1121
  def _raise_readonly_record_error
760
1122
  raise ReadOnlyRecord, "#{self.class} is marked as readonly"
761
1123
  end
1124
+
1125
+ def _raise_record_not_touched_error
1126
+ raise ActiveRecordError, <<~MSG.squish
1127
+ Cannot touch on a new or destroyed record object. Consider using
1128
+ persisted?, new_record?, or destroyed? before touching.
1129
+ MSG
1130
+ end
1131
+
1132
+ # The name of the method used to touch a +belongs_to+ association when the
1133
+ # +:touch+ option is used.
1134
+ def belongs_to_touch_method
1135
+ :touch
1136
+ end
762
1137
  end
763
1138
  end