activerecord 4.2.11.2 → 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 +4 -4
  2. data/CHANGELOG.md +613 -1638
  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.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +11 -6
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +125 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +131 -287
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +137 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +91 -37
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +460 -204
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -635
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +551 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +176 -41
  129. data/lib/active_record/core.rb +251 -231
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +312 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +328 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +518 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +277 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +575 -394
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +86 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +216 -150
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +51 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +164 -59
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,7 +1,6 @@
1
- require 'active_support/core_ext/enumerable'
2
- require 'active_support/core_ext/string/filters'
3
- require 'mutex_m'
4
- require 'thread_safe'
1
+ # frozen_string_literal: true
2
+
3
+ require "mutex_m"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Attribute Methods
@@ -23,43 +22,12 @@ module ActiveRecord
23
22
  delegate :column_for_attribute, to: :class
24
23
  end
25
24
 
26
- AttrNames = Module.new {
27
- def self.set_name_cache(name, value)
28
- const_name = "ATTR_#{name}"
29
- unless const_defined? const_name
30
- const_set const_name, value.dup.freeze
31
- end
32
- end
33
- }
34
-
35
- BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
25
+ RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
36
26
 
37
- class AttributeMethodCache
38
- def initialize
39
- @module = Module.new
40
- @method_cache = ThreadSafe::Cache.new
41
- end
42
-
43
- def [](name)
44
- @method_cache.compute_if_absent(name) do
45
- safe_name = name.unpack('h*').first
46
- temp_method = "__temp__#{safe_name}"
47
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
48
- @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
49
- @module.instance_method temp_method
50
- end
51
- end
52
-
53
- private
54
-
55
- # Override this method in the subclasses for method body.
56
- def method_body(method_name, const_name)
57
- raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
58
- end
27
+ class GeneratedAttributeMethods < Module #:nodoc:
28
+ include Mutex_m
59
29
  end
60
30
 
61
- class GeneratedAttributeMethods < Module; end # :nodoc:
62
-
63
31
  module ClassMethods
64
32
  def inherited(child_class) #:nodoc:
65
33
  child_class.initialize_generated_modules
@@ -67,7 +35,8 @@ module ActiveRecord
67
35
  end
68
36
 
69
37
  def initialize_generated_modules # :nodoc:
70
- @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
38
+ @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
39
+ private_constant :GeneratedAttributeMethods
71
40
  @attribute_methods_generated = false
72
41
  include @generated_attribute_methods
73
42
 
@@ -82,11 +51,10 @@ module ActiveRecord
82
51
  # attribute methods.
83
52
  generated_attribute_methods.synchronize do
84
53
  return false if @attribute_methods_generated
85
- superclass.define_attribute_methods unless self == base_class
86
- super(column_names)
54
+ superclass.define_attribute_methods unless base_class?
55
+ super(attribute_names)
87
56
  @attribute_methods_generated = true
88
57
  end
89
- true
90
58
  end
91
59
 
92
60
  def undefine_attribute_methods # :nodoc:
@@ -96,7 +64,7 @@ module ActiveRecord
96
64
  end
97
65
  end
98
66
 
99
- # Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
67
+ # Raises an ActiveRecord::DangerousAttributeError exception when an
100
68
  # \Active \Record method is defined in the model, otherwise +false+.
101
69
  #
102
70
  # class Person < ActiveRecord::Base
@@ -106,7 +74,7 @@ module ActiveRecord
106
74
  # end
107
75
  #
108
76
  # Person.instance_method_already_implemented?(:save)
109
- # # => ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
77
+ # # => ActiveRecord::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
110
78
  #
111
79
  # Person.instance_method_already_implemented?(:name)
112
80
  # # => false
@@ -147,10 +115,10 @@ module ActiveRecord
147
115
  # A class method is 'dangerous' if it is already (re)defined by Active Record, but
148
116
  # not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
149
117
  def dangerous_class_method?(method_name)
150
- BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
118
+ RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
151
119
  end
152
120
 
153
- def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
121
+ def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
154
122
  if klass.respond_to?(name, true)
155
123
  if superklass.respond_to?(name, true)
