activerecord 3.2.22.5 → 5.2.8

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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  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 +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,23 +1,27 @@
1
- require 'thread'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  # See ActiveRecord::Transactions::ClassMethods for documentation.
5
5
  module Transactions
6
6
  extend ActiveSupport::Concern
7
-
8
- class TransactionError < ActiveRecordError # :nodoc:
9
- end
7
+ #:nodoc:
8
+ ACTIONS = [:create, :destroy, :update]
10
9
 
11
10
  included do
12
- define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
11
+ define_callbacks :commit, :rollback,
12
+ :before_commit,
13
+ :before_commit_without_transaction_enrollment,
14
+ :commit_without_transaction_enrollment,
15
+ :rollback_without_transaction_enrollment,
16
+ scope: [:kind, :name]
13
17
  end
14
18
 
15
19
  # = Active Record Transactions
16
20
  #
17
- # Transactions are protective blocks where SQL statements are only permanent
21
+ # \Transactions are protective blocks where SQL statements are only permanent
18
22
  # if they can all succeed as one atomic action. The classic example is a
19
23
  # transfer between two accounts where you can only have a deposit if the
20
- # withdrawal succeeded and vice versa. Transactions enforce the integrity of
24
+ # withdrawal succeeded and vice versa. \Transactions enforce the integrity of
21
25
  # the database and guard the data against program errors or database
22
26
  # break-downs. So basically you should use transaction blocks whenever you
23
27
  # have a number of statements that must be executed together or not at all.
@@ -37,20 +41,20 @@ module ActiveRecord
37
41
  #
38
42
  # == Different Active Record classes in a single transaction
39
43
  #
40
- # Though the transaction class method is called on some Active Record class,
44
+ # Though the #transaction class method is called on some Active Record class,
41
45
  # the objects within the transaction block need not all be instances of
42
46
  # that class. This is because transactions are per-database connection, not
43
47
  # per-model.
44
48
  #
45
49
  # In this example a +balance+ record is transactionally saved even
46
- # though +transaction+ is called on the +Account+ class:
50
+ # though #transaction is called on the +Account+ class:
47
51
  #
48
52
  # Account.transaction do
49
53
  # balance.save!
50
54
  # account.save!
51
55
  # end
52
56
  #
53
- # The +transaction+ method is also available as a model instance method.
57
+ # The #transaction method is also available as a model instance method.
54
58
  # For example, you can also do this:
55
59
  #
56
60
  # balance.transaction do
@@ -77,7 +81,8 @@ module ActiveRecord
77
81
  #
78
82
  # == +save+ and +destroy+ are automatically wrapped in a transaction
79
83
  #
80
- # Both +save+ and +destroy+ come wrapped in a transaction that ensures
84
+ # Both {#save}[rdoc-ref:Persistence#save] and
85
+ # {#destroy}[rdoc-ref:Persistence#destroy] come wrapped in a transaction that ensures
81
86
  # that whatever you do in validations or callbacks will happen under its
82
87
  # protected cover. So you can use validations to check for values that
83
88
  # the transaction depends on or you can raise exceptions in the callbacks
@@ -86,7 +91,7 @@ module ActiveRecord
86
91
  # As a consequence changes to the database are not seen outside your connection
87
92
  # until the operation is complete. For example, if you try to update the index
88
93
  # of a search engine in +after_save+ the indexer won't see the updated record.
89
- # The +after_commit+ callback is the only one that is triggered once the update
94
+ # The #after_commit callback is the only one that is triggered once the update
90
95
  # is committed. See below.
91
96
  #
92
97
  # == Exception handling and rolling back
@@ -95,11 +100,11 @@ module ActiveRecord
95
100
  # be propagated (after triggering the ROLLBACK), so you should be ready to
96
101
  # catch those in your application code.
97
102
  #
98
- # One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger
103
+ # One exception is the ActiveRecord::Rollback exception, which will trigger
99
104
  # a ROLLBACK when raised, but not be re-raised by the transaction block.
100
105
  #
