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_active_cached_counter?, #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.



992
993
994
995
996
997
998
999
1000
1001
# File 'activerecord/lib/active_record/reflection.rb', line 992

def initialize(delegate_reflection)
  super()

  @validated = false
  @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



1212
1213
1214
# File 'activerecord/lib/active_record/reflection.rb', line 1212

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

#add_as_source(seed) ⇒ Object



1208
1209
1210
# File 'activerecord/lib/active_record/reflection.rb', line 1208

def add_as_source(seed)
  collect_join_reflections seed
end

#add_as_through(seed) ⇒ Object



1216
1217
1218
# File 'activerecord/lib/active_record/reflection.rb', line 1216

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.



1101
1102
1103
1104
1105
1106
1107
1108
1109
# File 'activerecord/lib/active_record/reflection.rb', line 1101

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



1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
# File 'activerecord/lib/active_record/reflection.rb', line 1158

def check_validity!
  return if @validated

  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.normalized_reflections.keys

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

  check_validity_of_inverse!

  @validated = true
end

#clear_association_scope_cacheObject

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



1073
1074
1075
1076
1077
# File 'activerecord/lib/active_record/reflection.rb', line 1073

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


1067
1068
1069
# File 'activerecord/lib/active_record/reflection.rb', line 1067

def collect_join_chain
  collect_join_reflections [self]
end

#constraintsObject



1202
1203
1204
1205
1206
# File 'activerecord/lib/active_record/reflection.rb', line 1202

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

#deprecated_nested_reflectionsObject



1220
1221
1222
# File 'activerecord/lib/active_record/reflection.rb', line 1220

def deprecated_nested_reflections
  @deprecated_nested_reflections ||= collect_deprecated_nested_reflections
end

#has_scope?Boolean

Returns:

  • (Boolean)


1087
1088
1089
1090
1091
# File 'activerecord/lib/active_record/reflection.rb', line 1087

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

#join_primary_key(klass = self.klass) ⇒ Object



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

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

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

:nodoc:



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

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

#klassObject



1007
1008
1009
# File 'activerecord/lib/active_record/reflection.rb', line 1007

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

#nested?Boolean

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

Returns:

  • (Boolean)


1094
1095
1096
# File 'activerecord/lib/active_record/reflection.rb', line 1094

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

#scopesObject



1079
1080
1081
# File 'activerecord/lib/active_record/reflection.rb', line 1079

def scopes
  source_reflection.scopes + super
end

#source_optionsObject



1150
1151
1152
# File 'activerecord/lib/active_record/reflection.rb', line 1150

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


1028
1029
1030
1031
1032
# File 'activerecord/lib/active_record/reflection.rb', line 1028

def source_reflection
  return unless source_reflection_name

  through_reflection.klass._reflect_on_association(source_reflection_name)
end

#source_reflection_nameObject

:nodoc:



1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
# File 'activerecord/lib/active_record/reflection.rb', line 1130

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]


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

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

#through_optionsObject



1154
1155
1156
# File 'activerecord/lib/active_record/reflection.rb', line 1154

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


1046
1047
1048
# File 'activerecord/lib/active_record/reflection.rb', line 1046

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

#through_reflection?Boolean

Returns:

  • (Boolean)


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

def through_reflection?
  true
end