Let's imagine a User
model with a has many posts relationship. It has an associated scope to ensure we only get relevant posts (i.e., posts not yet deleted).
class User
has_many :posts, -> { not_deleted }, inverse_of :user
end
class Post
belongs_to :user, inverse_of: :posts
scope :not_deleted, -> { where(deleted: false) }
end
Now, the following exercises the "bug".
user = User.create(...)
user.posts.create(deleted: false)
user.posts.create(deleted: true)
# This seems correct
user.posts # => [Post]
# This does not
user.posts.to_a # => [Post, Post]
I think I've tracked it down to ActiveRecord::Associations::CollectionAssociation#merge_target_lists. merge_target_lists
take a persisted
collection and a memory
collection and consolidate them. The problem (as I see it) is that we have persisted records in the memory
collection that aren't in the persisted
collection, but we're still adding them together blindly. Since the persisted
collection should be an up-to-date collection of the persisted records, any in-memory records that are persisted should not be accounted for.