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
trueifselfis abelongs_toreflection. -
#collection? ⇒ Boolean
readonly
Returns whether or not this association reflection is for a collection association.
- #deprecated? ⇒ Boolean readonly
- #foreign_type readonly
- #has_inverse? ⇒ Boolean readonly
-
#has_one? ⇒ Boolean
readonly
Returns
trueifselfis ahas_onereflection. - #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
nilor 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
-
#derive_primary_key(model)
private
Shared by #active_record_primary_key and #association_primary_key to resolve the key from
modelonce a customprimary_keyis ruled out. -
#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 521
def initialize(name, scope, , active_record) super @validated = false @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 @deprecated = !![:deprecated] 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 707
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 689
def collection? false end
#deprecated? ⇒ Boolean (readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 738
def deprecated? @deprecated end
#foreign_type (readonly)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 518
attr_reader :type, :foreign_type
#has_inverse? ⇒ Boolean (readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 667
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 710
def has_one?; false; end
#has_scope? ⇒ Boolean (readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 663
def has_scope? scope end
#nested? ⇒ Boolean (readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 659
def nested? false end
#parent_reflection (rw)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 519
attr_accessor :parent_reflection # Reflection
#polymorphic? ⇒ Boolean (readonly)
[ GitHub ]
# File 'activerecord/lib/active_record/reflection.rb', line 714
def polymorphic? [:polymorphic] end
#type (readonly)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 518
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_manyassociation
# File 'activerecord/lib/active_record/reflection.rb', line 702
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 586
def active_record_primary_key @active_record_primary_key ||= if [:primary_key] ActiveRecord::Key.for([:primary_key]).name else derive_primary_key(active_record) { |model| model.query_constraints_list } end end
#add_as_polymorphic_through(reflection, seed)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 726
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 722
def add_as_source(seed) seed end
#add_as_through(seed)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 730
def add_as_through(seed) seed + [self] end
#association_class
# File 'activerecord/lib/active_record/reflection.rb', line 712
def association_class; raise NotImplementedError; end
#association_foreign_key
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 578
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 582
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 548
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 755
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 797
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 623
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 607
def check_validity! return if @validated 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 @validated = true 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 655
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 649
def collect_join_chain [self] end
#compute_class(name)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 494
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 832
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 850
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 838
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 890
def derive_join_table ModelSchema.derive_join_table_name active_record.table_name, klass.table_name end
#derive_primary_key(model) (private)
Shared by #active_record_primary_key and #association_primary_key to
resolve the key from model once a custom primary_key is ruled out.
The block is yielded model to supply its query-constraints list.
# File 'activerecord/lib/active_record/reflection.rb', line 821
def derive_primary_key(model) if model.has_query_constraints? || [:query_constraints] yield model else # inferred_id is nil unless the key is composite; otherwise fall back # to primary_key, which respects a custom getter (it may return an # unfrozen string) and raises UnknownPrimaryKey when there is no key. model.primary_key_definition.inferred_id || primary_key(model).freeze end end
#extensions
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 734
def extensions Array([:extend]) end
#foreign_key(infer_from_inverse_of: true)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 562
def foreign_key(infer_from_inverse_of: true) @foreign_key ||= if [:foreign_key] ActiveRecord::Key.for([:foreign_key]).name 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 !derived_fk.is_a?(Array) && active_record.has_query_constraints? derived_fk = derive_fk_query_constraints(derived_fk) end ActiveRecord::Key.for(derived_fk).name 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 746
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 603
def join_foreign_key active_record_primary_key end
#join_id_for(owner)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 635
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 595
def join_primary_key(klass = nil) foreign_key end
#join_primary_type
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 599
def join_primary_type type end
#join_table
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 558
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 684
def macro; raise NotImplementedError; end
#polymorphic_inverse_of(associated_class)
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 671
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 718
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
config.active_record.automatic_scope_inversing is set to
{true} (the default for new applications).
# File 'activerecord/lib/active_record/reflection.rb', line 810
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 643
def source_reflection self end
#through_reflection
[ GitHub ]# File 'activerecord/lib/active_record/reflection.rb', line 639
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 783
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