activerecord 3.2.19 → 5.0.0

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

Potentially problematic release.


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

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