101
- # *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions
102
- # inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an
106
+ # *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions
107
+ # inside a transaction block. ActiveRecord::StatementInvalid exceptions indicate that an
103
108
  # error occurred at the database level, for example when a unique constraint
104
109
  # is violated. On some database systems, such as PostgreSQL, database errors
105
110
  # inside a transaction cause the entire transaction to become unusable
@@ -108,10 +113,10 @@ module ActiveRecord
108
113
  #
109
114
  # # Suppose that we have a Number model with a unique column called 'i'.
110
115
  # Number.transaction do
111
- # Number.create(:i => 0)
116
+ # Number.create(i: 0)
112
117
  # begin
113
118
  # # This will raise a unique constraint error...
114
- # Number.create(:i => 0)
119
+ # Number.create(i: 0)
115
120
  # rescue ActiveRecord::StatementInvalid
116
121
  # # ...which we ignore.
117
122
  # end
@@ -119,74 +124,74 @@ module ActiveRecord
119
124
  # # On PostgreSQL, the transaction is now unusable. The following
120
125
  # # statement will cause a PostgreSQL error, even though the unique
121
126
  # # constraint is no longer violated:
122
- # Number.create(:i => 1)
123
- # # => "PGError: ERROR: current transaction is aborted, commands
127
+ # Number.create(i: 1)
128
+ # # => "PG::Error: ERROR: current transaction is aborted, commands
124
129
  # # ignored until end of transaction block"
125
130
  # end
126
131
  #
127
132
  # One should restart the entire transaction if an
128
- # <tt>ActiveRecord::StatementInvalid</tt> occurred.
133
+ # ActiveRecord::StatementInvalid occurred.
129
134
  #
130
135
  # == Nested transactions
131
136
  #
132
- # +transaction+ calls can be nested. By default, this makes all database
137
+ # #transaction calls can be nested. By default, this makes all database
133
138
  # statements in the nested transaction block become part of the parent
134
139
  # transaction. For example, the following behavior may be surprising:
135
140
  #
136
141
  # User.transaction do
137
- # User.create(:username => 'Kotori')
142
+ # User.create(username: 'Kotori')
138
143
  # User.transaction do
139
- # User.create(:username => 'Nemu')
144
+ # User.create(username: 'Nemu')
140
145
  # raise ActiveRecord::Rollback
141
146
  # end
142
147
  # end
143
148
  #
144
- # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
149
+ # creates both "Kotori" and "Nemu". Reason is the ActiveRecord::Rollback
145
150
  # exception in the nested block does not issue a ROLLBACK. Since these exceptions
146
151
  # are captured in transaction blocks, the parent block does not see it and the
147
152
  # real transaction is committed.
148
153
  #
149
154
  # In order to get a ROLLBACK for the nested transaction you may ask for a real
150
- # sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
155
+ # sub-transaction by passing <tt>requires_new: true</tt>. If anything goes wrong,
151
156
  # the database rolls back to the beginning of the sub-transaction without rolling
152
157
  # back the parent transaction. If we add it to the previous example:
153
158
  #
154
159
  # User.transaction do
155
- # User.create(:username => 'Kotori')
156
- # User.transaction(:requires_new => true) do
157
- # User.create(:username => 'Nemu')
160
+ # User.create(username: 'Kotori')
161
+ # User.transaction(requires_new: true) do
162
+ # User.create(username: 'Nemu')
158
163
  # raise ActiveRecord::Rollback
159
164
  # end
160
165
  # end
161
166
  #
162
- # only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
167
+ # only "Kotori" is created. This works on MySQL and PostgreSQL. SQLite3 version >= '3.6.8' also supports it.
163
168
  #
164
169
  # Most databases don't support true nested transactions. At the time of
165
170
  # writing, the only database that we're aware of that supports true nested
166
171
  # transactions, is MS-SQL. Because of this, Active Record emulates nested
167
172
  # transactions by using savepoints on MySQL and PostgreSQL. See
168
- # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
173
+ # https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
169
174
  # for more information about savepoints.
