activerecord 6.0.0.beta3 → 6.0.0.rc1

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -6
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +0 -1
  5. data/lib/active_record/associations.rb +3 -2
  6. data/lib/active_record/associations/association.rb +1 -1
  7. data/lib/active_record/associations/builder/association.rb +14 -18
  8. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  9. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  10. data/lib/active_record/associations/builder/has_many.rb +2 -0
  11. data/lib/active_record/associations/builder/has_one.rb +35 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  13. data/lib/active_record/associations/collection_proxy.rb +1 -1
  14. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  15. data/lib/active_record/associations/preloader.rb +11 -6
  16. data/lib/active_record/associations/preloader/association.rb +32 -30
  17. data/lib/active_record/associations/preloader/through_association.rb +48 -28
  18. data/lib/active_record/attribute_methods.rb +4 -3
  19. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  20. data/lib/active_record/attribute_methods/dirty.rb +42 -14
  21. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  22. data/lib/active_record/attribute_methods/query.rb +2 -3
  23. data/lib/active_record/attribute_methods/read.rb +3 -9
  24. data/lib/active_record/attribute_methods/write.rb +6 -12
  25. data/lib/active_record/attributes.rb +13 -0
  26. data/lib/active_record/autosave_association.rb +13 -3
  27. data/lib/active_record/base.rb +0 -1
  28. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
  29. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  30. data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
  31. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  32. data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
  33. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
  34. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
  35. data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
  36. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
  37. data/lib/active_record/connection_adapters/column.rb +17 -13
  38. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  39. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
  40. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
  41. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  42. data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
  43. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  44. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  45. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
  46. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  47. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -27
  48. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  49. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  50. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  51. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  52. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
  53. data/lib/active_record/connection_handling.rb +17 -10
  54. data/lib/active_record/core.rb +15 -20
  55. data/lib/active_record/database_configurations.rb +14 -14
  56. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  57. data/lib/active_record/database_configurations/url_config.rb +12 -12
  58. data/lib/active_record/dynamic_matchers.rb +1 -1
  59. data/lib/active_record/enum.rb +6 -0
  60. data/lib/active_record/errors.rb +1 -1
  61. data/lib/active_record/gem_version.rb +1 -1
  62. data/lib/active_record/insert_all.rb +180 -0
  63. data/lib/active_record/integration.rb +13 -1
  64. data/lib/active_record/internal_metadata.rb +5 -1
  65. data/lib/active_record/locking/optimistic.rb +3 -4
  66. data/lib/active_record/log_subscriber.rb +1 -1
  67. data/lib/active_record/migration.rb +25 -18
  68. data/lib/active_record/migration/command_recorder.rb +28 -14
  69. data/lib/active_record/migration/compatibility.rb +10 -0
  70. data/lib/active_record/persistence.rb +206 -13
  71. data/lib/active_record/querying.rb +17 -12
  72. data/lib/active_record/railties/databases.rake +68 -6
  73. data/lib/active_record/reflection.rb +2 -2
  74. data/lib/active_record/relation.rb +98 -20
  75. data/lib/active_record/relation/calculations.rb +39 -39
  76. data/lib/active_record/relation/delegation.rb +22 -30
  77. data/lib/active_record/relation/finder_methods.rb +3 -9
  78. data/lib/active_record/relation/merger.rb +7 -16
  79. data/lib/active_record/relation/query_methods.rb +153 -38
  80. data/lib/active_record/relation/where_clause.rb +9 -5
  81. data/lib/active_record/sanitization.rb +3 -2
  82. data/lib/active_record/schema_dumper.rb +5 -0
  83. data/lib/active_record/schema_migration.rb +1 -1
  84. data/lib/active_record/scoping/default.rb +6 -7
  85. data/lib/active_record/scoping/named.rb +1 -1
  86. data/lib/active_record/statement_cache.rb +2 -2
  87. data/lib/active_record/store.rb +48 -0
  88. data/lib/active_record/table_metadata.rb +3 -3
  89. data/lib/active_record/tasks/database_tasks.rb +36 -1
  90. data/lib/active_record/touch_later.rb +2 -2
  91. data/lib/active_record/transactions.rb +52 -41
  92. data/lib/active_record/validations/uniqueness.rb +3 -5
  93. data/lib/arel/insert_manager.rb +3 -3
  94. data/lib/arel/nodes.rb +2 -1
  95. data/lib/arel/nodes/comment.rb +29 -0
  96. data/lib/arel/nodes/select_core.rb +16 -12
  97. data/lib/arel/nodes/unary.rb +1 -0
  98. data/lib/arel/nodes/values_list.rb +2 -17
  99. data/lib/arel/select_manager.rb +10 -10
  100. data/lib/arel/visitors/depth_first.rb +6 -1
  101. data/lib/arel/visitors/dot.rb +7 -2
  102. data/lib/arel/visitors/ibm_db.rb +13 -0
  103. data/lib/arel/visitors/informix.rb +6 -0
  104. data/lib/arel/visitors/mssql.rb +15 -1
  105. data/lib/arel/visitors/oracle12.rb +4 -5
  106. data/lib/arel/visitors/postgresql.rb +4 -10
  107. data/lib/arel/visitors/to_sql.rb +87 -108
  108. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  109. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  110. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  111. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  112. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  113. metadata +12 -11
  114. data/lib/active_record/collection_cache_key.rb +0 -53
  115. data/lib/arel/nodes/values.rb +0 -16
