activerecord 4.2.11.1 → 6.0.3

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

Potentially problematic release.


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

Files changed (373) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +711 -1547
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +266 -251
  9. data/lib/active_record/association_relation.rb +20 -13
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +128 -57
  12. data/lib/active_record/associations/association_scope.rb +103 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -66
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +136 -288
  23. data/lib/active_record/associations/collection_proxy.rb +241 -147
  24. data/lib/active_record/associations/foreign_association.rb +10 -1
  25. data/lib/active_record/associations/has_many_association.rb +34 -98
  26. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  27. data/lib/active_record/associations/has_one_association.rb +61 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +149 -166
  33. data/lib/active_record/associations/preloader/association.rb +90 -123
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +90 -93
  36. data/lib/active_record/associations/singular_association.rb +18 -39
  37. data/lib/active_record/associations/through_association.rb +38 -18
  38. data/lib/active_record/associations.rb +1737 -1597
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +57 -37
  48. data/lib/active_record/attribute_methods/write.rb +32 -55
  49. data/lib/active_record/attribute_methods.rb +120 -135
  50. data/lib/active_record/attributes.rb +213 -82
  51. data/lib/active_record/autosave_association.rb +97 -41
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +23 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +804 -297
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +240 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +371 -242
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +694 -256
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +473 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +507 -639
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -181
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +51 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -296
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +299 -349
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +252 -230
  130. data/lib/active_record/counter_cache.rb +70 -49
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +227 -501
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +86 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +623 -305
  165. data/lib/active_record/model_schema.rb +313 -112
  166. data/lib/active_record/nested_attributes.rb +263 -223
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +557 -126
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +430 -281
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +268 -254
  182. data/lib/active_record/relation/delegation.rb +75 -84
  183. data/lib/active_record/relation/finder_methods.rb +285 -241
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +78 -88
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +110 -119
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +603 -397
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +11 -14
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +530 -341
  201. data/lib/active_record/result.rb +79 -43
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping/default.rb +98 -83
  208. data/lib/active_record/scoping/named.rb +86 -33
  209. data/lib/active_record/scoping.rb +45 -27
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -100
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +80 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +223 -157
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -45
  228. data/lib/active_record/type/date_time.rb +4 -49
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +23 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +11 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +42 -55
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +42 -22
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. data/lib/rails/generators/active_record.rb +7 -5
  341. metadata +168 -59
  342. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  343. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  344. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  345. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  346. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  347. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  348. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  349. data/lib/active_record/attribute.rb +0 -163
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -31
  363. data/lib/active_record/type/decimal.rb +0 -64
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -59
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -40
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -110
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,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.
12
22
  #
13
- # This is +:nsec+, by default.
14
- class_attribute :cache_timestamp_format, :instance_writer => false
15
- self.cache_timestamp_format = :nsec
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.
30
+ #
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,64 @@ 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+ (which it is by default until Rails 6.0).
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
+ else
108
+ if self.class.has_attribute?("updated_at")
109
+ raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
110
+ end
111
+ end
112
+ end
113
+
114
+ # Returns a cache key along with the version.
115
+ def cache_key_with_version
116
+ if version = cache_version
117
+ "#{cache_key}-#{version}"
118
+ else
119
+ cache_key
68
120
  end
69
121
  end
70
122
 
@@ -84,9 +136,9 @@ module ActiveRecord
84
136
  # Values longer than 20 characters will be truncated. The value
85
137
  # is truncated word by word.
86
138
  #
87
- # user = User.find_by(name: 'David HeinemeierHansson')
139
+ # user = User.find_by(name: 'David Heinemeier Hansson')
88
140
  # user.id # => 125
89
- # user_path(user) # => "/users/125-david"
141
+ # user_path(user) # => "/users/125-david-heinemeier"
90
142
  #
91
143
  # Because the generated param begins with the record's +id+, it is
92
144
  # suitable for passing to +find+. In a controller, for example:
@@ -100,7 +152,7 @@ module ActiveRecord
100
152
  define_method :to_param do
101
153
  if (default = super()) &&
102
154
  (result = send(method_name).to_s).present? &&
