class ActiveRecord::Associations::ClassMethods::JoinDependency
:nodoc:
def build(associations, parent = nil)
def build(associations, parent = nil) parent ||= @joins.last case associations when Symbol, String reflection = parent.reflections[associations.to_s.intern] or raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?" @reflections << reflection @joins << build_join_association(reflection, parent) when Array associations.each do |association| build(association, parent) end when Hash associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| build(name, parent) build(associations[name]) end else raise ConfigurationError, associations.inspect end end
def build_join_association(reflection, parent)
def build_join_association(reflection, parent) JoinAssociation.new(reflection, self, parent) end
def construct(parent, associations, joins, row)
def construct(parent, associations, joins, row) case associations when Symbol, String join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name } raise(ConfigurationError, "No such association") if join.nil? joins.delete(join) construct_association(parent, join, row) when Array associations.each do |association| construct(parent, association, joins, row) end when Hash associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name| join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name } raise(ConfigurationError, "No such association") if join.nil? association = construct_association(parent, join, row) joins.delete(join) construct(association, associations[name], joins, row) if association end else raise ConfigurationError, associations.inspect end end
def construct_association(record, join, row)
def construct_association(record, join, row) case join.reflection.macro when :has_many, :has_and_belongs_to_many collection = record.send(join.reflection.name) collection.loaded return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil? association = join.instantiate(row) collection.target.push(association) collection.__send__(:set_inverse_instance, association, record) when :has_one return if record.id.to_s != join.parent.record_id(row).to_s return if record.instance_variable_defined?("@#{join.reflection.name}") association = join.instantiate(row) unless row[join.aliased_primary_key].nil? set_target_and_inverse(join, association, record) when :belongs_to return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil? association = join.instantiate(row) set_target_and_inverse(join, association, record) else raise ConfigurationError, "unknown macro: #{join.reflection.macro}" end return association end
def initialize(base, associations, joins)
def initialize(base, associations, joins) @joins = [JoinBase.new(base, joins)] @associations = associations @reflections = [] @base_records_hash = {} @base_records_in_order = [] @table_aliases = Hash.new { |aliases, table| aliases[table] = 0 } @table_aliases[base.table_name] = 1 build(associations) end
def instantiate(rows)
def instantiate(rows) rows.each_with_index do |row, i| primary_id = join_base.record_id(row) unless @base_records_hash[primary_id] @base_records_in_order << (@base_records_hash[primary_id] = join_base.instantiate(row)) end construct(@base_records_hash[primary_id], @associations, join_associations.dup, row) end remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations) return @base_records_in_order end
def join_associations
def join_associations @joins[1..-1].to_a end
def join_base
def join_base @joins[0] end
def join_for_table_name(table_name)
def join_for_table_name(table_name) join = (@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first) rescue nil return join unless join.nil? @joins.select{|j|j.is_a?(JoinAssociation) && j.aliased_join_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil end
def joins_for_table_name(table_name)
def joins_for_table_name(table_name) join = join_for_table_name(table_name) result = nil if join && join.is_a?(JoinAssociation) result = [join] if join.parent && join.parent.is_a?(JoinAssociation) result = joins_for_table_name(join.parent.aliased_table_name) + result end end result end
def remove_duplicate_results!(base, records, associations)
def remove_duplicate_results!(base, records, associations) case associations when Symbol, String reflection = base.reflections[associations] if reflection && reflection.collection? records.each { |record| record.send(reflection.name).target.uniq! } end when Array associations.each do |association| remove_duplicate_results!(base, records, association) end when Hash associations.keys.each do |name| reflection = base.reflections[name] parent_records = records.map do |record| descendant = record.send(reflection.name) next unless descendant descendant.target.uniq! if reflection.collection? descendant end.flatten.compact remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty? end end end
def set_target_and_inverse(join, association, record)
def set_target_and_inverse(join, association, record) association_proxy = record.send("set_#{join.reflection.name}_target", association) association_proxy.__send__(:set_inverse_instance, association, record) end