156
124
  klass.method(name).owner != superklass.method(name).owner
@@ -172,7 +140,7 @@ module ActiveRecord
172
140
  # Person.attribute_method?(:age=) # => true
173
141
  # Person.attribute_method?(:nothing) # => false
174
142
  def attribute_method?(attribute)
175
- super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
143
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, "")))
176
144
  end
177
145
 
178
146
  # Returns an array of column names as strings if it's not an abstract class and
@@ -185,14 +153,27 @@ module ActiveRecord
185
153
  # # => ["id", "created_at", "updated_at", "name", "age"]
186
154
  def attribute_names
187
155
  @attribute_names ||= if !abstract_class? && table_exists?
188
- column_names
189
- else
190
- []
191
- end
156
+ attribute_types.keys
157
+ else
158
+ []
159
+ end
160
+ end
161
+
162
+ # Returns true if the given attribute exists, otherwise false.
163
+ #
164
+ # class Person < ActiveRecord::Base
165
+ # end
166
+ #
167
+ # Person.has_attribute?('name') # => true
168
+ # Person.has_attribute?(:age) # => true
169
+ # Person.has_attribute?(:nothing) # => false
170
+ def has_attribute?(attr_name)
171
+ attribute_types.key?(attr_name.to_s)
192
172
  end
193
173
 
194
174
  # Returns the column object for the named attribute.
195
- # Returns nil if the named attribute does not exist.
175
+ # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
176
+ # named attribute does not exist.
196
177
  #
197
178
  # class Person < ActiveRecord::Base
198
179
  # end
@@ -202,49 +183,45 @@ module ActiveRecord
202
183
  # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
203
184
  #
204
185
  # person.column_for_attribute(:nothing)
205
- # # => nil
186
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
206
187
  def column_for_attribute(name)
207
- column = columns_hash[name.to_s]
208
- if column.nil?
209
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
210
- `#column_for_attribute` will return a null object for non-existent
211
- columns in Rails 5. Use `#has_attribute?` if you need to check for
212
- an attribute's existence.
213
- MSG
188
+ name = name.to_s
189
+ columns_hash.fetch(name) do
190
+ ConnectionAdapters::NullColumn.new(name)
214
191
  end
215
- column
216
192
  end
217
193
  end
218
194
 
219
195
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
220
196
  # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
221
- # which will all return +true+. It also define the attribute methods if they have
197
+ # which will all return +true+. It also defines the attribute methods if they have
222
198
  # not been generated.
223
199
  #
224
200
  # class Person < ActiveRecord::Base
225
201
  # end
226
202
  #
227
203
  # person = Person.new
228
- # person.respond_to(:name) # => true
229
- # person.respond_to(:name=) # => true
230
- # person.respond_to(:name?) # => true
231
- # person.respond_to('age') # => true
232
- # person.respond_to('age=') # => true
233
- # person.respond_to('age?') # => true
234
- # person.respond_to(:nothing) # => false
204
+ # person.respond_to?(:name) # => true
205
+ # person.respond_to?(:name=) # => true
206
+ # person.respond_to?(:name?) # => true
207
+ # person.respond_to?('age') # => true
208
+ # person.respond_to?('age=') # => true
209
+ # person.respond_to?('age?') # => true
210
+ # person.respond_to?(:nothing) # => false
235
211
  def respond_to?(name, include_private = false)
236
212
  return false unless super
237
- name = name.to_s
238
213
 
239
214
  # If the result is true then check for the select case.
240
215
  # For queries selecting a subset of columns, return false for unselected columns.
241
216
  # We check defined?(@attributes) not to issue warnings if called on objects that
242
217
  # have been allocated but not yet initialized.
243
- if defined?(@attributes) && self.class.column_names.include?(name)
244
- return has_attribute?(name)
218
+ if defined?(@attributes)
219
+ if name = self.class.symbol_column_to_string(name.to_sym)
220
+ return has_attribute?(name)
221
+ end
245
222
  end
246
223
 
247
- return true
224
+ true
248
225
  end
249
226
 
250
227
  # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
@@ -301,15 +278,8 @@ module ActiveRecord
301
278
  # person.attribute_for_inspect(:tag_ids)