170
175
  #
171
- # === Callbacks
176
+ # === \Callbacks
172
177
  #
173
178
  # There are two types of callbacks associated with committing and rolling back transactions:
174
- # +after_commit+ and +after_rollback+.
179
+ # #after_commit and #after_rollback.
175
180
  #
176
- # +after_commit+ callbacks are called on every record saved or destroyed within a
177
- # transaction immediately after the transaction is committed. +after_rollback+ callbacks
181
+ # #after_commit callbacks are called on every record saved or destroyed within a
182
+ # transaction immediately after the transaction is committed. #after_rollback callbacks
178
183
  # are called on every record saved or destroyed within a transaction immediately after the
179
184
  # transaction or savepoint is rolled back.
180
185
  #
181
186
  # These callbacks are useful for interacting with other systems since you will be guaranteed
182
187
  # that the callback is only executed when the database is in a permanent state. For example,
183
- # +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from
188
+ # #after_commit is a good spot to put in a hook to clearing a cache since clearing it from
184
189
  # within a transaction could trigger the cache to be regenerated before the database is updated.
185
190
  #
186
191
  # === Caveats
187
192
  #
188
- # If you're on MySQL, then do not use DDL operations in nested transactions
189
- # blocks that are emulated with savepoints. That is, do not execute statements
193
+ # If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested
194
+ # transactions blocks that are emulated with savepoints. That is, do not execute statements
190
195
  # like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
191
196
  # releases all savepoints upon executing a DDL operation. When +transaction+
192
197
  # is finished and tries to release the savepoint it created earlier, a
@@ -194,55 +199,101 @@ module ActiveRecord
194
199
  # automatically released. The following example demonstrates the problem:
195
200
  #
196
201
  # Model.connection.transaction do # BEGIN
197
- # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
202
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
198
203
  # Model.connection.create_table(...) # active_record_1 now automatically released
199
- # end # RELEASE savepoint active_record_1
204
+ # end # RELEASE SAVEPOINT active_record_1
200
205
  # # ^^^^ BOOM! database error!
201
206
  # end
202
207
  #
203
208
  # Note that "TRUNCATE" is also a MySQL DDL statement!
204
209
  module ClassMethods
205
- # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
210
+ # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
206
211
  def transaction(options = {}, &block)
207
- # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
208
212
  connection.transaction(options, &block)
209
213
  end
210
214
 
215
+ def before_commit(*args, &block) # :nodoc:
216
+ set_options_for_callbacks!(args)
217
+ set_callback(:before_commit, :before, *args, &block)
218
+ end
219
+
211
220
  # This callback is called after a record has been created, updated, or destroyed.
212
221
  #
213
222
  # You can specify that the callback should only be fired by a certain action with
214
223
  # the +:on+ option:
215
224
  #
216
- # after_commit :do_foo, :on => :create
217
- # after_commit :do_bar, :on => :update
218
- # after_commit :do_baz, :on => :destroy
219
- #
220
- # Also, to have the callback fired on create and update, but not on destroy:
225
+ # after_commit :do_foo, on: :create
226
+ # after_commit :do_bar, on: :update
227
+ # after_commit :do_baz, on: :destroy
221
228
  #
222
- # after_commit :do_zoo, :if => :persisted?
229
+ # after_commit :do_foo_bar, on: [:create, :update]
230
+ # after_commit :do_bar_baz, on: [:update, :destroy]
223
231
  #
224
- # Note that transactional fixtures do not play well with this feature. Please
225
- # use the +test_after_commit+ gem to have these hooks fired in tests.
226
232
  def after_commit(*args, &block)
