Skip to content

Commit

Permalink
Have Active Record build tables for their aliases
Browse files Browse the repository at this point in the history
This may help with rzane#97.
  • Loading branch information
rtweeks committed Aug 25, 2020
1 parent b1c5975 commit 8dd5a89
Showing 1 changed file with 21 additions and 29 deletions.
50 changes: 21 additions & 29 deletions lib/baby_squeel/join_dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,44 +35,36 @@ def initialize(relation)
# a list (in order of chaining) of associations and finding
# the respective JoinAssociation at each level.
def find_alias(associations)
# If we tell join_dependency to construct its tables, Active Record
# handles building the correct aliases and attaching them to its
# JoinDepenencies.
join_dependency.send(:construct_tables!, join_dependency.send(:join_root))
join_association = find_join_association(associations)

# NOTE: Below is a hack. It does not work. In previous
# versions of Active Record, `#table` would ALWAYS return
# an instance of Arel::Table.
if join_association.table
join_association.table
else
# This literally does not work. This will often
# give you the wrong Arel::Table instance, which
# causes aliases in the query to be wrong.
join_association.base_klass.arel_table
end
join_association.table
end

private

def find_join_association(associations)
associations.inject(join_dependency.send(:join_root)) do |parent, assoc|
parent.children.find do |join_association|
reflections_equal?(
assoc._reflection,
join_association.reflection
)
end
end
join_root = join_dependency.send(:join_root)
steps = associations.map {|a| a._reflection.name}
association_from_steps(join_root, steps)
end

# Compare two reflections and see if they're the same.
def reflections_equal?(a, b)
comparable_reflection(a) == comparable_reflection(b)
end

# Get the parent of the reflection if it has one.
# In AR4, #parent_reflection returns [name, reflection]
# In AR5, #parent_reflection returns just a reflection
def comparable_reflection(reflection)
[*reflection.parent_reflection].last || reflection
# Search depth-first through the descendants of +current+ for successive
# associations in +steps+.
#
# Each generation of descent consumes one element of +steps+.
def association_from_steps(current, steps)
return current if steps.empty?
matching_assoc = nil
current.children.each do |child|
next unless child.reflection.name == steps.first
matching_assoc = association_from_steps(child, steps[1..-1])
break if matching_assoc
end
matching_assoc
end
end
end
Expand Down

0 comments on commit 8dd5a89

Please sign in to comment.