302
279
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
303
280
  def attribute_for_inspect(attr_name)
304
- value = read_attribute(attr_name)
305
-
306
- if value.is_a?(String) && value.length > 50
307
- "#{value[0, 50]}...".inspect
308
- elsif value.is_a?(Date) || value.is_a?(Time)
309
- %("#{value.to_s(:db)}")
310
- else
311
- value.inspect
312
- end
281
+ value = _read_attribute(attr_name)
282
+ format_for_inspect(value)
313
283
  end
314
284
 
315
285
  # Returns +true+ if the specified +attribute+ has been set by the user or by a
@@ -338,8 +308,6 @@ module ActiveRecord
338
308
  #
339
309
  # Note: +:id+ is always present.
340
310
  #
341
- # Alias for the <tt>read_attribute</tt> method.
342
- #
343
311
  # class Person < ActiveRecord::Base
344
312
  # belongs_to :organization
345
313
  # end
@@ -356,7 +324,7 @@ module ActiveRecord
356
324
  end
357
325
 
358
326
  # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
359
- # (Alias for the protected <tt>write_attribute</tt> method).
327
+ # (Alias for the protected #write_attribute method).
360
328
  #
361
329
  # class Person < ActiveRecord::Base
362
330
  # end
@@ -369,67 +337,84 @@ module ActiveRecord
369
337
  write_attribute(attr_name, value)
370
338
  end
371
339
 
372
- protected
373
-
374
- def clone_attribute_value(reader_method, attribute_name) # :nodoc:
375
- value = send(reader_method, attribute_name)
376
- value.duplicable? ? value.clone : value
377
- rescue TypeError, NoMethodError
378
- value
379
- end
380
-
381
- def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
382
- arel_attributes_with_values(attributes_for_create(attribute_names))
383
- end
384
-
385
- def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
386
- arel_attributes_with_values(attributes_for_update(attribute_names))
387
- end
388
-
389
- def attribute_method?(attr_name) # :nodoc:
390
- # We check defined? because Syck calls respond_to? before actually calling initialize.
391
- defined?(@attributes) && @attributes.key?(attr_name)
340
+ # Returns the name of all database fields which have been read from this
341
+ # model. This can be useful in development mode to determine which fields
342
+ # need to be selected. For performance critical pages, selecting only the
343
+ # required fields can be an easy performance win (assuming you aren't using
344
+ # all of the fields on the model).
345
+ #
346
+ # For example:
347
+ #
348
+ # class PostsController < ActionController::Base
349
+ # after_action :print_accessed_fields, only: :index
350
+ #
351
+ # def index
352
+ # @posts = Post.all
353
+ # end
354
+ #
355
+ # private
356
+ #
357
+ # def print_accessed_fields
358
+ # p @posts.first.accessed_fields
359
+ # end
360
+ # end
361
+ #
362
+ # Which allows you to quickly change your code to:
363
+ #
364
+ # class PostsController < ActionController::Base
365
+ # def index
366
+ # @posts = Post.select(:id, :title, :author_id, :updated_at)
367
+ # end
368
+ # end
369
+ def accessed_fields
370
+ @attributes.accessed
392
371
  end
393
372
 
394
373
  private
374
+ def attribute_method?(attr_name)
375
+ # We check defined? because Syck calls respond_to? before actually calling initialize.
376
+ defined?(@attributes) && @attributes.key?(attr_name)
377
+ end
395
378
 
396
- # Returns a Hash of the Arel::Attributes and attribute values that have been
397
- # typecasted for use in an Arel insert/update method.
398
- def arel_attributes_with_values(attribute_names)
399
- attrs = {}
400
- arel_table = self.class.arel_table
401
-
402
- attribute_names.each do |name|
403
- attrs[arel_table[name]] = typecasted_attribute_value(name)
379
+ def attributes_with_values(attribute_names)
380
+ attribute_names.each_with_object({}) do |name, attrs|
381
+ attrs[name] = _read_attribute(name)
382
+ end
404
383
  end
405
- attrs
406
- end
407
384
 
