Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes count for collection object when there's explicit rollback in callbacks #31488

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 47 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
* Prevent ActiveRecord::Rollback to be swallowed and not reflect for collection objects

Previously collection models used to get persisted even when
there's an explicit ActiveRecord::Rollback in a callback and
change the count of the collection obect without any regard to
the callback specification. This used to happen because ActiveRecord::Rollback
used to be silently swallowed and the rest of the pipeline triggered
commit from it's previously assigned data values.

For example:

```
class Post < ApplicationRecord
has_many :comments
end

class Comment < ApplicationRecord
belongs_to :post
after_create -> { raise ActiveRecord::Rollback }
end
```

Before:
```
post = Post.first
post.comments.count == true # true

post.comments.create
post.comments.count == 0 # false
```

After:
```
post = Post.first
post.comments.count == true # true

post.comments.create
post.comments.count == 0 # true
```

This PR fixes the problem and makes the count consistent
if there's a rollback on a collection object.

Fixes #28836 and #28536

*Sayan Chakraborty*

* Undefine attribute methods on descendants when resetting column
information.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,14 @@ def _create_record(attributes, raise = false, &block)

# Do the relevant stuff to insert the given record into the association collection.
def insert_record(record, validate = true, raise = false, &block)
if raise
record.save!(validate: validate, &block)
else
record.save(validate: validate, &block)
end
saved = if raise
record.save!(validate: validate, &block)
else
record.save(validate: validate, &block)
end

raise ActiveRecord::Rollback unless saved
saved
end

def delete_or_destroy(records, method)
Expand Down
15 changes: 15 additions & 0 deletions activerecord/test/cases/associations/has_many_associations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2543,6 +2543,21 @@ def self.name
end
end

def test_count_does_not_change_for_collection_if_rolled_back
klass = Class.new(Bulb) do
def self.name; "Bulb"; end
belongs_to :car, class_name: "Car"
after_create { raise ActiveRecord::Rollback }
end

car = Car.create!
initial_count = car.bulbs.count

car.bulbs << klass.create

assert_equal initial_count, car.bulbs.count
end

class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base
self.table_name = "authors"
has_many :posts_with_error_destroying,
Expand Down