Reset scope after collection delete

Reset scope after delete on collection association to clear stale
offsets of removed records.
This commit is contained in:
Gannon McGibbon 2018-12-03 17:24:29 -05:00
parent 6ca6478a67
commit 67bca35a64
4 changed files with 72 additions and 4 deletions

@ -1,3 +1,7 @@
* Reset scope after delete on collection association to clear stale offsets of removed records.
*Gannon McGibbon*
* Add the ability to prevent writes to a database for the duration of a block. * Add the ability to prevent writes to a database for the duration of a block.
Allows the application to prevent writes to a database. This can be useful when Allows the application to prevent writes to a database. This can be useful when

@ -500,7 +500,7 @@ def replace(other_array)
# Pet.find(1, 2, 3) # Pet.find(1, 2, 3)
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3) # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
def delete_all(dependent = nil) def delete_all(dependent = nil)
@association.delete_all(dependent) @association.delete_all(dependent).tap { reset_scope }
end end
# Deletes the records of the collection directly from the database # Deletes the records of the collection directly from the database
@ -527,7 +527,7 @@ def delete_all(dependent = nil)
# #
# Pet.find(1) # => Couldn't find Pet with id=1 # Pet.find(1) # => Couldn't find Pet with id=1
def destroy_all def destroy_all
@association.destroy_all @association.destroy_all.tap { reset_scope }
end end
# Deletes the +records+ supplied from the collection according to the strategy # Deletes the +records+ supplied from the collection according to the strategy
@ -646,7 +646,7 @@ def destroy_all
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ] # # ]
def delete(*records) def delete(*records)
@association.delete(*records) @association.delete(*records).tap { reset_scope }
end end
# Destroys the +records+ supplied and removes them from the collection. # Destroys the +records+ supplied and removes them from the collection.
@ -718,7 +718,7 @@ def delete(*records)
# #
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6) # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
def destroy(*records) def destroy(*records)
@association.destroy(*records) @association.destroy(*records).tap { reset_scope }
end end
## ##

@ -294,6 +294,14 @@ def test_delete_all_on_association_with_nil_dependency_is_the_same_as_not_loaded
assert_equal(expected_sql, loaded_sql) assert_equal(expected_sql, loaded_sql)
end end
def test_delete_all_on_association_clears_scope
author = Author.create!(name: "Gannon")
posts = author.posts
posts.create!(title: "test", body: "body")
posts.delete_all
assert_nil posts.first
end
def test_building_the_associated_object_with_implicit_sti_base_class def test_building_the_associated_object_with_implicit_sti_base_class
firm = DependentFirm.new firm = DependentFirm.new
company = firm.companies.build company = firm.companies.build
@ -1710,6 +1718,30 @@ def test_destroy_all
assert companies(:first_firm).clients_of_firm.reload.empty?, "37signals has no clients after destroy all and refresh" assert companies(:first_firm).clients_of_firm.reload.empty?, "37signals has no clients after destroy all and refresh"
end end
def test_destroy_all_on_association_clears_scope
author = Author.create!(name: "Gannon")
posts = author.posts
posts.create!(title: "test", body: "body")
posts.destroy_all
assert_nil posts.first
end
def test_destroy_on_association_clears_scope
author = Author.create!(name: "Gannon")
posts = author.posts
post = posts.create!(title: "test", body: "body")
posts.destroy(post)
assert_nil posts.first
end
def test_delete_on_association_clears_scope
author = Author.create!(name: "Gannon")
posts = author.posts
post = posts.create!(title: "test", body: "body")
posts.delete(post)
assert_nil posts.first
end
def test_dependence def test_dependence
firm = companies(:first_firm) firm = companies(:first_firm)
assert_equal 3, firm.clients.size assert_equal 3, firm.clients.size

@ -227,6 +227,14 @@ def test_delete_all_for_with_dependent_option_delete_all
end end
end end
def test_delete_all_on_association_clears_scope
post = Post.create!(title: "Rails 6", body: "")
people = post.people
people.create!(first_name: "Jeb")
people.delete_all
assert_nil people.first
end
def test_concat def test_concat
person = people(:david) person = people(:david)
post = posts(:thinking) post = posts(:thinking)
@ -401,6 +409,30 @@ def test_destroy_all
assert_empty posts(:welcome).people.reload assert_empty posts(:welcome).people.reload
end end
def test_destroy_all_on_association_clears_scope
post = Post.create!(title: "Rails 6", body: "")
people = post.people
people.create!(first_name: "Jeb")
people.destroy_all
assert_nil people.first
end
def test_destroy_on_association_clears_scope
post = Post.create!(title: "Rails 6", body: "")
people = post.people
person = people.create!(first_name: "Jeb")
people.destroy(person)
assert_nil people.first
end
def test_delete_on_association_clears_scope
post = Post.create!(title: "Rails 6", body: "")
people = post.people
person = people.create!(first_name: "Jeb")
people.delete(person)
assert_nil people.first
end
def test_should_raise_exception_for_destroying_mismatching_records def test_should_raise_exception_for_destroying_mismatching_records
assert_no_difference ["Person.count", "Reader.count"] do assert_no_difference ["Person.count", "Reader.count"] do
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:welcome).people.destroy(posts(:thinking)) } assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:welcome).people.destroy(posts(:thinking)) }