408
- # Filters the primary keys and readonly attributes from the attribute names.
409
- def attributes_for_update(attribute_names)
410
- attribute_names.reject do |name|
411
- readonly_attribute?(name)
385
+ # Filters the primary keys and readonly attributes from the attribute names.
386
+ def attributes_for_update(attribute_names)
387
+ attribute_names &= self.class.column_names
388
+ attribute_names.delete_if do |name|
389
+ readonly_attribute?(name)
390
+ end
412
391
  end
413
- end
414
392
 
415
- # Filters out the primary keys, from the attribute names, when the primary
416
- # key is to be generated (e.g. the id attribute has no value).
417
- def attributes_for_create(attribute_names)
418
- attribute_names.reject do |name|
419
- pk_attribute?(name) && id.nil?
393
+ # Filters out the primary keys, from the attribute names, when the primary
394
+ # key is to be generated (e.g. the id attribute has no value).
395
+ def attributes_for_create(attribute_names)
396
+ attribute_names &= self.class.column_names
397
+ attribute_names.delete_if do |name|
398
+ pk_attribute?(name) && id.nil?
399
+ end
420
400
  end
421
- end
422
401
 
423
- def readonly_attribute?(name)
424
- self.class.readonly_attributes.include?(name)
425
- end
402
+ def format_for_inspect(value)
403
+ if value.is_a?(String) && value.length > 50
404
+ "#{value[0, 50]}...".inspect
405
+ elsif value.is_a?(Date) || value.is_a?(Time)
406
+ %("#{value.to_s(:db)}")
407
+ else
408
+ value.inspect
409
+ end
410
+ end
426
411
 
427
- def pk_attribute?(name)
428
- name == self.class.primary_key
429
- end
412
+ def readonly_attribute?(name)
413
+ self.class.readonly_attributes.include?(name)
414
+ end
430
415
 
431
- def typecasted_attribute_value(name)
432
- _read_attribute(name)
433
- end
416
+ def pk_attribute?(name)
417
+ name == @primary_key
418
+ end
434
419
  end
435
420
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  # = Active Record Attribute Methods Before Type Cast
4
6
  #
5
- # <tt>ActiveRecord::AttributeMethods::BeforeTypeCast</tt> provides a way to
7
+ # ActiveRecord::AttributeMethods::BeforeTypeCast provides a way to
6
8
  # read the value of the attributes before typecasting and deserialization.
7
9
  #
8
10
  # class Task < ActiveRecord::Base
@@ -44,6 +46,7 @@ module ActiveRecord
44
46
  # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
45
47
  # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
46
48
  def read_attribute_before_type_cast(attr_name)
49
+ sync_with_transaction_state if @transaction_state&.finalized?
47
50
  @attributes[attr_name.to_s].value_before_type_cast
48
51
  end
49
52
 
@@ -58,19 +61,21 @@ module ActiveRecord
58
61
  # task.attributes_before_type_cast
59
62
  # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
60
63
  def attributes_before_type_cast
64
+ sync_with_transaction_state if @transaction_state&.finalized?
61
65
  @attributes.values_before_type_cast
62
66
  end
63
67
 
64
68
  private
65
69
 
66
- # Handle *_before_type_cast for method_missing.
67
- def attribute_before_type_cast(attribute_name)
68
- read_attribute_before_type_cast(attribute_name)
69
- end
70
+ # Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
71
+ def attribute_before_type_cast(attribute_name)
72
+ read_attribute_before_type_cast(attribute_name)
73
+ end
70
74
 
71
- def attribute_came_from_user?(attribute_name)
72
- @attributes[attribute_name].came_from_user?
73
- end
75
+ def attribute_came_from_user?(attribute_name)
76
+ sync_with_transaction_state if @transaction_state&.finalized?
77
+ @attributes[attribute_name].came_from_user?
78
+ end
74
79
  end
75
80
  end
76
81
  end
@@ -1,8 +1,10 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
2
4
 
3
5
  module ActiveRecord
4
6
  module AttributeMethods
5
- module Dirty # :nodoc:
7
+ module Dirty
6
8
  extend ActiveSupport::Concern
7
9
 
8
10
  include ActiveModel::Dirty
