class Copyable::Declarations::Associations

Public Class Methods

execute(association_list, original_model, new_model, global_override = {}, skip_validations, skip_associations) click to toggle source

this is the algorithm for copying associated records according to the instructions given in the copyable declaration

# File lib/copyable/declarations/associations.rb, line 9
def execute(association_list, original_model, new_model, global_override = {}, skip_validations, skip_associations)
  @skip_validations = skip_validations
  @skip_associations = skip_associations
  @global_override = global_override
  association_list.each do |assoc_name, advice|
    association = original_model.class.reflections[assoc_name.to_s]
    check_advice(association, advice, original_model)
    unless advice == :do_not_copy || skip_associations.include?(assoc_name.to_sym)
      copy_association(association, original_model, new_model)
    end
  end
end

Private Class Methods

check_advice(association, advice, original_model) click to toggle source
# File lib/copyable/declarations/associations.rb, line 24
def check_advice(association, advice, original_model)
  if ![:copy, :do_not_copy, :copy_only_habtm_join_records].include?(advice)
    message = "Error in copyable:associations of "
    message << "#{original_model.class.name}: the association '#{association.name}' "
    message << "has unrecognized advice '#{advice}'."
    raise AssociationError.new(message)
  end
  if association.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection) && advice == :copy
    message = "Error in copyable:associations of "
    message << "#{original_model.class.name}: the association '#{association.name}' "
    message << "only supports the :copy_only_habtm_join_records advice, not the :copy advice, "
    message << "because it is a has_and_belongs_to_many association."
    raise AssociationError.new(message)
  end
  if !association.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection) && advice == :copy_only_habtm_join_records
    message = "Error in copyable:associations of "
    message << "#{original_model.class.name}: the association '#{association.name}' "
    message << "only supports the :copy advice, not the :copy_only_habtm_join_records advice, "
    message << "because it is not a has_and_belongs_to_many association."
    raise AssociationError.new(message)
  end
end
copy_association(association, original_model, new_model) click to toggle source
# File lib/copyable/declarations/associations.rb, line 47
def copy_association(association, original_model, new_model)
  if association.is_a?(ActiveRecord::Reflection::HasManyReflection)
    copy_has_many(association, original_model, new_model)
  elsif association.is_a?(ActiveRecord::Reflection::HasOneReflection)
    copy_has_one(association, original_model, new_model)
  elsif association.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection)
    copy_habtm(association, original_model, new_model)
  else
    raise "Unsupported association #{association.class}" # should never happen, since we filter out belongs_to
  end
end
copy_habtm(association, original_model, new_model) click to toggle source
# File lib/copyable/declarations/associations.rb, line 70
def copy_habtm(association, original_model, new_model)
  original_model.send(association.name).each do |child|
    new_model.send(association.name) << child
  end
end
copy_has_many(association, original_model, new_model) click to toggle source
# File lib/copyable/declarations/associations.rb, line 59
def copy_has_many(association, original_model, new_model)
  original_model.send(association.name).each do |child|
    copy_record(association, child, new_model)
  end
end
copy_has_one(association, original_model, new_model) click to toggle source
# File lib/copyable/declarations/associations.rb, line 65
def copy_has_one(association, original_model, new_model)
  child = original_model.send(association.name)
  copy_record(association, child, new_model) if child
end
copy_record(association, original_record, parent_model) click to toggle source
# File lib/copyable/declarations/associations.rb, line 76
def copy_record(association, original_record, parent_model)
  if SingleCopyEnforcer.can_copy?(original_record)
    if original_record.respond_to? :create_copy!
      copied_record = original_record.create_copy!(
        override: { association.foreign_key => parent_model.id },
        __called_recursively: true,
        global_override: @global_override,
        skip_validations: @skip_validations,
        skip_associations: @skip_associations)
    else
      message = "Could not copy #{parent_model.class.name}#id:#{parent_model.id} "
      message << "because #{original_record.class.name} does not have a copyable declaration."
      raise Copyable::CopyableError.new(message)
    end
  else
    copied_record = CopyRegistry.fetch_copy(record: original_record)
    copied_record.update_column(association.foreign_key, parent_model.id)
  end
  update_other_belongs_to_associations(association.foreign_key, copied_record)
end
update_other_belongs_to_associations(already_updated_key, copied_record) click to toggle source
# File lib/copyable/declarations/associations.rb, line 97
def update_other_belongs_to_associations(already_updated_key, copied_record)
  copied_record.class.reflect_on_all_associations(:belongs_to).each do |assoc|
    next if assoc.foreign_key == already_updated_key
    id = copied_record.send(assoc.foreign_key)
    if id
      if assoc.options.key? :polymorphic
        klass = copied_record.send("#{assoc.name}_type").constantize
      else
        klass = assoc.klass
      end
      copied_child = CopyRegistry.fetch_copy(class: klass, id: id)
      if copied_child
        copied_record.update_column(assoc.foreign_key, copied_child.id)
      end
    end
  end
end