1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Fix collection.create to could be rolled back by after_save

In `_create_record`, explicit `transaction` block requires rollback
handling manually when `insert_record` is failed.

We need to handle it in `_create_record`, not in `insert_record`, since
our test cases expect a record added to target and returned even if
`insert_record` is failed,

Closes #31488.
This commit is contained in:
Ryuta Kamizono 2018-06-05 00:27:43 +09:00
parent 48c95cf7e5
commit 5dc72378b7
3 changed files with 30 additions and 3 deletions

View file

@ -358,14 +358,18 @@ module ActiveRecord
if attributes.is_a?(Array)
attributes.collect { |attr| _create_record(attr, raise, &block) }
else
record = build_record(attributes, &block)
transaction do
add_to_target(build_record(attributes, &block)) do |record|
insert_record(record, true, raise) {
result = nil
add_to_target(record) do
result = insert_record(record, true, raise) {
@_was_loaded = loaded?
@association_ids = nil
}
end
raise ActiveRecord::Rollback unless result
end
record
end
end
@ -443,7 +447,9 @@ module ActiveRecord
end
end
result && records
raise ActiveRecord::Rollback unless result
records
end
def replace_on_target(record, index, skip_callbacks)

View file

@ -2705,6 +2705,17 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
def test_create_children_could_be_rolled_back_by_after_save
firm = Firm.create!(name: "A New Firm, Inc")
assert_no_difference "Client.count" do
client = firm.clients.create(name: "New Client") do |cli|
cli.rollback_on_save = true
assert_not cli.rollback_on_create_called
end
assert client.rollback_on_create_called
end
end
private
def force_signal37_to_load_all_clients_of_firm

View file

@ -150,6 +150,16 @@ class Client < Company
throw :abort if throw_on_save
end
attr_accessor :rollback_on_save
after_save do
raise ActiveRecord::Rollback if rollback_on_save
end
attr_accessor :rollback_on_create_called
after_rollback(on: :create) do |client|
client.rollback_on_create_called = true
end
class RaisedOnDestroy < RuntimeError; end
attr_accessor :raise_on_destroy
before_destroy do