Class: ActiveRecord::Reflection::ThroughReflection

Inherits:
AssociationReflection show all
Defined in:
activerecord/lib/active_record/reflection.rb

Overview

Holds all the meta-data about a :through association as it was specified in the Active Record class.

Instance Attribute Summary

Attributes inherited from AssociationReflection

#original_build_association_called

Attributes inherited from MacroReflection

#active_record, #macro, #name, #options, #plural_name

Instance Method Summary collapse

Methods inherited from AssociationReflection

#active_record_primary_key, #association_class, #association_foreign_key, #belongs_to?, #build_association, #check_validity_of_inverse!, #collection?, #columns, #counter_cache_column, #foreign_key, #foreign_type, #has_inverse?, #initialize, #inverse_of, #klass, #polymorphic_inverse_of, #primary_key_column, #primary_key_name, #quoted_table_name, #reset_column_information, #table_name, #type, #validate?

Methods inherited from MacroReflection

#==, #class_name, #initialize, #klass, #sanitized_conditions

Constructor Details

This class inherits a constructor from ActiveRecord::Reflection::AssociationReflection

Instance Method Details

#association_primary_key(klass = nil) ⇒ Object

We want to use the klass from this reflection, rather than just delegate straight to the source_reflection, because the source_reflection may be polymorphic. We still need to respect the source_reflection's :primary_key option, though.



480
481
482
483
484
485
486
487
488
489
# File 'activerecord/lib/active_record/reflection.rb', line 480

def association_primary_key(klass = nil)
  # Get the "actual" source reflection if the immediate source reflection has a
  # source reflection itself
  source_reflection = self.source_reflection
  while source_reflection.source_reflection
    source_reflection = source_reflection.source_reflection
  end

  source_reflection.options[:primary_key] || primary_key(klass || self.klass)
end

#chainObject

Returns an array of reflections which are involved in this association. Each item in the array corresponds to a table which will be part of the query for this association.

The chain is built by recursively calling #chain on the source reflection and the through reflection. The base case for the recursion is a normal association, which just returns

self

as its #chain.



417
418
419
420
421
422
423
# File 'activerecord/lib/active_record/reflection.rb', line 417

def chain
  @chain ||= begin
    chain = source_reflection.chain + through_reflection.chain
    chain[0] = self # Use self so we don't lose the information from :source_type
    chain
  end
end

#check_validity!Object



507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
# File 'activerecord/lib/active_record/reflection.rb', line 507

def check_validity!
  if through_reflection.nil?
    raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
  end

  if through_reflection.options[:polymorphic]
    raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
  end

  if source_reflection.nil?
    raise HasManyThroughSourceAssociationNotFoundError.new(self)
  end

  if options[:source_type] && source_reflection.options[:polymorphic].nil?
    raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
  end

  if source_reflection.options[:polymorphic] && options[:source_type].nil?
    raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
  end

  if macro == :has_one && through_reflection.collection?
    raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
  end

  check_validity_of_inverse!
end

#conditionsObject

Consider the following example:

class Person
  has_many :articles
  has_many :comment_tags, :through => :articles
end

class Article
  has_many :comments
  has_many :comment_tags, :through => :comments, :source => :tags
end

class Comment
  has_many :tags
end

There may be conditions on Person.comment_tags, Article.comment_tags and/or Comment.tags, but only Comment.tags will be represented in the #chain. So this method creates an array of conditions corresponding to the chain. Each item in the #conditions array corresponds to an item in the #chain, and is itself an array of conditions from an arbitrary number of relevant reflections, plus any :source_type or polymorphic :as constraints.



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'activerecord/lib/active_record/reflection.rb', line 446

def conditions
  @conditions ||= begin
    conditions = source_reflection.conditions

    # Add to it the conditions from this reflection if necessary.
    conditions.first << options[:conditions] if options[:conditions]

    through_conditions = through_reflection.conditions

    if options[:source_type]
      through_conditions.first << { foreign_type => options[:source_type] }
    end

    # Recursively fill out the rest of the array from the through reflection
    conditions += through_conditions

    # And return
    conditions
  end
end

#nested?Boolean

A through association is nested iff there would be more than one join table



473
474
475
# File 'activerecord/lib/active_record/reflection.rb', line 473

def nested?
  chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
end

#source_macroObject

The macro used by the source association



468
469
470
# File 'activerecord/lib/active_record/reflection.rb', line 468

def source_macro
  source_reflection.source_macro
end

#source_optionsObject



499
500
501
# File 'activerecord/lib/active_record/reflection.rb', line 499

def source_options
  source_reflection.options
end

#source_reflectionObject

Gets the source of the through reflection. It checks both a singularized and pluralized form for :belongs_to or :has_many.

class Post < ActiveRecord::Base
  has_many :taggings
  has_many :tags, :through => :taggings
end


392
393
394
# File 'activerecord/lib/active_record/reflection.rb', line 392

def source_reflection
  @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
end

#source_reflection_namesObject

Gets an array of possible :through source reflection names:

[:singularized, :pluralized]


495
496
497
# File 'activerecord/lib/active_record/reflection.rb', line 495

def source_reflection_names
  @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
end

#through_optionsObject



503
504
505
# File 'activerecord/lib/active_record/reflection.rb', line 503

def through_options
  through_reflection.options
end

#through_reflectionObject

Returns the AssociationReflection object specified in the :through option of a HasManyThrough or HasOneThrough association.

class Post < ActiveRecord::Base
  has_many :taggings
  has_many :tags, :through => :taggings
end

tags_reflection = Post.reflect_on_association(:tags)
taggings_reflection = tags_reflection.through_reflection


407
408
409
# File 'activerecord/lib/active_record/reflection.rb', line 407

def through_reflection
  @through_reflection ||= active_record.reflect_on_association(options[:through])
end