1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Refactor Preloader to remove AlreadyLoaded class

Part 2 of ?

It felt strange to have an `AlreadyLoaded` class when we have 2 classes
for the preloaders. `ThroughAssociation` already super's to
`Association`. Instead of having a new class that provides a confusing
indirection, we can get and set all the information we need on
`Association` inside of `run`.

In addition, the `already_loaded` instance variable on
`ThroughAssociation` actually meant something different than
`already_loaded` that was checked for `AlreadyLoaded`. By pushing this
check down we can get rid of the `initialize` in `ThroughAssociation`
entirely since then all it's doing is calling `super`.

Co-authored-by: John Crepezzi <john.crepezzi@gmail.com>
This commit is contained in:
eileencodes 2020-12-14 11:20:19 -05:00
parent 0ecf163cc8
commit 93f37d5295
No known key found for this signature in database
GPG key ID: BA5C575120BBE8DF
3 changed files with 18 additions and 35 deletions

View file

@ -135,7 +135,7 @@ module ActiveRecord
def preloaders_for_reflection(reflection, reflection_records)
reflection_records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope, associate_by_default).run
preloader_for(reflection).new(rhs_klass, rs, reflection, scope, associate_by_default).run
end
end
@ -149,37 +149,10 @@ module ActiveRecord
h
end
class AlreadyLoaded # :nodoc:
def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
@owners = owners
@reflection = reflection
end
def run
self
end
def preloaded_records
@preloaded_records ||= records_by_owner.flat_map(&:last)
end
def records_by_owner
@records_by_owner ||= owners.index_with do |owner|
Array(owner.association(reflection.name).target)
end
end
private
attr_reader :owners, :reflection
end
# Returns a class containing the logic needed to load preload the data
# and attach it to a relation. The class returned implements a `run` method
# that accepts a preloader.
def preloader_for(reflection, owners)
if owners.all? { |o| o.association(reflection.name).loaded? }
return AlreadyLoaded
end
def preloader_for(reflection)
reflection.check_preloadable!
if reflection.options[:through]

View file

@ -11,9 +11,16 @@ module ActiveRecord
@preload_scope = preload_scope
@associate = associate_by_default || !preload_scope || preload_scope.empty_scope?
@model = owners.first && owners.first.class
@already_loaded = owners.all? { |o| o.association(reflection.name).loaded? }
end
def run
if @already_loaded
fetch_from_preloaded_records
return self
end
records = records_by_owner
owners.each do |owner|
@ -38,6 +45,14 @@ module ActiveRecord
private
attr_reader :owners, :reflection, :preload_scope, :model, :klass
def fetch_from_preloaded_records
@records_by_owner = owners.index_with do |owner|
Array(owner.association(reflection.name).target)
end
@preloaded_records = records_by_owner.flat_map(&:last)
end
def load_records
# owners can be duplicated when a relation has a collection association join
# #compare_by_identity makes such owners different hash keys

View file

@ -4,11 +4,6 @@ module ActiveRecord
module Associations
class Preloader
class ThroughAssociation < Association # :nodoc:
def initialize(*)
super
@already_loaded = owners.first.association(through_reflection.name).loaded?
end
def preloaded_records
@preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
end
@ -21,7 +16,7 @@ module ActiveRecord
@records_by_owner = owners.each_with_object({}) do |owner, result|
through_records = through_records_by_owner[owner] || []
if @already_loaded
if owners.first.association(through_reflection.name).loaded?
if source_type = reflection.options[:source_type]
through_records = through_records.select do |record|
record[reflection.foreign_type] == source_type