Class: ActiveRecord::Associations::Association
Do not use. This class is for internal use only.
Overview
Class Method Summary
Instance Attribute Summary
Instance Method Summary
Constructor Details
.new(owner, reflection) ⇒ Association
[ GitHub ]
Instance Attribute Details
#collection? ⇒ Boolean
Whether the association represents a single record or a collection of records.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 237
def collection?
false
end
#disable_joins
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 37
attr_reader :reflection, :disable_joins
#find_target? ⇒ Boolean
[ GitHub ]
#foreign_key_present? ⇒ Boolean
Returns true if there is a foreign key present on the owner which references the target. This is used to determine whether we can load the target if the owner is currently a new record (and therefore without a key). If the owner is a new record then foreign_key must be present in order to load target.
Currently implemented by belongs_to (vanilla and polymorphic) and has_one/has_many :through associations which go through a belongs_to.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 332
def foreign_key_present?
false
end
#loaded? ⇒ Boolean
Has the target been already loaded?
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 81
def loaded?
@loaded
end
#options
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 39
delegate :options, to: :reflection
#owner
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 36
attr_accessor :owner
#reflection
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 37
attr_reader :reflection, :disable_joins
#stale_target? ⇒ Boolean
The target is stale if the target no longer points to the record(s) that the relevant foreign_key(s) refers to. If stale, the association accessor method on the owner will reload the target. It’s up to subclasses to implement the stale_state method if relevant.
Note that if the target has not been loaded, it is not considered stale.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 97
def stale_target?
loaded? && @stale_state != stale_state
end
#target
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 53
def target
if @target.is_a?(Promise)
@target = @target.value
end
@target
end
#target=(target)
Sets the target of this association to \target, and the loaded flag to true.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 102
def target=(target)
@target = target
loaded!
end
#violates_strict_loading? ⇒ Boolean
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 284
def violates_strict_loading?
return if @skip_strict_loading
return unless owner.validation_context.nil?
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
end
Instance Method Details
#association_scope
The scope for this association.
Note that the association_scope is merged into the target_scope only when the scope method is called. This is because at that point the call may be surrounded by scope.scoping { … } or unscoped { … } etc, which affects the scope which actually gets built.
[ GitHub ]
#build_record(attributes)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 383
def build_record(attributes)
reflection.build_association(attributes) do |record|
initialize_attributes(record, attributes)
yield(record) if block_given?
end
end
#create(attributes = nil, &block)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 227
def create(attributes = nil, &block)
_create_record(attributes, &block)
end
#create!(attributes = nil, &block)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 231
def create!(attributes = nil, &block)
_create_record(attributes, true, &block)
end
#enqueue_destroy_association(options)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 398
def enqueue_destroy_association(options)
job_class = owner.class.destroy_association_async_job
if job_class
owner._after_commit_jobs.push([job_class, options])
end
end
#ensure_klass_exists!
Reader and writer methods call this so that consistent errors are presented when the association target class does not exist.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 244
def ensure_klass_exists!
klass
end
#extensions
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 169
def extensions
extensions = klass.default_extensions | reflection.extensions
if reflection.scope
extensions |= reflection.scope_for(klass.unscoped, owner).extensions
end
extensions
end
#find_target(async: false)
[ GitHub ]
#foreign_key_for?(record) ⇒ Boolean
Returns true if record contains the foreign_key
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 370
def foreign_key_for?(record)
foreign_key = Array(reflection.foreign_key)
foreign_key.all? { |key| record._has_attribute?(key) }
end
#foreign_key_values(record)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 424
def foreign_key_values(record)
Array(reflection.foreign_key).map { |key| record.read_attribute(key) }
end
#initialize_attributes(record, except_from_scope_attributes = nil)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 217
def initialize_attributes(record, except_from_scope_attributes = nil) except_from_scope_attributes ||= {}
skip_assign = [reflection.foreign_key, reflection.type].compact
assigned_keys = record.changed_attribute_names_to_save
assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
attributes = scope_for_create.except!(*(assigned_keys - skip_assign))
record.send(:_assign_attributes, attributes) if attributes.any?
set_inverse_instance(record)
end
#inversable?(record) ⇒ Boolean
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 406
def inversable?(record)
record &&
((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
end
#inverse_association_for(record)
[ GitHub ]
#inverse_reflection_for(record)
Can be redefined by subclasses, notably polymorphic belongs_to The record parameter is necessary to support polymorphic inverses as we must check for the association in the specific class of the record.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 359
def inverse_reflection_for(record)
reflection.inverse_of
end
#inversed_from(record)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 153
def inversed_from(record)
self.target = record
end
#inversed_from_queries(record)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 157
def inversed_from_queries(record)
if inversable?(record)
self.target = record
end
end
#invertible_for?(record) ⇒ Boolean
Returns true if inverse association on the given record needs to be set. This method is redefined by subclasses.
[ GitHub ]
#klass
Returns the class of the target. belongs_to polymorphic overrides this to look at the polymorphic_type field on the owner.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 165
def klass
reflection.klass
end
#load_target
Loads the target if needed and returns it.
This method is abstract in the sense that it relies on #find_target, which is expected to be provided by descendants.
If the target is already loaded it is just returned. Thus, you can call load_target unconditionally to get the target.
::ActiveRecord::RecordNotFound is rescued within the method, and it is not reraised. The proxy is reset and nil is the return value.
[ GitHub ]
#loaded!
Asserts the target has been loaded setting the loaded flag to true.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 86
def loaded!
@loaded = true
@stale_state = stale_state
end
#marshal_dump
We can’t dump @reflection and @through_reflection since it contains the scope proc
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 206
def marshal_dump
ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
[@reflection.name, ivars]
end
#marshal_load(data)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 211
def marshal_load(data)
reflection_name, ivars = data
ivars.each { |name, val| instance_variable_set(name, val) }
@reflection = @owner.class._reflect_on_association(reflection_name)
end
#matches_foreign_key?(record) ⇒ Boolean
[ GitHub ]
#owner_foreign_key_matches_record?(record) ⇒ Boolean
[ GitHub ]
#primary_key_values(record)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 428
def primary_key_values(record)
Array(reflection.association_primary_key(record.class)).map { |key| record.read_attribute(key) }
end
#raise_on_type_mismatch!(record)
Raises ::ActiveRecord::AssociationTypeMismatch unless record is of the kind of the class of the associated objects. Meant to be used as a safety check when you are about to assign an associated record.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 339
def raise_on_type_mismatch!(record)
unless record.is_a?(reflection.klass)
fresh_class = reflection.class_name.safe_constantize
unless fresh_class && record.is_a?(fresh_class)
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, "\
"got #{record.inspect} which is an instance of #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
end
end
#record_foreign_key_matches_owner?(record) ⇒ Boolean
[ GitHub ]
#reload(force = false)
Reloads the target and returns self on success. The QueryCache is cleared if force is true.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 72
def reload(force = false)
klass.connection_pool.clear_query_cache if force && klass
reset
reset_scope
load_target
self unless target.nil?
end
#remove_inverse_instance(record)
Remove the inverse association, if possible
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 147
def remove_inverse_instance(record)
if inverse = inverse_association_for(record)
inverse.inversed_from(nil)
end
end
#reset
Resets the loaded flag to false and sets the target to nil.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 61
def reset
@loaded = false
@stale_state = nil
end
#reset_negative_cache
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 66
def reset_negative_cache reset if loaded? && target.nil?
end
#reset_scope
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 119
def reset_scope
@association_scope = nil
end
#scope_for_create
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 316
def scope_for_create
scope.scope_for_create
end
#set_inverse_instance(record)
Set the inverse association, if possible
[ GitHub ]
#set_inverse_instance_from_queries(record)
[ GitHub ]
#set_strict_loading(record)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 123
def set_strict_loading(record)
if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
record.strict_loading!
else
record.strict_loading!(false, mode: owner.strict_loading_mode)
end
end
#skip_statement_cache?(scope) ⇒ Boolean
Returns true if statement cache should be skipped on the association reader.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 391
def skip_statement_cache?(scope)
reflection.has_scope? ||
scope.eager_loading? ||
klass.scope_attributes? ||
reflection.source_reflection.active_record.default_scopes.any?
end
#skip_strict_loading(&block)
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 276
def skip_strict_loading(&block)
skip_strict_loading_was = @skip_strict_loading
@skip_strict_loading = true
yield
ensure
@skip_strict_loading = skip_strict_loading_was
end
#stale_state
This should be implemented to return the values of the relevant key(s) on the owner, so that when stale_state is different from the value stored on the last find_target, the target is stale.
This is only relevant to certain associations, which is why it returns nil by default.
[ GitHub ]
# File 'activerecord/lib/active_record/associations/association.rb', line 380
def stale_state
end
#target_scope
Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the through association’s scope)
[ GitHub ]