227
- options = args.last
228
- if options.is_a?(Hash) && options[:on]
229
- options[:if] = Array.wrap(options[:if])
230
- options[:if] << "transaction_include_action?(:#{options[:on]})"
231
- end
233
+ set_options_for_callbacks!(args)
234
+ set_callback(:commit, :after, *args, &block)
235
+ end
236
+
237
+ # Shortcut for <tt>after_commit :hook, on: :create</tt>.
238
+ def after_create_commit(*args, &block)
239
+ set_options_for_callbacks!(args, on: :create)
240
+ set_callback(:commit, :after, *args, &block)
241
+ end
242
+
243
+ # Shortcut for <tt>after_commit :hook, on: :update</tt>.
244
+ def after_update_commit(*args, &block)
245
+ set_options_for_callbacks!(args, on: :update)
246
+ set_callback(:commit, :after, *args, &block)
247
+ end
248
+
249
+ # Shortcut for <tt>after_commit :hook, on: :destroy</tt>.
250
+ def after_destroy_commit(*args, &block)
251
+ set_options_for_callbacks!(args, on: :destroy)
232
252
  set_callback(:commit, :after, *args, &block)
233
253
  end
234
254
 
235
255
  # This callback is called after a create, update, or destroy are rolled back.
236
256
  #
237
- # Please check the documentation of +after_commit+ for options.
257
+ # Please check the documentation of #after_commit for options.
238
258
  def after_rollback(*args, &block)
239
- options = args.last
240
- if options.is_a?(Hash) && options[:on]
241
- options[:if] = Array.wrap(options[:if])
242
- options[:if] << "transaction_include_action?(:#{options[:on]})"
243
- end
259
+ set_options_for_callbacks!(args)
244
260
  set_callback(:rollback, :after, *args, &block)
245
261
  end
262
+
263
+ def before_commit_without_transaction_enrollment(*args, &block) # :nodoc:
264
+ set_options_for_callbacks!(args)
265
+ set_callback(:before_commit_without_transaction_enrollment, :before, *args, &block)
266
+ end
267
+
268
+ def after_commit_without_transaction_enrollment(*args, &block) # :nodoc:
269
+ set_options_for_callbacks!(args)
270
+ set_callback(:commit_without_transaction_enrollment, :after, *args, &block)
271
+ end
272
+
273
+ def after_rollback_without_transaction_enrollment(*args, &block) # :nodoc:
274
+ set_options_for_callbacks!(args)
275
+ set_callback(:rollback_without_transaction_enrollment, :after, *args, &block)
276
+ end
277
+
278
+ private
279
+
280
+ def set_options_for_callbacks!(args, enforced_options = {})
281
+ options = args.extract_options!.merge!(enforced_options)
282
+ args << options
283
+
284
+ if options[:on]
285
+ fire_on = Array(options[:on])
286
+ assert_valid_transaction_action(fire_on)
287
+ options[:if] = Array(options[:if])
288
+ options[:if].unshift(-> { transaction_include_any_action?(fire_on) })
289
+ end
290
+ end
291
+
292
+ def assert_valid_transaction_action(actions)
293
+ if (actions - ACTIONS).any?
294
+ raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}"
295
+ end
296
+ end
246
297
  end
247
298
 
248
299
  # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
@@ -264,40 +315,63 @@ module ActiveRecord
264
315
  with_transaction_returning_status { super }
265
316
  end
266
317
 
318
+ def touch(*) #:nodoc:
319
+ with_transaction_returning_status { super }
320
+ end
321
+
267
322
  # Reset id and @new_record if the transaction rolls back.
268
323
  def rollback_active_record_state!
269
324
  remember_transaction_record_state
270
325
  yield
271
326
  rescue Exception
272
- IdentityMap.remove(self) if IdentityMap.enabled?
273
327
  restore_transaction_record_state
274
328
  raise
275
329
  ensure
276
330
  clear_transaction_record_state
277
331
  end
278
332
 
279
- # Call the after_commit callbacks
280
- def committed! #:nodoc:
281
- run_callbacks :commit
333
+ def before_committed! # :nodoc:
334
+ _run_before_commit_without_transaction_enrollment_callbacks
335
+ _run_before_commit_callbacks
336
+ end
337
+
338
+ # Call the #after_commit callbacks.
339
+ #
340
+ # Ensure that it is not called if the object was never persisted (failed create),
341
+ # but call it after the commit of a destroyed object.
342
+ def committed!(should_run_callbacks: true) #:nodoc:
343
+ force_clear_transaction_record_state
344
+ if should_run_callbacks && (destroyed? || persisted?)
345
+ @_committed_already_called = true
346
+ _run_commit_without_transaction_enrollment_callbacks
347
+ _run_commit_callbacks
348
+ end
282
349
  ensure
