class OccamsRecord::EagerLoaders::Through
Handles :through associations for has_many and has_one. Polymorphic associations are not supported.
Constants
- Link
Public Class Methods
new(*args)
click to toggle source
See documentation for OccamsRecord::EagerLoaders::Base
.
Calls superclass method
OccamsRecord::EagerLoaders::Base::new
# File lib/occams-record/eager_loaders/through.rb, line 12 def initialize(*args) super unless @ref.macro == :has_one or @ref.macro == :has_many raise ArgumentError, "#{@ref.active_record.name}##{@ref.name} cannot be eager loaded because only `has_one` and `has_many` are supported for `through` associations" end if (polys = @ref.chain.select(&:polymorphic?)).any? names = polys.map { |r| "#{r.active_record.name}##{r.name}" } raise ArgumentError, "#{@ref.active_record.name}##{@ref.name} cannot be eager loaded because these `through` associations are polymorphic: #{names.join ', '}" end unless @optimizer == :none or @optimizer == :select raise ArgumentError, "Unrecognized optimizer '#{@optimizer}'" end chain = @ref.chain.reverse @chain = chain.each_with_index.map { |x, i| Link.new(x.source_reflection.name, x.source_reflection.macro, x, chain[i + 1]) } @chain[-1].name = @as if @as and @chain.any? @loader = build_loader end
Public Instance Methods
run(rows, query_logger: nil, measurements: nil)
click to toggle source
# File lib/occams-record/eager_loaders/through.rb, line 39 def run(rows, query_logger: nil, measurements: nil) @loader.run(rows, query_logger: query_logger, measurements: measurements) attr_set = "#{name}=" rows.each do |row| row.send(attr_set, reduce(row)) end nil end
through_name()
click to toggle source
TODO make not hacky
# File lib/occams-record/eager_loaders/through.rb, line 35 def through_name @loader.name end
Private Instance Methods
build_loader()
click to toggle source
# File lib/occams-record/eager_loaders/through.rb, line 72 def build_loader head = @chain[0] links = @chain[1..-2] tail = @chain[-1] outer_loader = EagerLoaders.fetch!(head.ref).new(head.ref, optimized_select(head)) links. reduce(outer_loader) { |loader, link| loader.nest(link.ref.source_reflection.name, optimized_select(link)) }. nest(tail.ref.source_reflection.name, @scope, use: @use, as: @as) outer_loader end
optimized_select(link)
click to toggle source
# File lib/occams-record/eager_loaders/through.rb, line 88 def optimized_select(link) return nil unless @optimizer == :select cols = case link.macro when :belongs_to [link.ref.association_primary_key] when :has_one, :has_many [link.ref.association_primary_key, link.ref.foreign_key] else raise "Unsupported through chain link type '#{link.macro}'" end case link.next_ref.source_reflection.macro when :belongs_to cols << link.next_ref.foreign_key end ->(q) { q.select(cols.join(", ")) } end
reduce(node, depth = 0)
click to toggle source
# File lib/occams-record/eager_loaders/through.rb, line 50 def reduce(node, depth = 0) link = @chain[depth] case link&.macro when nil node when :has_many node.send(link.name).reduce(Set.new) { |a, child| result = reduce(child, depth + 1) case result when nil then a when Array then a + result else a << result end }.to_a when :has_one, :belongs_to child = node.send(link.name) child ? reduce(child, depth + 1) : nil else raise "Unsupported through chain link type '#{link.macro}'" end end