@@ -12,180 +14,208 @@ module ActiveRecord
12
14
  raise "You cannot include Dirty after Timestamp"
13
15
  end
14
16
 
15
- class_attribute :partial_writes, instance_writer: false
16
- self.partial_writes = true
17
- end
17
+ class_attribute :partial_writes, instance_writer: false, default: true
18
18
 
19
- # Attempts to +save+ the record and clears changed attributes if successful.
20
- def save(*)
21
- if status = super
22
- changes_applied
23
- end
24
- status
25
- end
19
+ # Attribute methods for "changed in last call to save?"
20
+ attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
21
+ attribute_method_prefix("saved_change_to_")
22
+ attribute_method_suffix("_before_last_save")
26
23
 
27
- # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
28
- def save!(*)
29
- super.tap do
30
- changes_applied
31
- end
24
+ # Attribute methods for "will change if I call save?"
25
+ attribute_method_affix(prefix: "will_save_change_to_", suffix: "?")
26
+ attribute_method_suffix("_change_to_be_saved", "_in_database")
32
27
  end
33
28
 
34
29
  # <tt>reload</tt> the record and clears changed attributes.
35
30
  def reload(*)
36
31
  super.tap do
37
- clear_changes_information
32
+ @mutations_before_last_save = nil
33
+ @mutations_from_database = nil
38
34
  end
39
35
  end
40
36
 
41
- def initialize_dup(other) # :nodoc:
42
- super
43
- @original_raw_attributes = nil
44
- calculate_changes_from_defaults
45
- end
46
-
47
- def changes_applied
48
- super
49
- store_original_raw_attributes
37
+ # Did this attribute change when we last saved?
38
+ #
39
+ # This method is useful in after callbacks to determine if an attribute
40
+ # was changed during the save that triggered the callbacks to run. It can
41
+ # be invoked as +saved_change_to_name?+ instead of
42
+ # <tt>saved_change_to_attribute?("name")</tt>.
43
+ #
44
+ # ==== Options
45
+ #
46
+ # +from+ When passed, this method will return false unless the original
47
+ # value is equal to the given option
48
+ #
49
+ # +to+ When passed, this method will return false unless the value was
50
+ # changed to the given value
51
+ def saved_change_to_attribute?(attr_name, **options)
52
+ mutations_before_last_save.changed?(attr_name.to_s, options)
53
+ end
54
+
55
+ # Returns the change to an attribute during the last save. If the
56
+ # attribute was changed, the result will be an array containing the
57
+ # original value and the saved value.
58
+ #
59
+ # This method is useful in after callbacks, to see the change in an
60
+ # attribute during the save that triggered the callbacks to run. It can be
61
+ # invoked as +saved_change_to_name+ instead of
62
+ # <tt>saved_change_to_attribute("name")</tt>.
63
+ def saved_change_to_attribute(attr_name)
64
+ mutations_before_last_save.change_to_attribute(attr_name.to_s)
65
+ end
66
+
67
+ # Returns the original value of an attribute before the last save.
68
+ #
69
+ # This method is useful in after callbacks to get the original value of an
70
+ # attribute before the save that triggered the callbacks to run. It can be
71
+ # invoked as +name_before_last_save+ instead of
72
+ # <tt>attribute_before_last_save("name")</tt>.
73
+ def attribute_before_last_save(attr_name)
74
+ mutations_before_last_save.original_value(attr_name.to_s)
75
+ end
76
+
77
+ # Did the last call to +save+ have any changes to change?
78
+ def saved_changes?
79
+ mutations_before_last_save.any_changes?
80
+ end
81
+
82
+ # Returns a hash containing all the changes that were just saved.
83
+ def saved_changes
84
+ mutations_before_last_save.changes
85
+ end
86
+
87
+ # Will this attribute change the next time we save?
88
+ #
89
+ # This method is useful in validations and before callbacks to determine
90
+ # if the next call to +save+ will change a particular attribute. It can be
91
+ # invoked as +will_save_change_to_name?+ instead of
92
+ # <tt>will_save_change_to_attribute("name")</tt>.
93
+ #
94
+ # ==== Options
95
+ #
96
+ # +from+ When passed, this method will return false unless the original
97
+ # value is equal to the given option
98
+ #
99
+ # +to+ When passed, this method will return false unless the value will be
100
+ # changed to the given value
101
+ def will_save_change_to_attribute?(attr_name, **options)
102
+ mutations_from_database.changed?(attr_name.to_s, options)
103
+ end
104
+
105
+ # Returns the change to an attribute that will be persisted during the
106
+ # next save.
107
+ #
108
+ # This method is useful in validations and before callbacks, to see the
109
+ # change to an attribute that will occur when the record is saved. It can
110
+ # be invoked as +name_change_to_be_saved+ instead of
111
+ # <tt>attribute_change_to_be_saved("name")</tt>.
112
+ #
113
+ # If the attribute will change, the result will be an array containing the
114
+ # original value and the new value about to be saved.
115
+ def attribute_change_to_be_saved(attr_name)
116
+ mutations_from_database.change_to_attribute(attr_name.to_s)
117
+ end
118
+
119
+ # Returns the value of an attribute in the database, as opposed to the
120
+ # in-memory value that will be persisted the next time the record is
121
+ # saved.
122
+ #
123
+ # This method is useful in validations and before callbacks, to see the
124
+ # original value of an attribute prior to any changes about to be
125
+ # saved. It can be invoked as +name_in_database+ instead of
126
+ # <tt>attribute_in_database("name")</tt>.
127
+ def attribute_in_database(attr_name)
128
+ mutations_from_database.original_value(attr_name.to_s)
129
+ end
130
+
131
+ # Will the next call to +save+ have any changes to persist?
132
+ def has_changes_to_save?
133
+ mutations_from_database.any_changes?
134
+ end
135
+
136
+ # Returns a hash containing all the changes that will be persisted during
137
+ # the next save.
138
+ def changes_to_save
139
+ mutations_from_database.changes
140
+ end
141
+
142
+ # Returns an array of the names of any attributes that will change when
143
+ # the record is next saved.
144
+ def changed_attribute_names_to_save
145
+ mutations_from_database.changed_attribute_names
146
+ end
147
+
148
+ # Returns a hash of the attributes that will change when the record is
149
+ # next saved.
150
+ #
151
+ # The hash keys are the attribute names, and the hash values are the
152
+ # original attribute values in the database (as opposed to the in-memory
153
+ # values about to be saved).
154
+ def attributes_in_database
155
+ mutations_from_database.changed_values
50
156
  end
