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 ]