@@ -70,7 +70,15 @@ module ActiveRecord
70
70
  predicates == other.predicates
71
71
  end
72
72
 
73
- def invert
73
+ def invert(as = :nand)
74
+ if predicates.size == 1
75
+ inverted_predicates = [ invert_predicate(predicates.first) ]
76
+ elsif as == :nor
77
+ inverted_predicates = predicates.map { |node| invert_predicate(node) }
78
+ else
79
+ inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
80
+ end
81
+
74
82
  WhereClause.new(inverted_predicates)
75
83
  end
76
84
 
@@ -115,10 +123,6 @@ module ActiveRecord
115
123
  node.respond_to?(:operator) && node.operator == :==
116
124
  end
117
125
 
118
- def inverted_predicates
119
- predicates.map { |node| invert_predicate(node) }
120
- end
121
-
122
126
  def invert_predicate(node)
123
127
  case node
124
128
  when NilClass
@@ -165,10 +165,11 @@ module ActiveRecord
165
165
 
166
166
  def quote_bound_value(value, c = connection)
167
167
  if value.respond_to?(:map) && !value.acts_like?(:string)
168
- if value.respond_to?(:empty?) && value.empty?
168
+ quoted = value.map { |v| c.quote(v) }
169
+ if quoted.empty?
169
170
  c.quote(nil)
170
171
  else
171
- value.map { |v| c.quote(v) }.join(",")
172
+ quoted.join(",")
172
173
  end
173
174
  else
174
175
  c.quote(value)
@@ -47,6 +47,7 @@ module ActiveRecord
47
47
  end
48
48
 
49
49
  private
50
+ attr_accessor :table_name
50
51
 
51
52
  def initialize(connection, options = {})
52
53
  @connection = connection
@@ -110,6 +111,8 @@ HEADER
110
111
  def table(table, stream)
111
112
  columns = @connection.columns(table)
112
113
  begin
114
+ self.table_name = table
115
+
113
116
  tbl = StringIO.new
114
117
 
115
118
  # first dump primary key column
@@ -159,6 +162,8 @@ HEADER
159
162
  stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
160
163
  stream.puts "# #{e.message}"
161
164
  stream.puts
165
+ ensure
166
+ self.table_name = nil
162
167
  end
163
168
  end
164
169
 
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def table_name
22
- "#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
22
+ "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
23
23
  end
24
24
 
25
25
  def table_exists?
@@ -86,8 +86,8 @@ module ActiveRecord
86
86
  # # Should return a scope, you can call 'super' here etc.
87
87
  # end
88
88
  # end
89
- def default_scope(scope = nil) # :doc:
90
- scope = Proc.new if block_given?
89
+ def default_scope(scope = nil, &block) # :doc:
90
+ scope = block if block_given?
91
91
 
92
92
  if scope.is_a?(Relation) || !scope.respond_to?(:call)
93
93
  raise ArgumentError,
@@ -100,7 +100,7 @@ module ActiveRecord
100
100
  self.default_scopes += [scope]
101
101
  end
102
102
 
103
- def build_default_scope(base_rel = nil)
103
+ def build_default_scope(relation = relation())
104
104
  return if abstract_class?
105
105
 
106
106
  if default_scope_override.nil?
@@ -111,15 +111,14 @@ module ActiveRecord
111
111
  # The user has defined their own default scope method, so call that
112
112
  evaluate_default_scope do
113
113
  if scope = default_scope
114
- (base_rel ||= relation).merge!(scope)
114
+ relation.merge!(scope)
115
115
  end
116
116
  end
117
117
  elsif default_scopes.any?
118
- base_rel ||= relation
119
118
  evaluate_default_scope do
