activerecord 4.2.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

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