Class: ActiveRecord::Reflection::ThroughReflection

Inherits:
AbstractReflection show all
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.

Instance Method Summary collapse

Methods inherited from AbstractReflection

#alias_candidate, #build_association, #build_scope, #chain, #check_validity_of_inverse!, #class_name, #counter_cache_column, #counter_must_be_updated_by_has_many?, #has_cached_counter?, #inverse_of, #inverse_updates_counter_in_memory?, #inverse_which_updates_counter_cache, #join_scope, #klass_join_scope, #strict_loading?, #strict_loading_violation_message, #table_name

Constructor Details

#initialize(delegate_reflection) ⇒ ThroughReflection

Returns a new instance of ThroughReflection.



904
905
906
907
908
909
910
911
# File 'activerecord/lib/active_record/reflection.rb', line 904

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 Method Details

#add_as_polymorphic_through(reflection, seed) ⇒ Object



1116
1117
1118
# File 'activerecord/lib/active_record/reflection.rb', line 1116

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

#add_as_source(seed) ⇒ Object



1112
1113
1114
# File 'activerecord/lib/active_record/reflection.rb', line 1112

def add_as_source(seed)
  collect_join_reflections seed
end

#add_as_through(seed) ⇒ Object



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

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

#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.



1009
1010
1011
1012
1013
1014
1015
1016
1017
# File 'activerecord/lib/active_record/reflection.rb', line 1009

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!Object



1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
# File 'activerecord/lib/active_record/reflection.rb', line 1066

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_cacheObject

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



981
982
983
984
985
# File 'activerecord/lib/active_record/reflection.rb', line 981

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_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.

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>]


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

def collect_join_chain
  collect_join_reflections [self]
end

#constraintsObject



1106
1107
1108
1109
1110
# File 'activerecord/lib/active_record/reflection.rb', line 1106

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

#has_scope?Boolean

Returns:

  • (Boolean)


995
996
997
998
999
# File 'activerecord/lib/active_record/reflection.rb', line 995

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

#join_primary_key(klass = self.klass) ⇒ Object



1019
1020
1021
# File 'activerecord/lib/active_record/reflection.rb', line 1019

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

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

:nodoc:



991
992
993
# File 'activerecord/lib/active_record/reflection.rb', line 991

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

#klassObject



917
918
919
# File 'activerecord/lib/active_record/reflection.rb', line 917

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

#nested?Boolean

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

Returns:

  • (Boolean)


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

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

#scopesObject



987
988
989
# File 'activerecord/lib/active_record/reflection.rb', line 987

def scopes
  source_reflection.scopes + super
end

#source_optionsObject



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

def source_options
  source_reflection.options
end

#source_reflectionObject

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">


938
939
940
# File 'activerecord/lib/active_record/reflection.rb', line 938

def source_reflection
  through_reflection.klass._reflect_on_association(source_reflection_name)
end

#source_reflection_nameObject

:nodoc:



1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
# File 'activerecord/lib/active_record/reflection.rb', line 1038

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_namesObject

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]


1034
1035
1036
# File 'activerecord/lib/active_record/reflection.rb', line 1034

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

#through_optionsObject



1062
1063
1064
# File 'activerecord/lib/active_record/reflection.rb', line 1062

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


954
955
956
# File 'activerecord/lib/active_record/reflection.rb', line 954

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

#through_reflection?Boolean

Returns:

  • (Boolean)


913
914
915
# File 'activerecord/lib/active_record/reflection.rb', line 913

def through_reflection?
  true
end