123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::Associations::HasOneAssociation

Do not use. This class is for internal use only.
Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: ActiveRecord::Associations::SingularAssociation
Defined in: activerecord/lib/active_record/associations/has_one_association.rb

Overview

Active Record Has One Association

Class Method Summary

Association - Inherited

Instance Attribute Summary

ForeignAssociation - Included

Association - Inherited

#collection?

Whether the association represent a single record or a collection of records.

#disable_joins,
#loaded?

Has the target been already loaded?

#options, #owner, #reflection,
#stale_target?

The target is stale if the target no longer points to the record(s) that the relevant foreign_key(s) refers to.

#target,
#target=

Sets the target of this association to \target, and the loaded flag to true.

#find_target?,
#foreign_key_present?

Returns true if there is a foreign key present on the owner which references the target.

#violates_strict_loading?

Instance Method Summary

ForeignAssociation - Included

#nullified_owner_attributes,
#set_owner_attributes

Sets the owner attributes on the given record.

SingularAssociation - Inherited

#build,
#force_reload_reader

Implements the reload reader method, e.g.

#reader

Implements the reader method, e.g.

#reset

Resets the loaded flag to false and sets the target to nil.

#writer

Implements the writer method, e.g.

#_create_record, #find_target, #replace, #scope_for_create, #set_new_record

Association - Inherited

#async_load_target, #create, #create!, #extensions, #initialize_attributes, #inversed_from, #inversed_from_queries,
#klass

Returns the class of the target.

#load_target

Loads the target if needed and returns it.

#loaded!

Asserts the target has been loaded setting the loaded flag to true.

#marshal_dump

We can’t dump @reflection and @through_reflection since it contains the scope proc.

#marshal_load,
#reload

Reloads the target and returns self on success.

#remove_inverse_instance

Remove the inverse association, if possible.

#reset

Resets the loaded flag to false and sets the target to nil.

#reset_negative_cache, #reset_scope, #scope,
#set_inverse_instance

Set the inverse association, if possible.

#set_inverse_instance_from_queries, #set_strict_loading,
#association_scope

The scope for this association.

#build_record, #enqueue_destroy_association,
#ensure_klass_exists!

Reader and writer methods call this so that consistent errors are presented when the association target class does not exist.

#find_target,
#foreign_key_for?

Returns true if record contains the foreign_key.

#inversable?, #inverse_association_for,
#inverse_reflection_for

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.

#invertible_for?

Returns true if inverse association on the given record needs to be set.

#matches_foreign_key?,
#raise_on_type_mismatch!

Raises ::ActiveRecord::AssociationTypeMismatch unless record is of the kind of the class of the associated objects.

#scope_for_create,
#skip_statement_cache?

Returns true if statement cache should be skipped on the association reader.

#skip_strict_loading,
#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.

#target_scope

Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e.

Constructor Details

This class inherits a constructor from ActiveRecord::Associations::Association

Instance Method Details

#_create_record(attributes, raise_error = false, &block) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 133

def _create_record(attributes, raise_error = false, &block)
  unless owner.persisted?
    raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
  end

  super
end

#delete(method = options[:dependent])

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 26

def delete(method = options[:dependent])
  if load_target
    case method
    when :delete
      target.delete
    when :destroy
      target.destroyed_by_association = reflection
      target.destroy
      throw(:abort) unless target.destroyed?
    when :destroy_async
      if target.class.query_constraints_list
        primary_key_column = target.class.query_constraints_list
        id = primary_key_column.map { |col| target.public_send(col) }
      else
        primary_key_column = target.class.primary_key
        id = target.public_send(primary_key_column)
      end

      enqueue_destroy_association(
        owner_model_name: owner.class.to_s,
        owner_id: owner.id,
        association_class: reflection.klass.to_s,
        association_ids: [id],
        association_primary_key_column: primary_key_column,
        ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
      )
    when :nullify
      target.update_columns(nullified_owner_attributes) if target.persisted?
    end
  end
end

#handle_dependency

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 9

def handle_dependency
  case options[:dependent]
  when :restrict_with_exception
    raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target

  when :restrict_with_error
    if load_target
      record = owner.class.human_attribute_name(reflection.name).downcase
      owner.errors.add(:base, :'restrict_dependent_destroy.has_one', record: record)
      throw(:abort)
    end

  else
    delete
  end
end

#nullify_owner_attributes(record) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 119

def nullify_owner_attributes(record)
  Array(reflection.foreign_key).each do |foreign_key_column|
    record[foreign_key_column] = nil unless foreign_key_column.in?(Array(record.class.primary_key))
  end
end

#remove_target!(method) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 95

def remove_target!(method)
  case method
  when :delete
    target.delete
  when :destroy
    target.destroyed_by_association = reflection
    if target.persisted?
      target.destroy
    end
  else
    nullify_owner_attributes(target)
    remove_inverse_instance(target)

    if target.persisted? && owner.persisted? && !target.save
      set_owner_attributes(target)
      raise RecordNotSaved.new(
        "Failed to remove the existing associated #{reflection.name}. " \
        "The record failed to save after its foreign key was set to nil.",
        target
      )
    end
  end
end

#replace(record, save = true) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 59

def replace(record, save = true)
  raise_on_type_mismatch!(record) if record

  return target unless load_target || record

  assigning_another_record = target != record
  if assigning_another_record || record.has_changes_to_save?
    save &&= owner.persisted?

    transaction_if(save) do
      remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record

      if record
        set_owner_attributes(record)
        set_inverse_instance(record)

        if save && !record.save
          nullify_owner_attributes(record)
          set_owner_attributes(target) if target
          raise RecordNotSaved.new("Failed to save the new associated #{reflection.name}.", record)
        end
      end
    end
  end

  self.target = record
end

#set_new_record(record) (private)

The reason that the save param for replace is false, if for create (not just build), is because the setting of the foreign keys is actually handled by the scoping when the record is instantiated, and so they are set straight away and do not need to be updated within replace.

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 91

def set_new_record(record)
  replace(record, false)
end

#transaction_if(value, &block) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/has_one_association.rb', line 125

def transaction_if(value, &block)
  if value
    reflection.klass.transaction(&block)
  else
    yield
  end
end