This fix corrects the logic in both the associated and missing methods. If the reflection.options hash has a key/value pair for :class_name it will use the association. This fixes 48651 which was not returning the correct value when using an enum association and querying for a record that had one key of the enum but was missing another. This also fixes 44964 where ActiveRecord was not properly aliasing self-referencing relations. If the reflection.options doesn’t contain the key/value pair then it will use the reflection.table_name. This fixes 47909 in which a user was trying to find records that were either missing a relation or were missing a relation that was ordered or unscoped or were missing a relation that was using extends in the query which resulted in an ActiveRecord Exception. It also allows for using extends in any of these capacities.

This commit is contained in:
paulreece 2023-08-02 11:47:13 -04:00
parent 05d93c7fa1
commit 4aa339cbbe
3 changed files with 93 additions and 3 deletions

@ -76,7 +76,7 @@ def associated(*associations)
associations.each do |association|
reflection = scope_association_reflection(association)
@scope.joins!(association)
if @scope.values.size > 1
if reflection.options[:class_name]
self.not(association => { reflection.association_primary_key => nil })
else
self.not(reflection.table_name => { reflection.association_primary_key => nil })
@ -108,7 +108,7 @@ def missing(*associations)
associations.each do |association|
reflection = scope_association_reflection(association)
@scope.left_outer_joins!(association)
if @scope.values.size > 1
if reflection.options[:class_name]
@scope.where!(association => { reflection.association_primary_key => nil })
else
@scope.where!(reflection.table_name => { reflection.association_primary_key => nil })

@ -48,8 +48,48 @@ def test_associated_merged_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.where.associated(:author).merge(Author.where(id: 1)).count
end
def test_associated_unscoped_merged_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.unscope(:where).where.associated(:author).merge(Author.where(id: 1)).count
end
def test_associated_unscoped_merged_joined_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.joins(:author).unscope(:where).where.associated(:author).merge(Author.where(id: 1)).count
end
def test_associated_unscoped_merged_joined_extended_early_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.extending(Post::NamedExtension).joins(:author).unscope(:where).where.associated(:author).merge(Author.where(id: 1)).count
end
def test_associated_unscoped_merged_joined_extended_late_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.joins(:author).unscope(:where).where.associated(:author).merge(Author.where(id: 1)).extending(Post::NamedExtension).count
end
def test_associated_ordered_merged_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.order(created_at: :desc).where.associated(:author).merge(Author.where(id: 1)).count
end
def test_associated_ordered_merged_joined_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.joins(:author).order(created_at: :desc).where.associated(:author).merge(Author.where(id: 1)).count
end
def test_associated_with_enum
assert_equal Author.find(2), Author.where.associated(:reading_listing).first
assert_equal Author.find(2), Author.joins(:reading_listing).where.associated(:reading_listing).first
end
def test_associated_with_enum_ordered
assert_equal Author.find(2), Author.order(id: :desc).joins(:reading_listing).where.associated(:reading_listing).first
end
def test_associated_with_enum_unscoped
assert_equal Author.find(2), Author.unscope(:where).joins(:reading_listing).where.associated(:reading_listing).first
end
def test_associated_with_enum_extended_early
assert_equal Author.find(2), Author.extending(Author::NamedExtension).order(id: :desc).joins(:reading_listing).where.associated(:reading_listing).first
end
def test_associated_with_enum_extended_late
assert_equal Author.find(2), Author.order(id: :desc).joins(:reading_listing).where.associated(:reading_listing).extending(Author::NamedExtension).first
end
def test_missing_with_association
@ -83,10 +123,50 @@ def test_missing_merged_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.where.missing(:author).merge(Author.where(id: 1)).count
end
def test_missing_unscoped_merged_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.joins(:author).unscope(:where).where.missing(:author).merge(Author.where(id: 1)).count
end
def test_missing_unscoped_merged_joined_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.unscope(:where).where.missing(:author).merge(Author.where(id: 1)).count
end
def test_missing_ordered_merged_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.order(created_at: :desc).where.missing(:author).merge(Author.where(id: 1)).count
end
def test_missing_ordered_merged_joined_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.joins(:author).order(created_at: :desc).where.missing(:author).merge(Author.where(id: 1)).count
end
def test_missing_unscoped_merged_joined_extended_early_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.extending(Post::NamedExtension).joins(:author).unscope(:where).where.missing(:author).merge(Author.where(id: 1)).count
end
def test_missing_unscoped_merged_joined_extended_late_with_scope_on_association
assert_equal Author.find(1).posts.count, Post.joins(:author).unscope(:where).where.missing(:author).merge(Author.where(id: 1)).extending(Post::NamedExtension).count
end
def test_missing_with_enum
assert_equal Author.find(2), Author.joins(:reading_listing).where.missing(:unread_listing).first
end
def test_missing_with_enum_ordered
assert_equal Author.find(2), Author.order(id: :desc).joins(:reading_listing).where.missing(:unread_listing).first
end
def test_missing_with_enum_unscoped
assert_equal Author.find(2), Author.unscope(:where).joins(:reading_listing).where.missing(:unread_listing).first
end
def test_missing_with_enum_extended_early
assert_equal Author.find(2), Author.extending(Author::NamedExtension).order(id: :desc).joins(:reading_listing).where.missing(:unread_listing).first
end
def test_missing_with_enum_extended_late
assert_equal Author.find(2), Author.order(id: :desc).joins(:reading_listing).where.missing(:unread_listing).extending(Author::NamedExtension).first
end
def test_not_inverts_where_clause
relation = Post.where.not(title: "hello")
expected_where_clause = Post.where(title: "hello").where_clause.invert

@ -239,6 +239,16 @@ def extension_method; end
attr_accessor :post_log
after_initialize :set_post_log
module NamedExtension
def author
"lifo"
end
def greeting
super + " :)"
end
end
def set_post_log
@post_log = []
end