120
- default_scopes.inject(base_rel) do |default_scope, scope|
119
+ default_scopes.inject(relation) do |default_scope, scope|
121
120
  scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
122
- default_scope.merge!(base_rel.instance_exec(&scope))
121
+ default_scope.instance_exec(&scope) || default_scope
123
122
  end
124
123
  end
125
124
  end
@@ -58,7 +58,7 @@ module ActiveRecord
58
58
  end
59
59
 
60
60
  def default_extensions # :nodoc:
61
- if scope = current_scope || build_default_scope
61
+ if scope = scope_for_association || build_default_scope
62
62
  scope.extensions
63
63
  else
64
64
  []
@@ -113,8 +113,8 @@ module ActiveRecord
113
113
  end
114
114
  end
115
115
 
116
- def self.create(connection, block = Proc.new)
117
- relation = block.call Params.new
116
+ def self.create(connection, callable = nil, &block)
117
+ relation = (callable || block).call Params.new
118
118
  query_builder, binds = connection.cacheable_query(self, relation.arel)
119
119
  bind_map = BindMap.new(binds)
120
120
  new(query_builder, bind_map, relation.klass)
@@ -11,6 +11,12 @@ module ActiveRecord
11
11
  # of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
12
12
  # already built around just accessing attributes on the model.
13
13
  #
14
+ # Every accessor comes with dirty tracking methods (+key_changed?+, +key_was+ and +key_change+) and
15
+ # methods to access the changes made during the last save (+saved_change_to_key?+, +saved_change_to_key+ and
16
+ # +key_before_last_save+).
17
+ #
18
+ # NOTE: There is no +key_will_change!+ method for accessors, use +store_will_change!+ instead.
19
+ #
14
20
  # Make sure that you declare the database column used for the serialized store as a text, so there's
15
21
  # plenty of room.
16
22
  #
@@ -49,6 +55,12 @@ module ActiveRecord
49
55
  # u.settings[:country] # => 'Denmark'
50
56
  # u.settings['country'] # => 'Denmark'
51
57
  #
58
+ # # Dirty tracking
59
+ # u.color = 'green'
60
+ # u.color_changed? # => true
61
+ # u.color_was # => 'black'
62
+ # u.color_change # => ['black', 'red']
63
+ #
52
64
  # # Add additional accessors to an existing store through store_accessor
53
65
  # class SuperUser < User
54
66
  # store_accessor :settings, :privileges, :servants
@@ -127,6 +139,42 @@ module ActiveRecord
127
139
  define_method(accessor_key) do
128
140
  read_store_attribute(store_attribute, key)
129
141
  end
142
+
143
+ define_method("#{accessor_key}_changed?") do
144
+ return false unless attribute_changed?(store_attribute)
145
+ prev_store, new_store = changes[store_attribute]
146
+ prev_store&.dig(key) != new_store&.dig(key)
147
+ end
148
+
149
+ define_method("#{accessor_key}_change") do
150
+ return unless attribute_changed?(store_attribute)
151
+ prev_store, new_store = changes[store_attribute]
152
+ [prev_store&.dig(key), new_store&.dig(key)]
153
+ end
154
+
155
+ define_method("#{accessor_key}_was") do
156
+ return unless attribute_changed?(store_attribute)
157
+ prev_store, _new_store = changes[store_attribute]
158
+ prev_store&.dig(key)
159
+ end
160
+
161
+ define_method("saved_change_to_#{accessor_key}?") do
162
+ return false unless saved_change_to_attribute?(store_attribute)
163
+ prev_store, new_store = saved_change_to_attribute(store_attribute)
164
+ prev_store&.dig(key) != new_store&.dig(key)
165
+ end
166
+
167
+ define_method("saved_change_to_#{accessor_key}") do
168
+ return unless saved_change_to_attribute?(store_attribute)
169
+ prev_store, new_store = saved_change_to_attribute(store_attribute)
170
+ [prev_store&.dig(key), new_store&.dig(key)]
171
+ end
172
+
173
+ define_method("#{accessor_key}_before_last_save") do
174
+ return unless saved_change_to_attribute?(store_attribute)
175
+ prev_store, _new_store = saved_change_to_attribute(store_attribute)
176
+ prev_store&.dig(key)
177
+ end
130
178
  end
131
179
  end
132
180
 
@@ -12,9 +12,9 @@ module ActiveRecord
12
12
 
13
13
  def resolve_column_aliases(hash)
14
14
  new_hash = hash.dup
