123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::Reflection::ThroughReflection

Do not use. This class is for internal use only.
Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: ActiveRecord::Reflection::AbstractReflection
Defined in: activerecord/lib/active_record/reflection.rb

Overview

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

Class Method Summary

Instance Attribute Summary

AbstractReflection - Inherited

#counter_must_be_updated_by_has_many?,
#has_active_cached_counter?

Returns whether this association has a counter cache and its column values were backfilled (and so it is used internally by methods like size/any?/etc).

#has_cached_counter?

Returns whether this association has a counter cache.

#inverse_updates_counter_cache?
#inverse_updates_counter_in_memory?, #strict_loading?, #through_reflection?

Instance Method Summary

AbstractReflection - Inherited

#alias_candidate,
#build_association

Returns a new, unsaved instance of the associated class.

#build_scope, #chain, #check_validity_of_inverse!,
#class_name

Returns the class name for the macro.

#constraints, #counter_cache_column, #inverse_of,
#inverse_which_updates_counter_cache

We need to avoid the following situation:

#join_scope, #join_scopes, #klass_join_scope,
#scopes

Returns a list of scopes that should be applied for this ::ActiveRecord::Reflection object when querying the database.

#strict_loading_violation_message, #table_name,
#actual_source_reflection

FIXME: this is a horrible name.

#ensure_option_not_given_as_class!, #predicate_builder, #primary_key

Constructor Details

.new(delegate_reflection) ⇒ ThroughReflection

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 968

def initialize(delegate_reflection)
  super()
  @delegate_reflection = delegate_reflection
  @klass = delegate_reflection.options[:anonymous_class]
  @source_reflection_name = delegate_reflection.options[:source]

  ensure_option_not_given_as_class!(:source_type)
end

Instance Attribute Details

#active_record_primary_key (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 965

delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
         :active_record_primary_key, :join_foreign_key, to: :source_reflection

#association_foreign_key (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 965

delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
         :active_record_primary_key, :join_foreign_key, to: :source_reflection

#delegate_reflection (readonly, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1194

attr_reader :delegate_reflection

#foreign_key (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 965

delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
         :active_record_primary_key, :join_foreign_key, to: :source_reflection

#foreign_type (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 965

delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
         :active_record_primary_key, :join_foreign_key, to: :source_reflection

#has_scope?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1059

def has_scope?
  scope || options[:source_type] ||
    source_reflection.has_scope? ||
    through_reflection.has_scope?
end

#join_foreign_key (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 965

delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
         :active_record_primary_key, :join_foreign_key, to: :source_reflection

#join_id_for (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 965

delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
         :active_record_primary_key, :join_foreign_key, to: :source_reflection

#nested?Boolean (readonly)

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

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1066

def nested?
  source_reflection.through_reflection? || through_reflection.through_reflection?
end

#through_reflection?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 977

def through_reflection?
  true
end

#type (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 965

delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
         :active_record_primary_key, :join_foreign_key, to: :source_reflection

Instance Method Details

#actual_source_reflection (protected)

FIXME: this is a horrible name

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1189

def actual_source_reflection # FIXME: this is a horrible name
  source_reflection.actual_source_reflection
end

#add_as_polymorphic_through(reflection, seed)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1180

def add_as_polymorphic_through(reflection, seed)
  collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
end

#add_as_source(seed)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1176

def add_as_source(seed)
  collect_join_reflections seed
end

#add_as_through(seed)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1184

def add_as_through(seed)
  collect_join_reflections(seed + [self])
end

#association_primary_key(klass = nil)

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.

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1073

def association_primary_key(klass = nil)
  # Get the "actual" source reflection if the immediate source reflection has a
  # source reflection itself
  if primary_key = actual_source_reflection.options[:primary_key]
    @association_primary_key ||= -primary_key.to_s
  else
    primary_key(klass || self.klass)
  end
end

#check_validity!

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1130

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

  if through_reflection.polymorphic?
    if has_one?
      raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
    else
      raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
    end
  end

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

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

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

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

  if parent_reflection.nil?
    reflections = active_record.reflections.keys.map(&:to_sym)

    if reflections.index(through_reflection.name) > reflections.index(name)
      raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
    end
  end

  check_validity_of_inverse!
end

#clear_association_scope_cache

This is for clearing cache on the reflection. Useful for tests that need to compare SQL queries on associations.

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1045

def clear_association_scope_cache # :nodoc:
  delegate_reflection.clear_association_scope_cache
  source_reflection.clear_association_scope_cache
  through_reflection.clear_association_scope_cache
end

#collect_join_chain

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.

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

tags_reflection = Post.reflect_on_association(:tags)
tags_reflection.chain
# => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
      <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1039

def collect_join_chain
  collect_join_reflections [self]
end

#collect_join_reflections(seed) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1196

def collect_join_reflections(seed)
  a = source_reflection.add_as_source seed
  if options[:source_type]
    through_reflection.add_as_polymorphic_through self, a
  else
    through_reflection.add_as_through a
  end
end

#constraints

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1170

def constraints
  scope_chain = source_reflection.constraints
  scope_chain << scope if scope
  scope_chain
end

#derive_class_name (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1207

def derive_class_name
  # get the class_name of the belongs_to association of the through reflection
  options[:source_type] || source_reflection.class_name
end

#inverse_name (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1205

def inverse_name; delegate_reflection.send(:inverse_name); end

#join_primary_key(klass = self.klass)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1083

def join_primary_key(klass = self.klass)
  source_reflection.join_primary_key(klass)
end

#join_scopes(table, predicate_builder, klass = self.klass, record = nil)

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1055

def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
  source_reflection.join_scopes(table, predicate_builder, klass, record) + super
end

#klass

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 981

def klass
  @klass ||= delegate_reflection.compute_class(class_name)
end

#scopes

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1051

def scopes
  source_reflection.scopes + super
end

#source_options

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1122

def source_options
  source_reflection.options
end

#source_reflection

Returns 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

class Tagging < ActiveRecord::Base
  belongs_to :post
  belongs_to :tag
end

tags_reflection = Post.reflect_on_association(:tags)
tags_reflection.source_reflection
# => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1002

def source_reflection
  through_reflection.klass._reflect_on_association(source_reflection_name)
end

#source_reflection_name

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1102

def source_reflection_name # :nodoc:
  @source_reflection_name ||= begin
    names = [name.to_s.singularize, name].collect(&:to_sym).uniq
    names = names.find_all { |n|
      through_reflection.klass._reflect_on_association(n)
    }

    if names.length > 1
      raise AmbiguousSourceReflectionForThroughAssociation.new(
        active_record.name,
        macro,
        name,
        options,
        source_reflection_names
      )
    end
    names.first
  end
end

#source_reflection_names

Gets an array of possible :through source reflection names in both singular and plural form.

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

tags_reflection = Post.reflect_on_association(:tags)
tags_reflection.source_reflection_names
# => [:tag, :tags]
[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1098

def source_reflection_names
  options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
end

#through_options

[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1126

def through_options
  through_reflection.options
end

#through_reflection (readonly)

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)
tags_reflection.through_reflection
# => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
[ GitHub ]

  
# File 'activerecord/lib/active_record/reflection.rb', line 1018

def through_reflection
  active_record._reflect_on_association(options[:through])
end