103
- (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
155
+ (param = result.squish.parameterize.truncate(20, separator: /-/, omission: "")).present?
104
156
  "#{default}-#{param}"
105
157
  else
106
158
  default
@@ -108,6 +160,48 @@ module ActiveRecord
108
160
  end
109
161
  end
110
162
  end
163
+
164
+ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
165
+ collection.send(:compute_cache_key, timestamp_column)
166
+ end
111
167
  end
168
+
169
+ private
170
+ # Detects if the value before type cast
171
+ # can be used to generate a cache_version.
172
+ #
173
+ # The fast cache version only works with a
174
+ # string value directly from the database.
175
+ #
176
+ # We also must check if the timestamp format has been changed
177
+ # or if the timezone is not set to UTC then
178
+ # we cannot apply our transformations correctly.
179
+ def can_use_fast_cache_version?(timestamp)
180
+ timestamp.is_a?(String) &&
181
+ cache_timestamp_format == :usec &&
182
+ default_timezone == :utc &&
183
+ !updated_at_came_from_user?
184
+ end
185
+
186
+ # Converts a raw database string to `:usec`
187
+ # format.
188
+ #
189
+ # Example:
190
+ #
191
+ # timestamp = "2018-10-15 20:02:15.266505"
192
+ # raw_timestamp_to_cache_version(timestamp)
193
+ # # => "20181015200215266505"
194
+ #
195
+ # PostgreSQL truncates trailing zeros,
196
+ # https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
197
+ # to account for this we pad the output with zeros
198
+ def raw_timestamp_to_cache_version(timestamp)
199
+ key = timestamp.delete("- :.")
200
+ if key.length < 20
201
+ key.ljust(20, "0")
202
+ else
203
+ key
204
+ end
205
+ end
112
206
  end
113
207
  end
@@ -0,0 +1,53 @@
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
+ class InternalMetadata < ActiveRecord::Base # :nodoc:
10
+ class << self
11
+ def _internal?
12
+ true
13
+ end
14
+
15
+ def primary_key
16
+ "key"
17
+ end
18
+
19
+ def table_name
20
+ "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
21
+ end
22
+
23
+ def []=(key, value)
24
+ find_or_initialize_by(key: key).update!(value: value)
25
+ end
26
+
27
+ def [](key)
28
+ where(key: key).pluck(:value).first
29
+ end
30
+
31
+ def table_exists?
32
+ connection.table_exists?(table_name)
33
+ end
34
+
35
+ # Creates an internal metadata table with columns +key+ and +value+
36
+ def create_table
37
+ unless table_exists?
38
+ key_options = connection.internal_string_options_for_primary_key
39
+
40
+ connection.create_table(table_name, id: false) do |t|
41
+ t.string :key, **key_options
42
+ t.string :value
43
+ t.timestamps
44
+ end
45
+ end
46
+ end
47
+
48
+ def drop_table
49
+ connection.drop_table table_name, if_exists: true
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,19 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module LegacyYamlAdapter
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
+ if coder["attributes"].is_a?(ActiveModel::AttributeSet)
12
+ Rails420.convert(klass, coder)
11
13
  else
12
14
  Rails41.convert(klass, coder)
13
15
  end
14
16
  end
15
17
  end
16
18
 
19
+ module Rails420
20
+ def self.convert(klass, coder)
21
+ attribute_set = coder["attributes"]
22
+
23
+ klass.attribute_names.each do |attr_name|
24
+ attribute = attribute_set[attr_name]
25
+ if attribute.type.is_a?(Delegator)
26
+ type_from_klass = klass.type_for_attribute(attr_name)
27
+ attribute_set[attr_name] = attribute.with_type(type_from_klass)
28
+ end
29
+ end
30
+
31
+ coder
32
+ end
33
+ end
34
+
17
35
  module Rails41
18
36
  def self.convert(klass, coder)
19
37
  attributes = klass.attributes_builder
@@ -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,8 +53,7 @@ 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:
@@ -60,13 +61,7 @@ module ActiveRecord
60
61
  end
61
62
 
62
63
  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)
67
- end
68
-
69
- def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
64
+ def _create_record(attribute_names = self.attribute_names)
70
65
  if locking_enabled?
71
66
  # We always want to persist the locking version, even if we don't detect
72
67
  # a change from the default, since the database might have no default
@@ -75,131 +70,126 @@ module ActiveRecord
75
70
  super
76
71
  end
77
72
 
78
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
73
+ def _touch_row(attribute_names, time)
74
+ @_touch_attr_names << self.class.locking_column if locking_enabled?
75
+ super
76
+ end
77
+
78
+ def _update_row(attribute_names, attempted_action = "update")
79
79
  return super unless locking_enabled?
80
- return 0 if attribute_names.empty?
81
80
 
82
- lock_col = self.class.locking_column
83
- previous_lock_value = send(lock_col).to_i
84
- increment_lock
81
+ begin
82
+ locking_column = self.class.locking_column
83
+ previous_lock_value = read_attribute_before_type_cast(locking_column)
84
+ attribute_names << locking_column
85
85
 
86
- attribute_names += [lock_col]
87
- attribute_names.uniq!
86
+ self[locking_column] += 1
88
87
 
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]
88
+ affected_rows = self.class._update_record(
89
+ attributes_with_values(attribute_names),
90
+ @primary_key => id_in_database,
91
+ locking_column => previous_lock_value
99
92
  )
100
93
 
101
- unless affected_rows == 1
102
- raise ActiveRecord::StaleObjectError.new(self, "update")
94
+ if affected_rows != 1
95
+ raise ActiveRecord::StaleObjectError.new(self, attempted_action)
103
96
  end