283
- clear_transaction_record_state
350
+ @_committed_already_called = false
284
351
  end
285
352
 
286
- # Call the after rollback callbacks. The restore_state argument indicates if the record
353
+ # Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
287
354
  # state should be rolled back to the beginning or just to the last savepoint.
288
- def rolledback!(force_restore_state = false) #:nodoc:
289
- run_callbacks :rollback
355
+ def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc:
356
+ if should_run_callbacks
357
+ _run_rollback_callbacks
358
+ _run_rollback_without_transaction_enrollment_callbacks
359
+ end
290
360
  ensure
291
- IdentityMap.remove(self) if IdentityMap.enabled?
292
361
  restore_transaction_record_state(force_restore_state)
362
+ clear_transaction_record_state
293
363
  end
294
364
 
295
- # Add the record to the current transaction so that the :after_rollback and :after_commit callbacks
365
+ # Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
296
366
  # can be called.
297
367
  def add_to_transaction
298
- if self.class.connection.add_transaction_record(self)
299
- remember_transaction_record_state
368
+ if has_transactional_callbacks?
369
+ self.class.connection.add_transaction_record(self)
370
+ else
371
+ sync_with_transaction_state
372
+ set_transaction_state(self.class.connection.transaction_state)
300
373
  end
374
+ remember_transaction_record_state
301
375
  end
302
376
 
303
377
  # Executes +method+ within a transaction and captures its return value as a
@@ -314,68 +388,115 @@ module ActiveRecord
314
388
  raise ActiveRecord::Rollback unless status
315
389
  end
316
390
  status
391
+ ensure
392
+ if @transaction_state && @transaction_state.committed?
393
+ clear_transaction_record_state
394
+ end
317
395
  end
318
396
 
319
397
  protected
398
+ attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback
320
399
 
321
- # Save the new record state and id of a record so it can be restored later if a transaction fails.
322
- def remember_transaction_record_state #:nodoc:
323
- @_start_transaction_state ||= {}
324
- @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
325
- unless @_start_transaction_state.include?(:new_record)
326
- @_start_transaction_state[:new_record] = @new_record
400
+ private
401
+
402
+ # Save the new record state and id of a record so it can be restored later if a transaction fails.
403
+ def remember_transaction_record_state
404
+ @_start_transaction_state.reverse_merge!(
405
+ id: id,
406
+ new_record: @new_record,
407
+ destroyed: @destroyed,
408
+ frozen?: frozen?,
409
+ )
410
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
411
+ remember_new_record_before_last_commit
327
412
  end
328
- unless @_start_transaction_state.include?(:destroyed)
329
- @_start_transaction_state[:destroyed] = @destroyed
413
+
414
+ def remember_new_record_before_last_commit
415
+ if _committed_already_called
416
+ @_new_record_before_last_commit = false
417
+ else
418
+ @_new_record_before_last_commit = @_start_transaction_state[:new_record]
419
+ end
330
420
  end
331
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
332
- @_start_transaction_state[:frozen?] = @attributes.frozen?
333
- end
334
421
 
335
- # Clear the new record state and id of a record.
336
- def clear_transaction_record_state #:nodoc:
337
- if defined?(@_start_transaction_state)
422
+ # Clear the new record state and id of a record.
423
+ def clear_transaction_record_state
338
424
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
339
- remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
425
+ force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
340
426
  end
341
- end
342
427
 