15
- hash.each do |key, _|
16
- if (key.is_a?(Symbol)) && klass.attribute_alias?(key)
17
- new_hash[klass.attribute_alias(key)] = new_hash.delete(key)
15
+ hash.each_key do |key|
16
+ if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
17
+ new_hash[new_key] = new_hash.delete(key)
18
18
  end
19
19
  end
20
20
  new_hash
@@ -142,7 +142,9 @@ module ActiveRecord
142
142
  end
143
143
 
144
144
  def for_each
145
- databases = Rails.application.config.database_configuration
145
+ return {} unless defined?(Rails)
146
+
147
+ databases = Rails.application.config.load_database_yaml
146
148
  database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
147
149
 
148
150
  # if this is a single database application we don't want tasks for each primary database
@@ -153,6 +155,20 @@ module ActiveRecord
153
155
  end
154
156
  end
155
157
 
158
+ def raise_for_multi_db(environment = env, command:)
159
+ db_configs = ActiveRecord::Base.configurations.configs_for(env_name: environment)
160
+
161
+ if db_configs.count > 1
162
+ dbs_list = []
163
+
164
+ db_configs.each do |db|
165
+ dbs_list << "#{command}:#{db.spec_name}"
166
+ end
167
+
168
+ raise "You're using a multiple database application. To use `#{command}` you must run the namespaced task with a VERSION. Available tasks are #{dbs_list.to_sentence}."
169
+ end
170
+ end
171
+
156
172
  def create_current(environment = env)
157
173
  each_current_configuration(environment) { |configuration|
158
174
  create configuration
@@ -182,6 +198,25 @@ module ActiveRecord
182
198
  }
183
199
  end
184
200
 
201
+ def truncate_tables(configuration)
202
+ ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
203
+ table_names = ActiveRecord::Base.connection.tables
204
+ table_names -= [
205
+ SchemaMigration.table_name,
206
+ InternalMetadata.table_name
207
+ ]
208
+
209
+ ActiveRecord::Base.connection.truncate_tables(*table_names)
210
+ end
211
+ end
212
+ private :truncate_tables
213
+
214
+ def truncate_all(environment = env)
215
+ ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
216
+ truncate_tables db_config.config
217
+ end
218
+ end
219
+
185
220
  def migrate
186
221
  check_target_version
187
222
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  # = Active Record Touch Later
5
- module TouchLater
5
+ module TouchLater # :nodoc:
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  @_touch_time = current_time_from_proper_timezone
23
23
 
24
24
  surreptitiously_touch @_defer_touch_attrs
25
- self.class.connection.add_transaction_record self
25
+ add_to_transaction
26
26
 
27
27
  # touch the parents as we are not calling the after_save callbacks
28
28
  self.class.reflect_on_all_associations(:belongs_to).each do |r|
@@ -234,6 +234,12 @@ module ActiveRecord
234
234
  set_callback(:commit, :after, *args, &block)
235
235
  end
236
236
 
237
+ # Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
238
+ def after_save_commit(*args, &block)
239
+ set_options_for_callbacks!(args, on: [ :create, :update ])
240
+ set_callback(:commit, :after, *args, &block)
241
+ end
242
+
237
243
  # Shortcut for <tt>after_commit :hook, on: :create</tt>.
238
244
  def after_create_commit(*args, &block)
239
245
  set_options_for_callbacks!(args, on: :create)
@@ -349,18 +355,6 @@ module ActiveRecord
349
355
  clear_transaction_record_state
350
356
  end
351
357
 
352
- # Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
353
- # can be called.
354
- def add_to_transaction
355
- if has_transactional_callbacks?
356
- self.class.connection.add_transaction_record(self)
357
- else
358
- sync_with_transaction_state
359
- set_transaction_state(self.class.connection.transaction_state)
360
- end
361
- remember_transaction_record_state
362
- end
363
-
364
358
  # Executes +method+ within a transaction and captures its return value as a
365
359
  # status flag. If the status is true the transaction is committed, otherwise
366
360
  # a ROLLBACK is issued. In any case the status flag is returned.
@@ -370,9 +364,19 @@ module ActiveRecord
370
364
  def with_transaction_returning_status
371
365
  status = nil
372
366
  self.class.transaction do
373
- add_to_transaction
367
+ unless has_transactional_callbacks?
368
+ sync_with_transaction_state if @transaction_state&.finalized?
369
+ @transaction_state = self.class.connection.transaction_state
370
+ end
371
+ remember_transaction_record_state
372
+
374
373
  status = yield
375
374
  raise ActiveRecord::Rollback unless status
375
+ ensure
376
+ if has_transactional_callbacks? &&
377
+ (@_new_record_before_last_commit && !new_record? || _trigger_update_callback || _trigger_destroy_callback)
378
+ add_to_transaction
379
+ end
376
380
  end
