activerecord 4.2.9 → 6.1.4.1

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 (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +964 -1382
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +266 -251
  8. data/lib/active_record/association_relation.rb +40 -15
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +162 -69
  11. data/lib/active_record/associations/association_scope.rb +105 -130
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -65
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -37
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +148 -287
  22. data/lib/active_record/associations/collection_proxy.rb +252 -150
  23. data/lib/active_record/associations/foreign_association.rb +23 -1
  24. data/lib/active_record/associations/has_many_association.rb +56 -98
  25. data/lib/active_record/associations/has_many_through_association.rb +68 -89
  26. data/lib/active_record/associations/has_one_association.rb +73 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
  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 +174 -169
  32. data/lib/active_record/associations/preloader/association.rb +108 -115
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +97 -94
  35. data/lib/active_record/associations/singular_association.rb +18 -39
  36. data/lib/active_record/associations/through_association.rb +39 -19
  37. data/lib/active_record/associations.rb +1845 -1598
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -148
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
  46. data/lib/active_record/attribute_methods/write.rb +24 -55
  47. data/lib/active_record/attribute_methods.rb +149 -154
  48. data/lib/active_record/attributes.rb +234 -78
  49. data/lib/active_record/autosave_association.rb +133 -60
  50. data/lib/active_record/base.rb +46 -46
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +34 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
  119. data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +458 -241
  133. data/lib/active_record/counter_cache.rb +70 -49
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +272 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +211 -92
  143. data/lib/active_record/errors.rb +224 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +10 -5
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +275 -500
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +62 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +27 -5
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +98 -92
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +295 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +673 -325
  170. data/lib/active_record/model_schema.rb +418 -113
  171. data/lib/active_record/nested_attributes.rb +263 -224
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +572 -136
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +170 -51
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +523 -199
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +454 -291
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +324 -249
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +316 -242
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +95 -103
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +136 -122
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -413
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +18 -20
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -343
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -23
  209. data/lib/active_record/scoping/default.rb +96 -83
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +128 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +364 -130
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +287 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +182 -163
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -45
  231. data/lib/active_record/type/date_time.rb +4 -49
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +27 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +63 -56
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +42 -29
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +172 -65
  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_decorators.rb +0 -66
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  353. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  354. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  355. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  356. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  357. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  358. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  359. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  360. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  361. data/lib/active_record/type/big_integer.rb +0 -13
  362. data/lib/active_record/type/binary.rb +0 -50
  363. data/lib/active_record/type/boolean.rb +0 -31
  364. data/lib/active_record/type/decimal.rb +0 -64
  365. data/lib/active_record/type/decorator.rb +0 -14
  366. data/lib/active_record/type/float.rb +0 -19
  367. data/lib/active_record/type/integer.rb +0 -59
  368. data/lib/active_record/type/mutable.rb +0 -16
  369. data/lib/active_record/type/numeric.rb +0 -36
  370. data/lib/active_record/type/string.rb +0 -40
  371. data/lib/active_record/type/time_value.rb +0 -38
  372. data/lib/active_record/type/value.rb +0 -110
  373. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  374. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
2
4
 
3
5
  module ActiveRecord
4
6
  module Integration
@@ -7,17 +9,32 @@ module ActiveRecord
7
9
  included do
8
10
  ##
9
11
  # :singleton-method:
10
- # Indicates the format used to generate the timestamp in the cache key.
11
- # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
12
+ # Indicates the format used to generate the timestamp in the cache key, if
13
+ # versioning is off. Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
14
+ #
15
+ # This is +:usec+, by default.
16
+ class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
17
+
18
+ ##
19
+ # :singleton-method:
20
+ # Indicates whether to use a stable #cache_key method that is accompanied
21
+ # by a changing version in the #cache_version method.
22
+ #
23
+ # This is +true+, by default on Rails 5.2 and above.
24
+ class_attribute :cache_versioning, instance_writer: false, default: false
25
+
26
+ ##
27
+ # :singleton-method:
28
+ # Indicates whether to use a stable #cache_key method that is accompanied
29
+ # by a changing version in the #cache_version method on collections.
12
30
  #
13
- # This is +:nsec+, by default.
14
- class_attribute :cache_timestamp_format, :instance_writer => false
15
- self.cache_timestamp_format = :nsec
31
+ # This is +false+, by default until Rails 6.1.
32
+ class_attribute :collection_cache_versioning, instance_writer: false, default: false
16
33
  end
17
34
 
18
- # Returns a String, which Action Pack uses for constructing an URL to this
19
- # object. The default implementation returns this record's id as a String,
20
- # or nil if this record's unsaved.
35
+ # Returns a +String+, which Action Pack uses for constructing a URL to this
36
+ # object. The default implementation returns this record's id as a +String+,
37
+ # or +nil+ if this record's unsaved.
21
38
  #
22
39
  # For example, suppose that you have a User model, and that you have a
23
40
  # <tt>resources :users</tt> route. Normally, +user_path+ will
@@ -42,29 +59,62 @@ module ActiveRecord
42
59
  id && id.to_s # Be sure to stringify the id for routes
43
60
  end
44
61
 
45
- # Returns a cache key that can be used to identify this record.
62
+ # Returns a stable cache key that can be used to identify this record.
46
63
  #
47
64
  # Product.new.cache_key # => "products/new"
48
- # Product.find(5).cache_key # => "products/5" (updated_at not available)
49
- # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
65
+ # Product.find(5).cache_key # => "products/5"
50
66
  #
51
- # You can also pass a list of named timestamps, and the newest in the list will be
52
- # used to generate the key:
67
+ # If ActiveRecord::Base.cache_versioning is turned off, as it was in Rails 5.1 and earlier,
68
+ # the cache key will also include a version.
53
69
  #
54
- # Person.find(5).cache_key(:updated_at, :last_reviewed_at)
55
- def cache_key(*timestamp_names)
56
- case
57
- when new_record?
70
+ # Product.cache_versioning = false
71
+ # Product.find(5).cache_key # => "products/5-20071224150000" (updated_at available)
72
+ def cache_key
73
+ if new_record?
58
74
  "#{model_name.cache_key}/new"
59
- when timestamp_names.any?
60
- timestamp = max_updated_column_timestamp(timestamp_names)
61
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
62
- "#{model_name.cache_key}/#{id}-#{timestamp}"
63
- when timestamp = max_updated_column_timestamp
64
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
65
- "#{model_name.cache_key}/#{id}-#{timestamp}"
66
75
  else
67
- "#{model_name.cache_key}/#{id}"
76
+ if cache_version
77
+ "#{model_name.cache_key}/#{id}"
78
+ else
79
+ timestamp = max_updated_column_timestamp
80
+
81
+ if timestamp
82
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
83
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
84
+ else
85
+ "#{model_name.cache_key}/#{id}"
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # Returns a cache version that can be used together with the cache key to form
92
+ # a recyclable caching scheme. By default, the #updated_at column is used for the
93
+ # cache_version, but this method can be overwritten to return something else.
94
+ #
95
+ # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
96
+ # +false+.
97
+ def cache_version
98
+ return unless cache_versioning
99
+
100
+ if has_attribute?("updated_at")
101
+ timestamp = updated_at_before_type_cast
102
+ if can_use_fast_cache_version?(timestamp)
103
+ raw_timestamp_to_cache_version(timestamp)
104
+ elsif timestamp = updated_at
105
+ timestamp.utc.to_s(cache_timestamp_format)
106
+ end
107
+ elsif self.class.has_attribute?("updated_at")
108
+ raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
109
+ end
110
+ end
111
+
112
+ # Returns a cache key along with the version.
113
+ def cache_key_with_version
114
+ if version = cache_version
115
+ "#{cache_key}-#{version}"
116
+ else
117
+ cache_key
68
118
  end
69
119
  end
70
120
 
@@ -84,9 +134,9 @@ module ActiveRecord
84
134
  # Values longer than 20 characters will be truncated. The value
85
135
  # is truncated word by word.
86
136
  #
87
- # user = User.find_by(name: 'David HeinemeierHansson')
137
+ # user = User.find_by(name: 'David Heinemeier Hansson')
88
138
  # user.id # => 125
89
- # user_path(user) # => "/users/125-david"
139
+ # user_path(user) # => "/users/125-david-heinemeier"
90
140
  #
91
141
  # Because the generated param begins with the record's +id+, it is
92
142
  # suitable for passing to +find+. In a controller, for example:
@@ -100,7 +150,7 @@ module ActiveRecord
100
150
  define_method :to_param do
101
151
  if (default = super()) &&
102
152
  (result = send(method_name).to_s).present? &&
103
- (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
153
+ (param = result.squish.parameterize.truncate(20, separator: /-/, omission: "")).present?
104
154
  "#{default}-#{param}"
105
155
  else
106
156
  default
@@ -108,6 +158,48 @@ module ActiveRecord
108
158
  end
109
159
  end
110
160
  end
161
+
162
+ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
163
+ collection.send(:compute_cache_key, timestamp_column)
164
+ end
111
165
  end
166
+
167
+ private
168
+ # Detects if the value before type cast
169
+ # can be used to generate a cache_version.
170
+ #
171
+ # The fast cache version only works with a
172
+ # string value directly from the database.
173
+ #
174
+ # We also must check if the timestamp format has been changed
175
+ # or if the timezone is not set to UTC then
176
+ # we cannot apply our transformations correctly.
177
+ def can_use_fast_cache_version?(timestamp)
178
+ timestamp.is_a?(String) &&
179
+ cache_timestamp_format == :usec &&
180
+ default_timezone == :utc &&
181
+ !updated_at_came_from_user?
182
+ end
183
+
184
+ # Converts a raw database string to `:usec`
185
+ # format.
186
+ #
187
+ # Example:
188
+ #
189
+ # timestamp = "2018-10-15 20:02:15.266505"
190
+ # raw_timestamp_to_cache_version(timestamp)
191
+ # # => "20181015200215266505"
192
+ #
193
+ # PostgreSQL truncates trailing zeros,
194
+ # https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
195
+ # to account for this we pad the output with zeros
196
+ def raw_timestamp_to_cache_version(timestamp)
197
+ key = timestamp.delete("- :.")
198
+ if key.length < 20
199
+ key.ljust(20, "0")
200
+ else
201
+ key
202
+ end
203
+ end
112
204
  end
113
205
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/scoping/default"
4
+ require "active_record/scoping/named"
5
+
6
+ module ActiveRecord
7
+ # This class is used to create a table that keeps track of values and keys such
8
+ # as which environment migrations were run in.
9
+ #
10
+ # This is enabled by default. To disable this functionality set
11
+ # `use_metadata_table` to false in your database configuration.
12
+ class InternalMetadata < ActiveRecord::Base # :nodoc:
13
+ class << self
14
+ def enabled?
15
+ ActiveRecord::Base.connection.use_metadata_table?
16
+ end
17
+
18
+ def _internal?
19
+ true
20
+ end
21
+
22
+ def primary_key
23
+ "key"
24
+ end
25
+
26
+ def table_name
27
+ "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
28
+ end
29
+
30
+ def []=(key, value)
31
+ return unless enabled?
32
+
33
+ find_or_initialize_by(key: key).update!(value: value)
34
+ end
35
+
36
+ def [](key)
37
+ return unless enabled?
38
+
39
+ where(key: key).pluck(:value).first
40
+ end
41
+
42
+ # Creates an internal metadata table with columns +key+ and +value+
43
+ def create_table
44
+ return unless enabled?
45
+
46
+ unless connection.table_exists?(table_name)
47
+ connection.create_table(table_name, id: false) do |t|
48
+ t.string :key, **connection.internal_string_options_for_primary_key
49
+ t.string :value
50
+ t.timestamps
51
+ end
52
+ end
53
+ end
54
+
55
+ def drop_table
56
+ return unless enabled?
57
+
58
+ connection.drop_table table_name, if_exists: true
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,20 +1,42 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- module LegacyYamlAdapter
4
+ module LegacyYamlAdapter # :nodoc:
3
5
  def self.convert(klass, coder)
4
6
  return coder unless coder.is_a?(Psych::Coder)
5
7
 
6
8
  case coder["active_record_yaml_version"]
7
- when 0 then coder
9
+ when 1, 2 then coder
8
10
  else
9
- if coder["attributes"].is_a?(AttributeSet)
10
- coder
11
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
+ YAML loading from legacy format older than Rails 5.0 is deprecated
13
+ and will be removed in Rails 6.2.
14
+ MSG
15
+ if coder["attributes"].is_a?(ActiveModel::AttributeSet)
16
+ Rails420.convert(klass, coder)
11
17
  else
12
18
  Rails41.convert(klass, coder)
13
19
  end
14
20
  end
15
21
  end
16
22
 
17
- module Rails41
23
+ module Rails420 # :nodoc:
24
+ def self.convert(klass, coder)
25
+ attribute_set = coder["attributes"]
26
+
27
+ klass.attribute_names.each do |attr_name|
28
+ attribute = attribute_set[attr_name]
29
+ if attribute.type.is_a?(Delegator)
30
+ type_from_klass = klass.type_for_attribute(attr_name)
31
+ attribute_set[attr_name] = attribute.with_type(type_from_klass)
32
+ end
33
+ end
34
+
35
+ coder
36
+ end
37
+ end
38
+
39
+ module Rails41 # :nodoc:
18
40
  def self.convert(klass, coder)
19
41
  attributes = klass.attributes_builder
20
42
  .build_from_database(coder["attributes"])
@@ -7,6 +7,7 @@ en:
7
7
  # Default error messages
8
8
  errors:
9
9
  messages:
10
+ required: "must exist"
10
11
  taken: "has already been taken"
11
12
 
12
13
  # Active Record models configuration
@@ -15,8 +16,8 @@ en:
15
16
  messages:
16
17
  record_invalid: "Validation failed: %{errors}"
17
18
  restrict_dependent_destroy:
18
- one: "Cannot delete record because a dependent %{record} exists"
19
- many: "Cannot delete record because dependent %{record} exist"
19
+ has_one: "Cannot delete record because a dependent %{record} exists"
20
+ has_many: "Cannot delete record because dependent %{record} exist"
20
21
  # Append your own errors here or at the model/attributes scope.
21
22
 
22
23
  # You can define own errors for models or model attributes.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Locking
3
5
  # == What is Optimistic Locking
@@ -11,7 +13,7 @@ module ActiveRecord
11
13
  #
12
14
  # == Usage
13
15
  #
14
- # Active Records support optimistic locking if the field +lock_version+ is present. Each update to the
16
+ # Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
15
17
  # record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
16
18
  # will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
17
19
  #
@@ -22,7 +24,7 @@ module ActiveRecord
22
24
  # p1.save
23
25
  #
24
26
  # p2.first_name = "should fail"
25
- # p2.save # Raises a ActiveRecord::StaleObjectError
27
+ # p2.save # Raises an ActiveRecord::StaleObjectError
26
28
  #
27
29
  # Optimistic locking will also check for stale data when objects are destroyed. Example:
28
30
  #
@@ -32,7 +34,7 @@ module ActiveRecord
32
34
  # p1.first_name = "Michael"
33
35
  # p1.save
34
36
  #
35
- # p2.destroy # Raises a ActiveRecord::StaleObjectError
37
+ # p2.destroy # Raises an ActiveRecord::StaleObjectError
36
38
  #
37
39
  # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
38
40
  # or otherwise apply the business logic needed to resolve the conflict.
@@ -51,22 +53,24 @@ module ActiveRecord
51
53
  extend ActiveSupport::Concern
52
54
 
53
55
  included do
54
- class_attribute :lock_optimistically, instance_writer: false
55
- self.lock_optimistically = true
56
+ class_attribute :lock_optimistically, instance_writer: false, default: true
56
57
  end
57
58
 
58
59
  def locking_enabled? #:nodoc:
59
60
  self.class.locking_enabled?
60
61
  end
61
62
 
62
- private
63
- def increment_lock
64
- lock_col = self.class.locking_column
65
- previous_lock_value = send(lock_col).to_i
66
- send(lock_col + '=', previous_lock_value + 1)
63
+ def increment!(*, **) #:nodoc:
64
+ super.tap do
65
+ if locking_enabled?
66
+ self[self.class.locking_column] += 1
67
+ clear_attribute_change(self.class.locking_column)
68
+ end
67
69
  end
70
+ end
68
71
 
69
- def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
72
+ private
73
+ def _create_record(attribute_names = self.attribute_names)
70
74
  if locking_enabled?
71
75
  # We always want to persist the locking version, even if we don't detect
72
76
  # a change from the default, since the database might have no default
@@ -75,131 +79,133 @@ module ActiveRecord
75
79
  super
76
80
  end
77
81
 
78
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
82
+ def _touch_row(attribute_names, time)
83
+ @_touch_attr_names << self.class.locking_column if locking_enabled?
84
+ super
85
+ end
86
+
87
+ def _update_row(attribute_names, attempted_action = "update")
79
88
  return super unless locking_enabled?
80
- return 0 if attribute_names.empty?
81
89
 
82
- lock_col = self.class.locking_column
83
- previous_lock_value = send(lock_col).to_i
84
- increment_lock
90
+ begin
91
+ locking_column = self.class.locking_column
92
+ lock_attribute_was = @attributes[locking_column]
93
+ lock_value_for_database = _lock_value_for_database(locking_column)
85
94
 
86
- attribute_names += [lock_col]
87
- attribute_names.uniq!
95
+ attribute_names = attribute_names.dup if attribute_names.frozen?
96
+ attribute_names << locking_column
88
97
 
89
- begin
90
- relation = self.class.unscoped
91
-
92
- affected_rows = relation.where(
93
- self.class.primary_key => id,
94
- lock_col => previous_lock_value,
95
- ).update_all(
96
- Hash[attributes_for_update(attribute_names).map do |name|
97
- [name, _read_attribute(name)]
98
- end]
98
+ self[locking_column] += 1
99
+
100
+ affected_rows = self.class._update_record(
101
+ attributes_with_values(attribute_names),
102
+ @primary_key => id_in_database,
103
+ locking_column => lock_value_for_database
99
104
  )
100
105
 
101
- unless affected_rows == 1
102
- raise ActiveRecord::StaleObjectError.new(self, "update")
106
+ if affected_rows != 1
107
+ raise ActiveRecord::StaleObjectError.new(self, attempted_action)
103
108
  end
104
109
 
105
110
  affected_rows
106
111
 
107
- # If something went wrong, revert the version.
112
+ # If something went wrong, revert the locking_column value.
108
113
  rescue Exception
109
- send(lock_col + '=', previous_lock_value)
114
+ @attributes[locking_column] = lock_attribute_was
110
115
  raise
111
116
  end
112
117
  end
113
118
 
114
119
  def destroy_row
115
- affected_rows = super
120
+ return super unless locking_enabled?
121
+
122
+ locking_column = self.class.locking_column
123
+
124
+ affected_rows = self.class._delete_record(
125
+ @primary_key => id_in_database,
126
+ locking_column => _lock_value_for_database(locking_column)
127
+ )
116
128
 
117
- if locking_enabled? && affected_rows != 1
129
+ if affected_rows != 1
118
130
  raise ActiveRecord::StaleObjectError.new(self, "destroy")
119
131
  end
120
132
 
121
133
  affected_rows
122
134
  end
123
135
 
124
- def relation_for_destroy
125
- relation = super
126
-
127
- if locking_enabled?
128
- column_name = self.class.locking_column
129
- column = self.class.columns_hash[column_name]
130
- substitute = self.class.connection.substitute_at(column)
131
-
132
- relation = relation.where(self.class.arel_table[column_name].eq(substitute))
133
- relation.bind_values << [column, self[column_name].to_i]
136
+ def _lock_value_for_database(locking_column)
137
+ if will_save_change_to_attribute?(locking_column)
138
+ @attributes[locking_column].value_for_database
139
+ else
140
+ @attributes[locking_column].original_value_for_database
134
141
  end
135
-
136
- relation
137
142
  end
138
143
 
139
- module ClassMethods
140
- DEFAULT_LOCKING_COLUMN = 'lock_version'
144
+ module ClassMethods
145
+ DEFAULT_LOCKING_COLUMN = "lock_version"
141
146
 
142
- # Returns true if the +lock_optimistically+ flag is set to true
143
- # (which it is, by default) and the table includes the
144
- # +locking_column+ column (defaults to +lock_version+).
145
- def locking_enabled?
146
- lock_optimistically && columns_hash[locking_column]
147
- end
147
+ # Returns true if the +lock_optimistically+ flag is set to true
148
+ # (which it is, by default) and the table includes the
149
+ # +locking_column+ column (defaults to +lock_version+).
150
+ def locking_enabled?
151
+ lock_optimistically && columns_hash[locking_column]
152
+ end
148
153
 
149
- # Set the column to use for optimistic locking. Defaults to +lock_version+.
150
- def locking_column=(value)
151
- clear_caches_calculated_from_columns
152
- @locking_column = value.to_s
153
- end
154
+ # Set the column to use for optimistic locking. Defaults to +lock_version+.
155
+ def locking_column=(value)
156
+ reload_schema_from_cache
157
+ @locking_column = value.to_s
158
+ end
154
159
 
155
- # The version column used for optimistic locking. Defaults to +lock_version+.
156
- def locking_column
157
- reset_locking_column unless defined?(@locking_column)
158
- @locking_column
159
- end
160
+ # The version column used for optimistic locking. Defaults to +lock_version+.
161
+ def locking_column
162
+ @locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
163
+ @locking_column
164
+ end
160
165
 
161
- # Reset the column used for optimistic locking back to the +lock_version+ default.
162
- def reset_locking_column
163
- self.locking_column = DEFAULT_LOCKING_COLUMN
164
- end
166
+ # Reset the column used for optimistic locking back to the +lock_version+ default.
167
+ def reset_locking_column
168
+ self.locking_column = DEFAULT_LOCKING_COLUMN
169
+ end
165
170
 
166
- # Make sure the lock version column gets updated when counters are
167
- # updated.
168
- def update_counters(id, counters)
169
- counters = counters.merge(locking_column => 1) if locking_enabled?
170
- super
171
- end
171
+ # Make sure the lock version column gets updated when counters are
172
+ # updated.
173
+ def update_counters(id, counters)
174
+ counters = counters.merge(locking_column => 1) if locking_enabled?
175
+ super
176
+ end
172
177
 
173
- private
174
-
175
- # We need to apply this decorator here, rather than on module inclusion. The closure
176
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
177
- # sub class being decorated. As such, changes to `lock_optimistically`, or
178
- # `locking_column` would not be picked up.
179
- def inherited(subclass)
180
- subclass.class_eval do
181
- is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
182
- decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
183
- LockingType.new(type)
178
+ def define_attribute(name, cast_type, **) # :nodoc:
179
+ if lock_optimistically && name == locking_column
180
+ cast_type = LockingType.new(cast_type)
184
181
  end
182
+ super
185
183
  end
186
- super
187
184
  end
188
- end
189
185
  end
190
186
 
191
- class LockingType < SimpleDelegator # :nodoc:
192
- def type_cast_from_database(value)
193
- # `nil` *should* be changed to 0
187
+ # In de/serialize we change `nil` to 0, so that we can allow passing
188
+ # `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
189
+ # during update record.
190
+ class LockingType < DelegateClass(Type::Value) # :nodoc:
191
+ def self.new(subtype)
192
+ self === subtype ? subtype : super
193
+ end
194
+
195
+ def deserialize(value)
196
+ super.to_i
197
+ end
198
+
199
+ def serialize(value)
194
200
  super.to_i
195
201
  end
196
202
 
197
203
  def init_with(coder)
198
- __setobj__(coder['subtype'])
204
+ __setobj__(coder["subtype"])
199
205
  end
200
206
 
201
207
  def encode_with(coder)
202
- coder['subtype'] = __getobj__
208
+ coder["subtype"] = __getobj__
203
209
  end
204
210
  end
205
211
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Locking
3
5
  # Locking::Pessimistic provides support for row-level locking using
@@ -12,9 +14,9 @@ module ActiveRecord
12
14
  # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
13
15
  #
14
16
  # Account.transaction do
15
- # # select * from accounts where name = 'shugo' limit 1 for update
16
- # shugo = Account.where("name = 'shugo'").lock(true).first
17
- # yuko = Account.where("name = 'yuko'").lock(true).first
17
+ # # select * from accounts where name = 'shugo' limit 1 for update nowait
18
+ # shugo = Account.lock("FOR UPDATE NOWAIT").find_by(name: "shugo")
19
+ # yuko = Account.lock("FOR UPDATE NOWAIT").find_by(name: "yuko")
18
20
  # shugo.balance -= 100
19
21
  # shugo.save!
20
22
  # yuko.balance += 100
@@ -51,15 +53,29 @@ module ActiveRecord
51
53
  # end
52
54
  #
53
55
  # Database-specific information on row locking:
54
- # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
55
- # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
+ #
57
+ # [MySQL]
58
+ # https://dev.mysql.com/doc/refman/en/innodb-locking-reads.html
59
+ #
60
+ # [PostgreSQL]
61
+ # https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
62
  module Pessimistic
57
63
  # Obtain a row lock on this record. Reloads the record to obtain the requested
58
64
  # lock. Pass an SQL locking clause to append the end of the SELECT statement
59
65
  # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
60
66
  # the locked record.
61
67
  def lock!(lock = true)
62
- reload(:lock => lock) if persisted?
68
+ if persisted?
69
+ if has_changes_to_save?
70
+ raise(<<-MSG.squish)
71
+ Locking a record with unpersisted changes is not supported. Use
72
+ `save` to persist the changes, or `reload` to discard them
73
+ explicitly.
74
+ MSG
75
+ end
76
+
77
+ reload(lock: lock)
78
+ end
63
79
  self
64
80
  end
65
81