123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::Associations::Builder::BelongsTo

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

Constant Summary

Association - Inherited

VALID_OPTIONS

Class Attribute Summary

Association - Inherited

Class Method Summary

Class Method Details

.add_counter_cache_callbacks(model, reflection) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 28

def self.add_counter_cache_callbacks(model, reflection)
  cache_column = reflection.counter_cache_column

  name = reflection.name
  model.after_update lambda { |record|
    association = association(name)

    if association.saved_change_to_target?
      association.increment_counters
      association.decrement_counters_before_last_save
    end
  }

  klass = reflection.class_name.safe_constantize
  klass._counter_cache_columns = (klass._counter_cache_columns | [cache_column]).freeze if klass && klass.respond_to?(:_counter_cache_columns)
  model.counter_cached_association_names = (model.counter_cached_association_names | [reflection.name]).freeze
end

.add_default_callbacks(model, reflection) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 115

def self.add_default_callbacks(model, reflection)
  name = reflection.name
  default = reflection.options[:default]
  model.before_validation lambda { |o|
    o.association(name).default(&default)
  }
end

.add_destroy_callbacks(model, reflection) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 123

def self.add_destroy_callbacks(model, reflection)
  if reflection.deprecated?
    # If :dependent is set, destroying the record has some side effect that
    # would no longer happen if the association is removed.
    model.before_destroy do
      report_deprecated_association(reflection, context: ":dependent has a side effect here")
    end
  end

  model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
end

.add_touch_callbacks(model, reflection) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 92

def self.add_touch_callbacks(model, reflection)
  name  = reflection.name
  touch = reflection.options[:touch]

  callback = lambda { |change_method| lambda { |record|
    BelongsTo.touch_record(record, change_method, name, touch)
  }}

  if reflection.counter_cache_column
    touch_callback = callback.(:saved_change_to_attribute)
    update_callback = lambda { |record|
      instance_exec(record, &touch_callback) unless association(reflection.name).saved_change_to_target?
    }
    model.after_update update_callback, if: :saved_changes?
  else
    model.after_create callback.(:saved_change_to_attribute), if: :saved_changes?
    model.after_update callback.(:saved_change_to_attribute), if: :saved_changes?
    model.after_destroy callback.(:attribute_change_to_be_saved)
  end

  model.after_touch callback.(:attribute_change_to_be_saved)
end

.define_callbacks(model, reflection) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 21

def self.define_callbacks(model, reflection)
  super
  add_counter_cache_callbacks(model, reflection) if reflection.options[:counter_cache]
  add_touch_callbacks(model, reflection)         if reflection.options[:touch]
  add_default_callbacks(model, reflection)       if reflection.options[:default]
end

.define_change_tracking_methods(model, reflection, mixin) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 170

def self.define_change_tracking_methods(model, reflection, mixin)
  name = reflection.name

  mixin.define_cached_method(:"#{name}_changed?", namespace: :association_changed) do |batch|
    batch << <<~RUBY
      def #{name}_changed?
        association = association(:#{name})
        deprecated_associations_api_guard(association, :#{name}_changed?)
        association.target_changed?
      end
    RUBY
  end

  mixin.define_cached_method(:"#{name}_previously_changed?", namespace: :association_previously_changed) do |batch|
    batch << <<~RUBY
      def #{name}_previously_changed?
        association = association(:#{name})
        deprecated_associations_api_guard(association, :#{name}_previously_changed?)
        association.target_previously_changed?
      end
    RUBY
  end
end

.define_validations(model, reflection) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 135

def self.define_validations(model, reflection)
  if reflection.options.key?(:required)
    reflection.options[:optional] = !reflection.options.delete(:required)
  end

  if reflection.options[:optional].nil?
    required = model.belongs_to_required_by_default
  else
    required = !reflection.options[:optional]
  end

  super

  if required
    if ActiveRecord.belongs_to_required_validates_foreign_key
      model.validates_presence_of reflection.name, message: :required
    else
      name = reflection.name
      polymorphic = reflection.polymorphic?
      condition = lambda { |record|
        association = record.association(name)

        fk_missing_or_changed = association.foreign_key.any? do |fk|
          record._read_attribute(fk).nil? || record.attribute_changed?(fk)
        end

        fk_missing_or_changed ||
          (polymorphic && (record._read_attribute(association.foreign_type).nil? || record.attribute_changed?(association.foreign_type)))
      }

      model.validates_presence_of reflection.name, message: :required, if: condition
    end
  end
end

.macro (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 5

def self.macro
  :belongs_to
end

.touch_record(o, change_method, name, touch)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 46

def self.touch_record(o, change_method, name, touch) # :nodoc:
  association = o.association(name)
  foreign_key = association.foreign_key

  if foreign_key.any? { |key| o.public_send(change_method, key) }
    read_old = lambda do |column|
      change = o.public_send(change_method, column)
      change ? change.first : o._read_attribute(column)
    end
    old_reference = foreign_key.map_value { |key| read_old.call(key) }

    if old_reference
      reflection = association.reflection
      if reflection.polymorphic?
        type = read_old.call(association.foreign_type) || o.public_send(association.foreign_type)
        klass = o.class.polymorphic_class_for(type)
      else
        klass = association.klass
      end

      old_route = reflection.association_route(origin_class: o.class, destination_class: klass)
      old_origin_id = old_route._origin_key.map_value { |key| read_old.call(key) }
      unless foreign_key.composite? && old_origin_id.any?(nil)
        old_record = klass.find_by(old_route.destination_key.name => [old_origin_id])
      end

      if old_record
        if touch != true
          old_record.touch_later(touch)
        else
          old_record.touch_later
        end
      end
    end
  end

  record = o.public_send name
  if record && record.persisted?
    if touch != true
      record.touch_later(touch)
    else
      record.touch_later
    end
  end
end

.valid_dependent_options (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/builder/belongs_to.rb', line 17

def self.valid_dependent_options
  [:destroy, :delete, :destroy_async]
end

.valid_options(options) (private)

[ GitHub ]

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

def self.valid_options(options)
  valid = super + [:polymorphic, :counter_cache, :optional, :default]
  valid << :class_name unless options[:polymorphic]
  valid << :foreign_type if options[:polymorphic]
  valid << :ensuring_owner_was if options[:dependent] == :destroy_async
  valid
end