class ActiveRecord::CompositeKeyPreloader
Modified ActiveRecord::Associations::Preloader::Association
Attributes
klass[R]
owners[R]
reflection[R]
Public Class Methods
call(records, association, composite_key)
click to toggle source
The CompositeKeyPreloader
does the same as default rails preloader ( ActiveRecord::Associations::Preloader ) The difference: CompositeKeyPreloader
allows to reference another table by multiple columns
@param [Array<ActiveModel>] records Collection of active record models @param [Symbol] association ActiveRecord
model association name to preload @param [Array<Symbol>] composite_key Array of primary_keys, defines how association should be loaded.
Default Rails implementation does not allow to specify composite key (multi-column key)
# File lib/active_record/composite_key_preloader.rb, line 13 def self.call(records, association, composite_key) records_for_preload = records.reject { |record| record.association(association).loaded? } return if records_for_preload.blank? assoc = records.first.association(association) new(assoc.klass, records, assoc.reflection, composite_key).run end
new(klass, owners, reflection, composite_key)
click to toggle source
# File lib/active_record/composite_key_preloader.rb, line 21 def initialize(klass, owners, reflection, composite_key) @klass = klass @owners = owners @reflection = reflection @preloaded_records = [] @composite_key = composite_key end
Public Instance Methods
run()
click to toggle source
# File lib/active_record/composite_key_preloader.rb, line 29 def run records = load_records owners.each do |owner| owner_key = @composite_key.map { |key_name| owner[key_name] } associate_records_to_owner(owner, records[owner_key] || []) end end
Private Instance Methods
associate_records_to_owner(owner, records)
click to toggle source
# File lib/active_record/composite_key_preloader.rb, line 44 def associate_records_to_owner(owner, records) association = owner.association(reflection.name) association.loaded! raise 'no tested yet' if reflection.collection? association.target = records.first unless records.empty? end
load_records()
click to toggle source
# File lib/active_record/composite_key_preloader.rb, line 66 def load_records return {} if owner_keys.empty? # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000) # Make several smaller queries if necessary or make one query if the adapter supports it slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size) @preloaded_records = slices.flat_map do |slice| records_for(slice) end @preloaded_records.group_by do |record| @composite_key.map { |key_name| record[key_name] } end end
owner_keys()
click to toggle source
# File lib/active_record/composite_key_preloader.rb, line 52 def owner_keys @owner_keys ||= owners_by_key.keys end
owners_by_key()
click to toggle source
# File lib/active_record/composite_key_preloader.rb, line 56 def owners_by_key unless defined?(@owners_by_key) @owners_by_key = owners.each_with_object({}) do |owner, h| key = @composite_key.map { |key_name| owner[key_name] } h[key] = owner if key end end @owners_by_key end
records_for(ids)
click to toggle source
# File lib/active_record/composite_key_preloader.rb, line 80 def records_for(ids) composed_id_rows = ids.map { |multiple_ids| "ROW(#{multiple_ids.map(&:to_i).join(',')})" }.join(', ') klass.scope_for_association.where("(#{@composite_key.join(', ')}) IN (#{composed_id_rows})") end