class Restforce::DB::Associations::Base

Restforce::DB::Associations::Base defines an association between two mappings in the Registry.

Attributes

cache[R]
lookup[R]
name[R]

Public Class Methods

new(name, through: nil, build: true) click to toggle source

Public: Initialize a new Restforce::DB::Associations::Base.

name - The name of the ActiveRecord association to construct. through - The name of the lookup field on the Salesforce record. build - A Boolean indicating whether or not the association chain

should be built out for new records.
# File lib/restforce/db/associations/base.rb, line 19
def initialize(name, through: nil, build: true)
  @name = name.to_sym
  @lookup = through.is_a?(Array) ? through.map(&:to_s) : through.to_s
  @build = build
end

Public Instance Methods

build(_database_record, _salesforce_record, _cache) click to toggle source

Public: Build a record or series of records for the association defined by this class. Must be overridden in subclasses.

Raises a NotImplementedError.

# File lib/restforce/db/associations/base.rb, line 29
def build(_database_record, _salesforce_record, _cache)
  raise NotImplementedError
end
fields() click to toggle source

Public: Get a list of fields which should be included in the Salesforce record's lookups for any mapping including this association.

Returns a list of Salesforce fields this record should return.

# File lib/restforce/db/associations/base.rb, line 38
def fields
  [*lookup]
end
synced_for?(instance) click to toggle source

Public: Has a record for this association already been synchronized for the supplied instance?

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

Returns a Boolean.

# File lib/restforce/db/associations/base.rb, line 48
def synced_for?(instance)
  association_id = associated_salesforce_id(instance)
  return false unless association_id

  base_class = instance.mapping.database_model
  reflection = base_class.reflect_on_association(name)

  mappings_for(reflection).any? do |mapping|
    reflection.klass.exists?(mapping.lookup_column => association_id)
  end
end

Private Instance Methods

associated_salesforce_id(_instance) click to toggle source

Internal: Get the Salesforce ID belonging to the associated record for a supplied instance. Must be implemented per-association.

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

Returns a String.

# File lib/restforce/db/associations/base.rb, line 212
def associated_salesforce_id(_instance)
  raise NotImplementedError
end
association_for(reflection) click to toggle source

Internal: Get the first association which corresponds to an ActiveRecord reflection.

reflection - An ActiveRecord::AssociationReflection.

Returns a Restforce::DB::Associations::Base.

# File lib/restforce/db/associations/base.rb, line 175
def association_for(reflection)
  inverse = inverse_association_name(reflection)
  Registry[reflection.klass].detect do |mapping|
    association = mapping.associations.detect { |a| a.name == inverse }
    break association if association
  end
end
build?() click to toggle source

Internal: Should records for this association be synchronized between the two systems when a new record is added for the base mapping?

Returns a Boolean.

# File lib/restforce/db/associations/base.rb, line 66
def build?
  @build
end
constructed_records(database_record, lookups, attributes) { |associated| ... } click to toggle source

Internal: Get a list of all newly-constructed records based on this association, for a set of lookups.

database_record - An instance of an ActiveRecord::Base subclass. lookups - A Hash mapping database columns to Salesforce IDs. attributes - A Hash of attributes to assign to the new record.

Yields the new database record if one is built. Returns an Array containing all newly-constructed records.

# File lib/restforce/db/associations/base.rb, line 98
def constructed_records(database_record, lookups, attributes)
  associated = cache.find(target_class(database_record), lookups)

  # If the association record already exists, we don't need to build out
  # associations any further.
  if associated
    database_record.association(name).send(construction_method, associated)
    return []
  end

  associated = database_record.association(name).build(lookups)
  cache << associated

  associated.assign_attributes(attributes)

  nested = yield associated if block_given?

  [associated, *nested]
end
construction_method() click to toggle source

Internal: Get the method by which an associated record should be assigned to this record. Defaults to :writer, which overwrites the existing association, if one exists.

Returns a Symbol.

# File lib/restforce/db/associations/base.rb, line 123
def construction_method
  :writer
end
inverse_association_name(reflection) click to toggle source

Internal: Get the name of the inverse association which corresponds to this one.

reflection - An ActiveRecord::AssociationReflection.

Returns a Symbol.

# File lib/restforce/db/associations/base.rb, line 152
def inverse_association_name(reflection)
  reflection.send(:inverse_name)
end
lookup_field(mapping, reflection) click to toggle source

Internal: Get the appropriate Salesforce Lookup ID field for the passed mapping.

mapping - A Restforce::DB::Mapping. reflection - An ActiveRecord::AssociationReflection.

Returns a String or nil.

# File lib/restforce/db/associations/base.rb, line 77
def lookup_field(mapping, reflection)
  inverse = inverse_association_name(reflection)
  association = mapping.associations.detect { |a| a.name == inverse }
  return unless association

  if lookup.is_a?(Array)
    association.lookup
  else
    lookup
  end
end
mappings_for(reflection) click to toggle source

Internal: Get the first mapping which corresponds to an ActiveRecord reflection.

reflection - An ActiveRecord::AssociationReflection.

Returns a Restforce::DB::Mapping.

# File lib/restforce/db/associations/base.rb, line 162
def mappings_for(reflection)
  inverse = inverse_association_name(reflection)
  Registry[reflection.klass].select do |mapping|
    mapping.associations.any? { |a| a.name == inverse }
  end
end
nested_records(parent_record, database_record, salesforce_instance) click to toggle source

Internal: Construct all associated records for the passed database record, based on the mapping represented by the passed Salesforce instance.

parent_record - The parent of the associated_record; an instance

of an ActiveRecord::Base subclass.

associated_record - The child of the parent_record; an instance of

an ActiveRecord::Base subclass.

salesforce_instance - A Restforce::DB::Instances::Salesforce which

corresponds to the associated_record.

Returns an Array of associated records.

# File lib/restforce/db/associations/base.rb, line 195
def nested_records(parent_record, database_record, salesforce_instance)
  # We need to identify _this_ association to prevent backtracking.
  inverse = inverse_association_name(target_reflection(parent_record))
  nested = salesforce_instance.mapping.associations.flat_map do |a|
    next if a.name == inverse
    a.build(database_record, salesforce_instance.record, cache)
  end

  nested.compact
end
target_class(database_record) click to toggle source

Internal: Get the class of the inverse ActiveRecord association.

database_record - An instance of an ActiveRecord::Base subclass.

Returns a Class.

# File lib/restforce/db/associations/base.rb, line 132
def target_class(database_record)
  target_reflection(database_record).klass
end
target_reflection(database_record) click to toggle source

Internal: Get an AssociationReflection for this association on the passed database record.

database_record - An instance of an ActiveRecord::Base subclass.

Returns an ActiveRecord::AssociationReflection.

# File lib/restforce/db/associations/base.rb, line 142
def target_reflection(database_record)
  database_record.class.reflect_on_association(name)
end