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:
parent
0ecf163cc8
commit
93f37d5295
3 changed files with 18 additions and 35 deletions
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue