Class: ActiveRecord::Reflection::ThroughReflection

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

Overview

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

Instance Attribute Summary collapse

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?, #get_join_keys, #has_cached_counter?, #inverse_of, #inverse_updates_counter_in_memory?, #inverse_which_updates_counter_cache, #join_keys, #join_scope, #klass_join_scope, #primary_key_type, #quoted_table_name, #scope_chain, #table_name

Constructor Details

#initialize(delegate_reflection) ⇒ ThroughReflection

Returns a new instance of ThroughReflection.



778
779
780
781
782
# File 'lib/active_record/reflection.rb', line 778

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

Instance Attribute Details

#delegate_reflectionObject (readonly)

:nodoc:



774
775
776
# File 'lib/active_record/reflection.rb', line 774

def delegate_reflection
  @delegate_reflection
end

Instance Method Details

#add_as_polymorphic_through(reflection, seed) ⇒ Object



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

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

#add_as_source(seed) ⇒ Object



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

def add_as_source(seed)
  collect_join_reflections seed
end

#add_as_through(seed) ⇒ Object



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

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.



884
885
886
887
888
# File 'lib/active_record/reflection.rb', line 884

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

#association_primary_key_typeObject



890
891
892
# File 'lib/active_record/reflection.rb', line 890

def association_primary_key_type
  klass.type_for_attribute(association_primary_key.to_s)
end

#check_validity!Object



942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
# File 'lib/active_record/reflection.rb', line 942

def check_validity!
  if through_reflection.nil?
    raise HasManyThroughAssociationNotFoundError.new(active_record.name, 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.



852
853
854
855
856
# File 'lib/active_record/reflection.rb', line 852

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


846
847
848
# File 'lib/active_record/reflection.rb', line 846

def collect_join_chain
  collect_join_reflections [self]
end

#collect_join_reflections(seed) ⇒ Object



1000
1001
1002
1003
1004
1005
1006
1007
# File 'lib/active_record/reflection.rb', line 1000

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

#constraintsObject



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

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

#has_scope?Boolean

Returns:

  • (Boolean)


870
871
872
873
874
# File 'lib/active_record/reflection.rb', line 870

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

#join_id_for(owner) ⇒ Object

:nodoc:



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

def join_id_for(owner) # :nodoc:
  source_reflection.join_id_for(owner)
end

#join_scopes(table, predicate_builder) ⇒ Object

:nodoc:



862
863
864
# File 'lib/active_record/reflection.rb', line 862

def join_scopes(table, predicate_builder) # :nodoc:
  source_reflection.join_scopes(table, predicate_builder) + super
end

#klassObject



788
789
790
# File 'lib/active_record/reflection.rb', line 788

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)


877
878
879
# File 'lib/active_record/reflection.rb', line 877

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

#scopesObject



858
859
860
# File 'lib/active_record/reflection.rb', line 858

def scopes
  source_reflection.scopes + super
end

#source_optionsObject



930
931
932
# File 'lib/active_record/reflection.rb', line 930

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


809
810
811
# File 'lib/active_record/reflection.rb', line 809

def source_reflection
  through_reflection.klass._reflect_on_association(source_reflection_name)
end

#source_reflection_nameObject

:nodoc:



909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
# File 'lib/active_record/reflection.rb', line 909

def source_reflection_name # :nodoc:
  return @source_reflection_name if @source_reflection_name

  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

  @source_reflection_name = names.first
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]


905
906
907
# File 'lib/active_record/reflection.rb', line 905

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

#source_type_scopeObject



866
867
868
# File 'lib/active_record/reflection.rb', line 866

def source_type_scope
  through_reflection.klass.where(foreign_type => options[:source_type])
end

#through_optionsObject



934
935
936
# File 'lib/active_record/reflection.rb', line 934

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


825
826
827
# File 'lib/active_record/reflection.rb', line 825

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

#through_reflection?Boolean

Returns:

  • (Boolean)


784
785
786
# File 'lib/active_record/reflection.rb', line 784

def through_reflection?
  true
end