377
381
  status
378
382
  end
@@ -382,13 +386,15 @@ module ActiveRecord
382
386
 
383
387
  # Save the new record state and id of a record so it can be restored later if a transaction fails.
384
388
  def remember_transaction_record_state
385
- @_start_transaction_state.reverse_merge!(
389
+ @_start_transaction_state ||= {
386
390
  id: id,
387
391
  new_record: @new_record,
388
392
  destroyed: @destroyed,
393
+ attributes: @attributes,
389
394
  frozen?: frozen?,
390
- )
391
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
395
+ level: 0
396
+ }
397
+ @_start_transaction_state[:level] += 1
392
398
  remember_new_record_before_last_commit
393
399
  end
394
400
 
@@ -402,27 +408,32 @@ module ActiveRecord
402
408
 
403
409
  # Clear the new record state and id of a record.
404
410
  def clear_transaction_record_state
405
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
411
+ return unless @_start_transaction_state
412
+ @_start_transaction_state[:level] -= 1
406
413
  force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
407
414
  end
408
415
 
409
416
  # Force to clear the transaction record state.
410
417
  def force_clear_transaction_record_state
411
- @_start_transaction_state.clear
418
+ @_start_transaction_state = nil
419
+ @transaction_state = nil
412
420
  end
413
421
 
414
422
  # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
415
- def restore_transaction_record_state(force = false)
416
- unless @_start_transaction_state.empty?
417
- transaction_level = (@_start_transaction_state[:level] || 0) - 1
418
- if transaction_level < 1 || force
419
- restore_state = @_start_transaction_state
420
- thaw
423
+ def restore_transaction_record_state(force_restore_state = false)
424
+ if restore_state = @_start_transaction_state
425
+ if force_restore_state || restore_state[:level] <= 1
421
426
  @new_record = restore_state[:new_record]
422
427
  @destroyed = restore_state[:destroyed]
423
- pk = self.class.primary_key
424
- if pk && _read_attribute(pk) != restore_state[:id]
425
- _write_attribute(pk, restore_state[:id])
428
+ @attributes = restore_state[:attributes].map do |attr|
429
+ value = @attributes.fetch_value(attr.name)
430
+ attr = attr.with_value_from_user(value) if attr.value != value
431
+ attr
432
+ end
433
+ @mutations_from_database = nil
434
+ @mutations_before_last_save = nil
435
+ if @attributes.fetch_value(@primary_key) != restore_state[:id]
436
+ @attributes.write_from_user(@primary_key, restore_state[:id])
426
437
  end
427
438
  freeze if restore_state[:frozen?]
428
439
  end
@@ -443,8 +454,10 @@ module ActiveRecord
443
454
  end
444
455
  end
445
456
 
446
- def set_transaction_state(state)
447
- @transaction_state = state
457
+ # Add the record to the current transaction so that the #after_rollback and #after_commit
458
+ # callbacks can be called.
459
+ def add_to_transaction
460
+ self.class.connection.add_transaction_record(self)
448
461
  end
449
462
 
450
463
  def has_transactional_callbacks?
@@ -464,19 +477,17 @@ module ActiveRecord
464
477
  # This method checks to see if the ActiveRecord object's state reflects
465
478
  # the TransactionState, and rolls back or commits the Active Record object
466
479
  # as appropriate.
467
- #
468
- # Since Active Record objects can be inside multiple transactions, this
469
- # method recursively goes through the parent of the TransactionState and
470
- # checks if the Active Record object reflects the state of the object.
471
480
  def sync_with_transaction_state
472
- update_attributes_from_transaction_state(@transaction_state)
473
- end
474
-
475
- def update_attributes_from_transaction_state(transaction_state)
476
- if transaction_state && transaction_state.finalized?
477
- restore_transaction_record_state(transaction_state.fully_rolledback?) if transaction_state.rolledback?
478
- force_clear_transaction_record_state if transaction_state.fully_committed?
479
- clear_transaction_record_state if transaction_state.fully_completed?
481
+ if transaction_state = @transaction_state
482
+ if transaction_state.fully_committed?
483
+ force_clear_transaction_record_state
484
+ elsif transaction_state.committed?
485
+ clear_transaction_record_state
486
+ elsif transaction_state.rolledback?
487
+ force_restore_state = transaction_state.fully_rolledback?
488
+ restore_transaction_record_state(force_restore_state)
489
+ clear_transaction_record_state
490
+ end
480
491
  end
481
492
  end
482
493
  end