51
157
 
52
- def clear_changes_information
53
- super
54
- original_raw_attributes.clear
55
- end
56
-
57
- def changed_attributes
58
- # This should only be set by methods which will call changed_attributes
59
- # multiple times when it is known that the computed value cannot change.
60
- if defined?(@cached_changed_attributes)
61
- @cached_changed_attributes
62
- else
63
- super.reverse_merge(attributes_changed_in_place).freeze
158
+ private
159
+ def mutations_from_database
160
+ sync_with_transaction_state if @transaction_state&.finalized?
161
+ super
64
162
  end
65
- end
66
163
 
67
- def changes
68
- cache_changed_attributes do
164
+ def mutations_before_last_save
165
+ sync_with_transaction_state if @transaction_state&.finalized?
69
166
  super
70
167
  end
71
- end
72
-
73
- def attribute_changed_in_place?(attr_name)
74
- old_value = original_raw_attribute(attr_name)
75
- @attributes[attr_name].changed_in_place_from?(old_value)
76
- end
77
168
 
78
- private
79
-
80
- def changes_include?(attr_name)
81
- super || attribute_changed_in_place?(attr_name)
82
- end
83
-
84
- def calculate_changes_from_defaults
85
- @changed_attributes = nil
86
- self.class.column_defaults.each do |attr, orig_value|
87
- set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value)
169
+ def write_attribute_without_type_cast(attr_name, value)
170
+ result = super
171
+ clear_attribute_change(attr_name)
172
+ result
88
173
  end
89
- end
90
174
 
