Class: ActiveRecord::Reflection::AssociationReflection
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Subclasses:
|
|
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
|
|
Instance Chain:
|
|
Inherits: |
ActiveRecord::Reflection::MacroReflection
|
Defined in: | activerecord/lib/active_record/reflection.rb |
Overview
Holds all the metadata about an association as it was specified in the Active Record class.
Class Method Summary
MacroReflection
- Inherited
AbstractReflection
- Inherited
Instance Attribute Summary
-
#belongs_to? ⇒ Boolean
readonly
Returns
true
ifself
is abelongs_to
reflection. -
#collection? ⇒ Boolean
readonly
Returns whether or not this association reflection is for a collection association.
- #foreign_type readonly
- #has_inverse? ⇒ Boolean readonly
-
#has_one? ⇒ Boolean
readonly
Returns
true
ifself
is ahas_one
reflection. - #has_scope? ⇒ Boolean readonly
- #nested? ⇒ Boolean readonly
- #parent_reflection rw
- #polymorphic? ⇒ Boolean readonly
- #type readonly
-
#validate? ⇒ Boolean
readonly
Returns whether or not the association should be validated as part of the parent’s validation.
MacroReflection
- Inherited
#active_record, #autosave=, | |
#name | Returns the name of the macro. |
#options | Returns the hash of options used for the macro. |
#scope, #plural_name |
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 |
#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
- #active_record_primary_key
- #add_as_polymorphic_through(reflection, seed)
- #add_as_source(seed)
- #add_as_through(seed)
- #association_class
- #association_foreign_key
- #association_primary_key(klass = nil)
- #association_scope_cache(klass, owner, &block)
- #check_eager_loadable!
- #check_validity!
-
#clear_association_scope_cache
This is for clearing cache on the reflection.
-
#collect_join_chain
A chain of reflections from this one back to the owner.
- #compute_class(name)
- #extensions
- #foreign_key(infer_from_inverse_of: true)
- #join_foreign_key
- #join_id_for(owner)
- #join_primary_key(klass = nil)
- #join_primary_type
- #join_table
-
#macro
Returns the macro type.
- #polymorphic_inverse_of(associated_class)
- #polymorphic_name
- #source_reflection
- #through_reflection
-
#automatic_inverse_of
private
returns either
nil
or the inverse association name that it finds. -
#can_find_inverse_of_automatically?(reflection, inverse_reflection = false) ⇒ Boolean
private
Checks to see if the reflection doesn’t have any options that prevent us from being able to guess the inverse automatically.
- #derive_class_name private
- #derive_fk_query_constraints(foreign_key) private
- #derive_foreign_key(infer_from_inverse_of: true) private
- #derive_join_table private
-
#inverse_name
private
Attempts to find the inverse association name automatically.
-
#scope_allows_automatic_inverse_of?(reflection, inverse_reflection) ⇒ Boolean
private
Scopes on the potential inverse reflection prevent automatic
inverse_of
, since the scope could exclude the owner record we would inverse from. -
#valid_inverse_reflection?(reflection) ⇒ Boolean
private
Checks if the inverse reflection that is returned from the #automatic_inverse_of method is a valid reflection.
MacroReflection
- Inherited
#== | Returns |
#compute_class, | |
#klass | Returns the class for the macro. |
#scope_for, #derive_class_name, #normalize_options, #_klass |
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 |
#strict_loading_violation_message, #table_name, | |
#actual_source_reflection | FIXME: this is a horrible name. |
#ensure_option_not_given_as_class!, #primary_key |
Constructor Details
.new(name, scope, options, active_record) ⇒ AssociationReflection
# File 'activerecord/lib/active_record/reflection.rb', line 517
def initialize(name, scope, , active_record) super @type = -( [:foreign_type]&.to_s || "#{ [:as]}_type") if [:as] @foreign_type = -( [:foreign_type]&.to_s || "#{name}_type") if [:polymorphic] @join_table = nil @foreign_key = nil @association_foreign_key = nil @association_primary_key = nil if [:query_constraints] raise ConfigurationError, <<~MSG.squish Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is not allowed. To get the same behavior, use the `foreign_key` option instead. MSG end # If the foreign key is an array, set query constraints options and don't use the foreign key if [:foreign_key].is_a?(Array) [:query_constraints] = .delete(:foreign_key) end ensure_option_not_given_as_class!(:class_name) end
Instance Attribute Details
#belongs_to? ⇒ Boolean
(readonly)
Returns true
if self
is a belongs_to
reflection.
# File 'activerecord/lib/active_record/reflection.rb', line 714
def belongs_to?; false; end
#collection? ⇒ Boolean
(readonly)
Returns whether or not this association reflection is for a collection association. Returns true
if the #macro is either has_many
or has_and_belongs_to_many
, false
otherwise.
# File 'activerecord/lib/active_record/reflection.rb', line 696
def collection? false end
#foreign_type (readonly)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 514
attr_reader :type, :foreign_type
#has_inverse? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 674
def has_inverse? inverse_name end
#has_one? ⇒ Boolean
(readonly)
Returns true
if self
is a has_one
reflection.
# File 'activerecord/lib/active_record/reflection.rb', line 717
def has_one?; false; end
#has_scope? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 670
def has_scope? scope end
#nested? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 666
def nested? false end
#parent_reflection (rw)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 515
attr_accessor :parent_reflection # Reflection
#polymorphic? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 721
def polymorphic? [:polymorphic] end
#type (readonly)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 514
attr_reader :type, :foreign_type
#validate? ⇒ Boolean
(readonly)
Returns whether or not the association should be validated as part of the parent’s validation.
Unless you explicitly disable validation with validate: false
, validation will take place when:
-
you explicitly enable validation;
validate: true
-
you use autosave;
autosave: true
-
the association is a
has_many
association
# File 'activerecord/lib/active_record/reflection.rb', line 709
def validate? ! [:validate].nil? ? [:validate] : ( [:autosave] == true || collection?) end
Instance Method Details
#active_record_primary_key
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 587
def active_record_primary_key custom_primary_key = [:primary_key] @active_record_primary_key ||= if custom_primary_key if custom_primary_key.is_a?(Array) custom_primary_key.map { |pk| pk.to_s.freeze }.freeze else custom_primary_key.to_s.freeze end elsif active_record.has_query_constraints? || [:query_constraints] active_record.query_constraints_list elsif active_record.composite_primary_key? # If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id primary_key = primary_key(active_record) primary_key.include?("id") ? "id" : primary_key.freeze else primary_key(active_record).freeze end end
#add_as_polymorphic_through(reflection, seed)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 733
def add_as_polymorphic_through(reflection, seed) seed + [PolymorphicReflection.new(self, reflection)] end
#add_as_source(seed)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 729
def add_as_source(seed) seed end
#add_as_through(seed)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 737
def add_as_through(seed) seed + [self] end
#association_class
# File 'activerecord/lib/active_record/reflection.rb', line 719
def association_class; raise NotImplementedError; end
#association_foreign_key
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 579
def association_foreign_key @association_foreign_key ||= -( [:association_foreign_key]&.to_s || class_name.foreign_key) end
#association_primary_key(klass = nil)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 583
def association_primary_key(klass = nil) primary_key(klass || self.klass) end
#association_scope_cache(klass, owner, &block)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 540
def association_scope_cache(klass, owner, &block) key = self if polymorphic? key = [key, owner._read_attribute(@foreign_type)] end klass.with_connection do |connection| klass.cached_find_by_statement(connection, key, &block) end end
#automatic_inverse_of (private)
returns either nil
or the inverse association name that it finds.
# File 'activerecord/lib/active_record/reflection.rb', line 758
def automatic_inverse_of if can_find_inverse_of_automatically?(self) inverse_name = ActiveSupport::Inflector.underscore( [:as] || active_record.name.demodulize).to_sym begin reflection = klass._reflect_on_association(inverse_name) if !reflection && active_record.automatically_invert_plural_associations plural_inverse_name = ActiveSupport::Inflector.pluralize(inverse_name) reflection = klass._reflect_on_association(plural_inverse_name) end rescue NameError => error raise unless error.name.to_s == class_name # Give up: we couldn't compute the klass type so we won't be able # to find any associations either. reflection = false end if valid_inverse_reflection?(reflection) reflection.name end end end
#can_find_inverse_of_automatically?(reflection, inverse_reflection = false) ⇒ Boolean
(private)
Checks to see if the reflection doesn’t have any options that prevent us from being able to guess the inverse automatically. First, the inverse_of
option cannot be set to false. Second, we must have has_many
, has_one
, belongs_to
associations. Third, we must not have options such as :foreign_key
which prevent us from correctly guessing the inverse association.
# File 'activerecord/lib/active_record/reflection.rb', line 800
def can_find_inverse_of_automatically?(reflection, inverse_reflection = false) reflection. [:inverse_of] != false && !reflection. [:through] && !reflection. [:foreign_key] && scope_allows_automatic_inverse_of?(reflection, inverse_reflection) end
#check_eager_loadable!
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 630
def check_eager_loadable! return unless scope unless scope.arity == 0 raise ArgumentError, <<-MSG.squish The association scope '#{name}' is instance dependent (the scope block takes an argument). Eager loading instance dependent scopes is not supported. MSG end end
#check_validity!
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 618
def check_validity! check_validity_of_inverse! if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?) if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length raise CompositePrimaryKeyMismatchError.new(self) elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length raise CompositePrimaryKeyMismatchError.new(self) end end end
#clear_association_scope_cache
This is for clearing cache on the reflection. Useful for tests that need to compare SQL queries on associations.
# File 'activerecord/lib/active_record/reflection.rb', line 662
def clear_association_scope_cache # :nodoc: klass.initialize_find_by_cache end
#collect_join_chain
A chain of reflections from this one back to the owner. For more see the explanation in ThroughReflection
.
# File 'activerecord/lib/active_record/reflection.rb', line 656
def collect_join_chain [self] end
#compute_class(name)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 490
def compute_class(name) if polymorphic? raise ArgumentError, "Polymorphic associations do not support computing the class." end begin klass = active_record.send(:compute_type, name) rescue NameError => error if error.name.match?(/(?:\A|::)#{name}\z/) = "Missing model class #{name} for the #{active_record}##{self.name} association." += " You can specify a different model class with the :class_name option." unless [:class_name] raise NameError.new(, name) else raise end end unless klass < ActiveRecord::Base raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass." end klass end
#derive_class_name (private)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 821
def derive_class_name class_name = name.to_s class_name = class_name.singularize if collection? class_name.camelize end
#derive_fk_query_constraints(foreign_key) (private)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 839
def derive_fk_query_constraints(foreign_key) primary_query_constraints = active_record.query_constraints_list owner_pk = active_record.primary_key if primary_query_constraints.size > 2 raise ArgumentError, <<~MSG.squish The query constraints list on the `#{active_record}` model has more than 2 attributes. Active Record is unable to derive the query constraints for the association. You need to explicitly define the query constraints for this association. MSG end if !primary_query_constraints.include?(owner_pk) raise ArgumentError, <<~MSG.squish The query constraints on the `#{active_record}` model does not include the primary key so Active Record is unable to derive the foreign key constraints for the association. You need to explicitly define the query constraints for this association. MSG end return foreign_key if primary_query_constraints.include?(foreign_key) first_key, last_key = primary_query_constraints if first_key == owner_pk [foreign_key, last_key.to_s] elsif last_key == owner_pk [first_key.to_s, foreign_key] else raise ArgumentError, <<~MSG.squish Active Record couldn't correctly interpret the query constraints for the `#{active_record}` model. The query constraints on `#{active_record}` are `#{primary_query_constraints}` and the foreign key is `#{foreign_key}`. You need to explicitly set the query constraints for this association. MSG end end
#derive_foreign_key(infer_from_inverse_of: true) (private)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 827
def derive_foreign_key(infer_from_inverse_of: true) if belongs_to? "#{name}_id" elsif [:as] "#{ [:as]}_id" elsif [:inverse_of] && infer_from_inverse_of inverse_of.foreign_key(infer_from_inverse_of: false) else active_record.model_name.to_s.foreign_key end end
#derive_join_table (private)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 879
def derive_join_table ModelSchema.derive_join_table_name active_record.table_name, klass.table_name end
#extensions
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 741
def extensions Array( [:extend]) end
#foreign_key(infer_from_inverse_of: true)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 554
def foreign_key(infer_from_inverse_of: true) @foreign_key ||= if [:foreign_key] if [:foreign_key].is_a?(Array) [:foreign_key].map { |fk| -fk.to_s.freeze }.freeze else [:foreign_key].to_s.freeze end elsif [:query_constraints] [:query_constraints].map { |fk| -fk.to_s.freeze }.freeze else derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of) if active_record.has_query_constraints? derived_fk = derive_fk_query_constraints(derived_fk) end if derived_fk.is_a?(Array) derived_fk.map! { |fk| -fk.freeze } derived_fk.freeze else -derived_fk.freeze end end end
#inverse_name (private)
Attempts to find the inverse association name automatically. If it cannot find a suitable inverse association name, it returns nil
.
# File 'activerecord/lib/active_record/reflection.rb', line 749
def inverse_name unless defined?(@inverse_name) @inverse_name = .fetch(:inverse_of) { automatic_inverse_of } end @inverse_name end
#join_foreign_key
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 614
def join_foreign_key active_record_primary_key end
#join_id_for(owner)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 642
def join_id_for(owner) # :nodoc: Array(join_foreign_key).map { |key| owner._read_attribute(key) } end
#join_primary_key(klass = nil)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 606
def join_primary_key(klass = nil) foreign_key end
#join_primary_type
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 610
def join_primary_type type end
#join_table
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 550
def join_table @join_table ||= -( [:join_table]&.to_s || derive_join_table) end
#macro
Returns the macro type.
has_many :clients
returns :has_many
# File 'activerecord/lib/active_record/reflection.rb', line 691
def macro; raise NotImplementedError; end
#polymorphic_inverse_of(associated_class)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 678
def polymorphic_inverse_of(associated_class) if has_inverse? if inverse_relationship = associated_class._reflect_on_association( [:inverse_of]) inverse_relationship else raise InverseOfAssociationNotFoundError.new(self, associated_class) end end end
#polymorphic_name
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 725
def polymorphic_name active_record.polymorphic_name end
#scope_allows_automatic_inverse_of?(reflection, inverse_reflection) ⇒ Boolean
(private)
Scopes on the potential inverse reflection prevent automatic inverse_of
, since the scope could exclude the owner record we would inverse from. Scopes on the reflection itself allow for automatic inverse_of
as long as <tt>config.active_record.automatic_scope_inversing<tt> is set to true
(the default for new applications).
# File 'activerecord/lib/active_record/reflection.rb', line 813
def scope_allows_automatic_inverse_of?(reflection, inverse_reflection) if inverse_reflection !reflection.scope else !reflection.scope || reflection.klass.automatic_scope_inversing end end
#source_reflection
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 650
def source_reflection self end
#through_reflection
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 646
def through_reflection nil end
#valid_inverse_reflection?(reflection) ⇒ Boolean
(private)
Checks if the inverse reflection that is returned from the #automatic_inverse_of method is a valid reflection. We must make sure that the reflection’s active_record name matches up with the current reflection’s klass name.
# File 'activerecord/lib/active_record/reflection.rb', line 786
def valid_inverse_reflection?(reflection) reflection && reflection != self && foreign_key == reflection.foreign_key && klass <= reflection.active_record && can_find_inverse_of_automatically?(reflection, true) end