104
97
 
105
98
  affected_rows
106
99
 
107
- # If something went wrong, revert the version.
100
+ # If something went wrong, revert the locking_column value.
108
101
  rescue Exception
109
- send(lock_col + '=', previous_lock_value)
102
+ self[locking_column] = previous_lock_value.to_i
110
103
  raise
111
104
  end
112
105
  end
113
106
 
114
107
  def destroy_row
115
- affected_rows = super
108
+ return super unless locking_enabled?
116
109
 
117
- if locking_enabled? && affected_rows != 1
110
+ locking_column = self.class.locking_column
111
+
112
+ affected_rows = self.class._delete_record(
113
+ @primary_key => id_in_database,
114
+ locking_column => read_attribute_before_type_cast(locking_column)
115
+ )
116
+
117
+ if affected_rows != 1
118
118
  raise ActiveRecord::StaleObjectError.new(self, "destroy")
119
119
  end
120
120
 
121
121
  affected_rows
122
122
  end
123
123
 
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)
124
+ module ClassMethods
125
+ DEFAULT_LOCKING_COLUMN = "lock_version"
131
126
 
132
- relation = relation.where(self.class.arel_table[column_name].eq(substitute))
133
- relation.bind_values << [column, self[column_name].to_i]
127
+ # Returns true if the +lock_optimistically+ flag is set to true
128
+ # (which it is, by default) and the table includes the
129
+ # +locking_column+ column (defaults to +lock_version+).
130
+ def locking_enabled?
131
+ lock_optimistically && columns_hash[locking_column]
134
132
  end
135
133
 
136
- relation
137
- end
138
-
139
- module ClassMethods
140
- DEFAULT_LOCKING_COLUMN = 'lock_version'
141
-
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
148
-
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
134
+ # Set the column to use for optimistic locking. Defaults to +lock_version+.
135
+ def locking_column=(value)
136
+ reload_schema_from_cache
137
+ @locking_column = value.to_s
138
+ end
154
139
 
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
140
+ # The version column used for optimistic locking. Defaults to +lock_version+.
141
+ def locking_column
142
+ @locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
143
+ @locking_column
144
+ end
160
145
 
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
146
+ # Reset the column used for optimistic locking back to the +lock_version+ default.
147
+ def reset_locking_column
148
+ self.locking_column = DEFAULT_LOCKING_COLUMN
149
+ end
165
150
 
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
151
+ # Make sure the lock version column gets updated when counters are
152
+ # updated.
153
+ def update_counters(id, counters)
154
+ counters = counters.merge(locking_column => 1) if locking_enabled?
155
+ super
156
+ end
172
157
 
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)
158
+ private
159
+ # We need to apply this decorator here, rather than on module inclusion. The closure
160
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
161
+ # sub class being decorated. As such, changes to `lock_optimistically`, or
162
+ # `locking_column` would not be picked up.
163
+ def inherited(subclass)
164
+ subclass.class_eval do
165
+ is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
166
+ decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
167
+ LockingType.new(type)
168
+ end
169
+ end
170
+ super
184
171
  end
185
- end
186
- super
187
172
  end
188
- end
189
173
  end
190
174
 
191
- class LockingType < SimpleDelegator # :nodoc:
192
- def type_cast_from_database(value)
193
- # `nil` *should* be changed to 0
175
+ # In de/serialize we change `nil` to 0, so that we can allow passing
176
+ # `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
177
+ # during update record.
178
+ class LockingType < DelegateClass(Type::Value) # :nodoc:
179
+ def deserialize(value)
180
+ super.to_i
181
+ end
182
+
183
+ def serialize(value)
194
184
  super.to_i
195
185
  end
196
186
 
197
187
  def init_with(coder)
198
- __setobj__(coder['subtype'])
188
+ __setobj__(coder["subtype"])
199
189
  end
200
190
 
201
191
  def encode_with(coder)
202
- coder['subtype'] = __getobj__
192
+ coder["subtype"] = __getobj__
203
193
  end
204
194
  end
205
195
  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,25 @@ 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
+ # MySQL: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
57
+ # PostgreSQL: https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
58
  module Pessimistic
57
59
  # Obtain a row lock on this record. Reloads the record to obtain the requested
58
60
  # lock. Pass an SQL locking clause to append the end of the SELECT statement
59
61
  # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
60
62
  # the locked record.
61
63
  def lock!(lock = true)
62
- reload(:lock => lock) if persisted?
64
+ if persisted?
65
+ if has_changes_to_save?
66
+ raise(<<-MSG.squish)
67
+ Locking a record with unpersisted changes is not supported. Use
68
+ `save` to persist the changes, or `reload` to discard them
69
+ explicitly.
70
+ MSG
71
+ end
72
+
73
+ reload(lock: lock)
74
+ end
63
75
  self
64
76
  end
65
77