module ViewModel::ActiveRecord::AssociationManipulation

def delete_associated(association_name, associated_id, type: nil, deserialize_context: self.class.new_deserialize_context)

or `:delete_all`
garbage-collected if the assocation is specified with `dependent: :destroy`
the provided associated viewmodel. The associated model will be
Removes the association between the models represented by this viewmodel and
def delete_associated(association_name, associated_id, type: nil, deserialize_context: self.class.new_deserialize_context)
  if self.changes.changed?
    raise ArgumentError.new('Invalid call to delete_associated on viewmodel with pending changes')
  end
  association_data = self.class._association_data(association_name)
  direct_reflection = association_data.direct_reflection
  unless association_data.collection?
    raise ArgumentError.new("Cannot remove element from single association '#{association_name}'")
  end
  check_association_type!(association_data, type)
  target_ref = ViewModel::Reference.new(type || association_data.viewmodel_class, associated_id)
  model_class.transaction do
    ViewModel::Callbacks.wrap_deserialize(self, deserialize_context: deserialize_context) do |hook_control|
      association_changed!(association_name)
      deserialize_context.run_callback(ViewModel::Callbacks::Hook::BeforeValidate, self)
      association = self.model.association(direct_reflection.name)
      association_scope = association.scope
      if association_data.through?
        raise ArgumentError.new('Polymorphic through relationships not supported yet') if association_data.polymorphic?
        direct_viewmodel = association_data.direct_viewmodel
        association_scope = association_scope.where(association_data.indirect_reflection.foreign_key => associated_id)
      else
        # viewmodel type for current association: nil in case of empty polymorphic association
        direct_viewmodel = association.klass.try { |k| association_data.viewmodel_class_for_model!(k) }
        if association_data.pointer_location == :local
          # If we hold the pointer, we can immediately check if the type and id match.
          if target_ref != ViewModel::Reference.new(direct_viewmodel, model.read_attribute(direct_reflection.foreign_key))
            raise ViewModel::DeserializationError::AssociatedNotFound.new(association_name.to_s, target_ref, blame_reference)
          end
        else
          # otherwise add the target constraint to the association scope
          association_scope = association_scope.where(id: associated_id)
        end
      end
      models = association_scope.to_a
      if models.blank?
        raise ViewModel::DeserializationError::AssociatedNotFound.new(association_name.to_s, target_ref, blame_reference)
      elsif models.size > 1
        raise ViewModel::DeserializationError::Internal.new(
                "Internal error: encountered multiple records for #{target_ref} in association #{association_name}",
                blame_reference)
      end
      child_context = self.context_for_child(association_name, context: deserialize_context)
      child_vm = direct_viewmodel.new(models.first)
      ViewModel::Callbacks.wrap_deserialize(child_vm, deserialize_context: child_context) do |child_hook_control|
        changes = ViewModel::Changes.new(deleted: true)
        child_context.run_callback(ViewModel::Callbacks::Hook::OnChange, child_vm, changes: changes)
        child_hook_control.record_changes(changes)
        association.delete(child_vm.model)
      end
      self.children_changed!
      final_changes = self.clear_changes!
      unless final_changes.contained_to?(associations: [association_name.to_s])
        raise ViewModel::DeserializationError::InvalidParentEdit.new(final_changes, blame_reference)
      end
      deserialize_context.run_callback(ViewModel::Callbacks::Hook::OnChange, self, changes: final_changes)
      hook_control.record_changes(final_changes)
      child_vm
    end
  end
end