343
- # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
344
- def restore_transaction_record_state(force = false) #:nodoc:
345
- if defined?(@_start_transaction_state)
346
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
347
- if @_start_transaction_state[:level] < 1 || force
348
- restore_state = remove_instance_variable(:@_start_transaction_state)
349
- was_frozen = restore_state[:frozen?]
350
- @attributes = @attributes.dup if @attributes.frozen?
351
- @new_record = restore_state[:new_record]
352
- @destroyed = restore_state[:destroyed]
353
- if restore_state.has_key?(:id)
354
- self.id = restore_state[:id]
355
- else
356
- @attributes.delete(self.class.primary_key)
357
- @attributes_cache.delete(self.class.primary_key)
428
+ # Force to clear the transaction record state.
429
+ def force_clear_transaction_record_state
430
+ @_start_transaction_state.clear
431
+ end
432
+
433
+ # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
434
+ def restore_transaction_record_state(force = false)
435
+ unless @_start_transaction_state.empty?
436
+ transaction_level = (@_start_transaction_state[:level] || 0) - 1
437
+ if transaction_level < 1 || force
438
+ restore_state = @_start_transaction_state
439
+ thaw
440
+ @new_record = restore_state[:new_record]
441
+ @destroyed = restore_state[:destroyed]
442
+ pk = self.class.primary_key
443
+ if pk && _read_attribute(pk) != restore_state[:id]
444
+ _write_attribute(pk, restore_state[:id])
445
+ end
446
+ freeze if restore_state[:frozen?]
358
447
  end
359
- @attributes.freeze if was_frozen
360
448
  end
361
449
  end
362
- end
363
450
 
364
- # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
365
- def transaction_record_state(state) #:nodoc:
366
- @_start_transaction_state[state] if defined?(@_start_transaction_state)
367
- end
451
+ # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
452
+ def transaction_include_any_action?(actions)
453
+ actions.any? do |action|
454
+ case action
455
+ when :create
456
+ persisted? && @_new_record_before_last_commit
457
+ when :update
458
+ !(@_new_record_before_last_commit || destroyed?) && _trigger_update_callback
459
+ when :destroy
460
+ _trigger_destroy_callback
461
+ end
462
+ end
463
+ end
368
464
 
369
- # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
370
- def transaction_include_action?(action) #:nodoc:
371
- case action
372
- when :create
373
- transaction_record_state(:new_record)
374
- when :destroy
375
- destroyed?
376
- when :update
377
- !(transaction_record_state(:new_record) || destroyed?)
465
+ def set_transaction_state(state)
466
+ @transaction_state = state
467
+ end
468
+
469
+ def has_transactional_callbacks?
470
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
471
+ end
472
+
473
+ # Updates the attributes on this particular Active Record object so that
474
+ # if it's associated with a transaction, then the state of the Active Record
475
+ # object will be updated to reflect the current state of the transaction.
476
+ #
477
+ # The <tt>@transaction_state</tt> variable stores the states of the associated
478
+ # transaction. This relies on the fact that a transaction can only be in
479
+ # one rollback or commit (otherwise a list of states would be required).
480
+ # Each Active Record object inside of a transaction carries that transaction's
481
+ # TransactionState.
482
+ #
483
+ # This method checks to see if the ActiveRecord object's state reflects
484
+ # the TransactionState, and rolls back or commits the Active Record object
485
+ # as appropriate.
486
+ #
487
+ # Since Active Record objects can be inside multiple transactions, this
488
+ # method recursively goes through the parent of the TransactionState and
489
+ # checks if the Active Record object reflects the state of the object.
490
+ def sync_with_transaction_state
491
+ update_attributes_from_transaction_state(@transaction_state)
492
+ end
493
+
494
+ def update_attributes_from_transaction_state(transaction_state)
495
+ if transaction_state && transaction_state.finalized?
496
+ restore_transaction_record_state(transaction_state.fully_rolledback?) if transaction_state.rolledback?
497
+ force_clear_transaction_record_state if transaction_state.fully_committed?
498
+ clear_transaction_record_state if transaction_state.fully_completed?
499
+ end
378
500
  end
379
- end
380
501
  end
381
502
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Translation
3
5
  include ActiveModel::Translation