91
- # Wrap write_attribute to remember original attribute value.
92
- def write_attribute(attr, value)
93
- attr = attr.to_s
175
+ def _touch_row(attribute_names, time)
176
+ @_touch_attr_names = Set.new(attribute_names)
94
177
 
95
- old_value = old_attribute_value(attr)
178
+ affected_rows = super
96
179
 
97
- result = super
98
- store_original_raw_attribute(attr)
99
- save_changed_attribute(attr, old_value)
100
- result
101
- end
180
+ if @_skip_dirty_tracking ||= false
181
+ clear_attribute_changes(@_touch_attr_names)
182
+ return affected_rows
183
+ end
102
184
 
103
- def raw_write_attribute(attr, value)
104
- attr = attr.to_s
185
+ changes = {}
186
+ @attributes.keys.each do |attr_name|
187
+ next if @_touch_attr_names.include?(attr_name)
105
188
 
106
- result = super
107
- original_raw_attributes[attr] = value
108
- result
109
- end
189
+ if attribute_changed?(attr_name)
190
+ changes[attr_name] = _read_attribute(attr_name)
191
+ _write_attribute(attr_name, attribute_was(attr_name))
192
+ clear_attribute_change(attr_name)
193
+ end
194
+ end
110
195
 
111
- def save_changed_attribute(attr, old_value)
112
- clear_changed_attributes_cache
113
- if attribute_changed_by_setter?(attr)
114
- clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
115
- else
116
- set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
117
- end
118
- end
119
-
120
- def old_attribute_value(attr)
121
- if attribute_changed?(attr)
122
- changed_attributes[attr]
123
- else
124
- clone_attribute_value(:_read_attribute, attr)
125
- end
126
- end
127
-
128
- def _update_record(*)
129
- partial_writes? ? super(keys_for_partial_write) : super
130
- end
131
-
132
- def _create_record(*)
133
- partial_writes? ? super(keys_for_partial_write) : super
134
- end
135
-
136
- # Serialized attributes should always be written in case they've been
137
- # changed in place.
138
- def keys_for_partial_write
139
- changed & persistable_attribute_names
140
- end
141
-
142
- def _field_changed?(attr, old_value)
143
- @attributes[attr].changed_from?(old_value)
144
- end
196
+ changes_applied
197
+ changes.each { |attr_name, value| _write_attribute(attr_name, value) }
145
198
 
146
- def attributes_changed_in_place
147
- changed_in_place.each_with_object({}) do |attr_name, h|
148
- orig = @attributes[attr_name].original_value
149
- h[attr_name] = orig
199
+ affected_rows
200
+ ensure
201
+ @_touch_attr_names, @_skip_dirty_tracking = nil, nil
150
202
  end
151
- end
152
203
 
153
- def changed_in_place
154
- self.class.attribute_names.select do |attr_name|
155
- attribute_changed_in_place?(attr_name)
204
+ def _update_record(attribute_names = attribute_names_for_partial_writes)
205
+ affected_rows = super
206
+ changes_applied
207
+ affected_rows
156
208
  end
157
- end
158
209
 
159
- def original_raw_attribute(attr_name)
160
- original_raw_attributes.fetch(attr_name) do
161
- read_attribute_before_type_cast(attr_name)
210
+ def _create_record(attribute_names = attribute_names_for_partial_writes)
211
+ id = super
212
+ changes_applied
213
+ id
162
214
  end
163
- end
164
215
 
165
- def original_raw_attributes
166
- @original_raw_attributes ||= {}
167
- end
168
-
169
- def store_original_raw_attribute(attr_name)
170
- original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database rescue nil
171
- end
172
-
173
- def store_original_raw_attributes
174
- attribute_names.each do |attr|
175
- store_original_raw_attribute(attr)
216
+ def attribute_names_for_partial_writes
217
+ partial_writes? ? changed_attribute_names_to_save : attribute_names
176
218
  end
177
- end
178
-
179
- def cache_changed_attributes
180
- @cached_changed_attributes = changed_attributes
181
- yield
182
- ensure
183
- clear_changed_attributes_cache
184
- end
185
-
186
- def clear_changed_attributes_cache
187
- remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
188
- end
189
219
  end
190
220
  end
191
221
  end