class Restforce::DB::Associator

Restforce::DB::Associator is responsible for determining when one or more associations have been updated to point to a new Salesforce/database record, and propagate the modification to the opposite system when this occurs.

Public Instance Methods

run(*_) click to toggle source

Public: Run the re-association process, pulling in records from Salesforce and the database to determine the most recently attached association, then propagating the change between systems.

Returns nothing.

# File lib/restforce/db/associator.rb, line 16
def run(*_)
  return if belongs_to_associations.empty?

  @runner.run(@mapping) do |run|
    run.salesforce_instances.each { |instance| verify_associations(instance) }
    run.database_instances.each { |instance| verify_associations(instance) }
  end
end

Private Instance Methods

belongs_to_association_ids(database_instance) click to toggle source

Internal: Get a Hash of associated lookup IDs for the passed database record.

database_instance - A Restforce::DB::Instances::ActiveRecord.

Returns a Hash.

# File lib/restforce/db/associator.rb, line 95
def belongs_to_association_ids(database_instance)
  belongs_to_associations.inject({}) do |ids, association|
    ids.merge(association.lookups(database_instance.record))
  end
end
belongs_to_associations() click to toggle source

Internal: Get a list of the BelongsTo associations defined for the target mapping. Ignores associations where the foreign key is Id, as a record's Id will never change.

Returns an Array of Restforce::DB::Association::BelongsTo objects.

# File lib/restforce/db/associator.rb, line 106
def belongs_to_associations
  @belongs_to_associations ||= @mapping.associations.select do |association|
    association.is_a?(Restforce::DB::Associations::BelongsTo) && association.lookup != "Id"
  end
end
sync_associations(database_instance, salesforce_instance) click to toggle source

Internal: Given a database record and corresponding Salesforce data, synchronize the record associations in whichever system has the least recent data.

database_instance - A Restforce::DB::Instances::ActiveRecord. salesforce_instance - A Restforce::DB::Instances::Salesforce.

Returns nothing.

# File lib/restforce/db/associator.rb, line 72
def sync_associations(database_instance, salesforce_instance)
  ids = belongs_to_association_ids(database_instance)
  return if ids.all? { |field, id| salesforce_instance.record[field] == id }

  if database_instance.last_update > salesforce_instance.last_update
    salesforce_instance.update!(ids)
  else
    database_record = database_instance.record
    belongs_to_associations.each do |association|
      association.build(database_record, salesforce_instance.record)
    end
    database_record.save!
  end
rescue ActiveRecord::ActiveRecordError, Faraday::Error::ClientError => e
  DB.logger.error(SynchronizationError.new(e, salesforce_instance))
end
synchronized_instances(instance) click to toggle source

Internal: Given an instance for one system, find its synchronized pair in the other system.

instance - A Restforce::DB::Instances::Base.

Returns an Array containing one Restforce::DB::Instances::ActiveRecord

and one Restforce::DB::Instances::Salesforce, in that order.
# File lib/restforce/db/associator.rb, line 55
def synchronized_instances(instance)
  case instance
  when Restforce::DB::Instances::Salesforce
    [@mapping.database_record_type.find(instance.id), instance]
  when Restforce::DB::Instances::ActiveRecord
    [instance, @mapping.salesforce_record_type.find(instance.id)]
  end
end
verify_associations(instance) click to toggle source

Internal: Ensure integrity between the lookup columns in Salesforce and the synchronized records in the database. Skips records which have only been updated by Restforce::DB itself.

instance - A Restforce::DB::Instances::Base.

Returns nothing.

# File lib/restforce/db/associator.rb, line 34
def verify_associations(instance)
  return unless instance.synced?
  return if instance.updated_internally?

  database_instance, salesforce_instance = synchronized_instances(instance)
  return unless database_instance && salesforce_instance

  sync_associations(database_instance, salesforce_instance)
rescue Faraday::Error::ClientError => e
  # This should only be tripped if `synchronized_instances` makes a
  # request to fetch the Salesforce instance.
  DB.logger.error(SynchronizationError.new(e, database_instance))
end