module ViewModel::ActiveRecord::NestedControllerBase

def association_data

def association_data
  @association_data ||= owner_viewmodel._association_data(association_name)
end

def association_name

def association_name
  params.fetch(:association_name) { raise ArgumentError.new('No association name from routes') }
end

def destroy_association(collection, serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil)

def destroy_association(collection, serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil)
  require_external_referenced_association!
  if lock_owner
    owner_viewmodel.find(owner_viewmodel_id, eager_include: false, lock: lock_owner)
  end
  empty_update = collection ? [] : nil
  owner_viewmodel.deserialize_from_view(owner_update_hash(empty_update),
                                        deserialize_context: deserialize_context)
  render_viewmodel(empty_update, serialize_context: serialize_context)
end

def owner_update_hash(update)

def owner_update_hash(update)
  {
    ViewModel::ID_ATTRIBUTE   => owner_viewmodel_id,
    ViewModel::TYPE_ATTRIBUTE => owner_viewmodel.view_name,
    association_name.to_s     => update,
  }
end

def owner_viewmodel

def owner_viewmodel
  name = params.fetch(:owner_viewmodel) { raise ArgumentError.new("No owner viewmodel present") }
  owner_viewmodel_class_for_name(name.to_s.camelize)
end

def owner_viewmodel_class_for_name(name)

def owner_viewmodel_class_for_name(name)
  ViewModel::Registry.for_view_name(name)
end

def owner_viewmodel_id(required: true)

def owner_viewmodel_id(required: true)
  id_param_name = owner_viewmodel.view_name.underscore + '_id'
  default = required ? {} : { default: nil }
  parse_param(id_param_name, **default)
end

def require_external_referenced_association!

def require_external_referenced_association!
  unless association_data.referenced? && association_data.external?
    raise ArgumentError.new("Expected referenced external association: '#{association_name}'")
  end
end

def show_association(scope: nil, serialize_context: new_serialize_context, lock_owner: nil)

def show_association(scope: nil, serialize_context: new_serialize_context, lock_owner: nil)
  require_external_referenced_association!
  associated_views = nil
  pre_rendered = owner_viewmodel.transaction do
    owner_view = owner_viewmodel.find(owner_viewmodel_id, eager_include: false, lock: lock_owner)
    ViewModel::Callbacks.wrap_serialize(owner_view, context: serialize_context) do
      # Association manipulation methods construct child contexts internally
      associated_views = owner_view.load_associated(association_name, scope: scope, serialize_context: serialize_context)
      associated_views = yield(associated_views) if block_given?
      child_context = owner_view.context_for_child(association_name, context: serialize_context)
      prerender_viewmodel(associated_views, serialize_context: child_context)
    end
  end
  render_json_string(pre_rendered)
  associated_views
end

def write_association(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil)

safe to use update hashes directly.
single parent each child can only appear once. This means it's
There's no multi membership, so when viewing the children of a

viewmodels directly.
This method always takes direct update hashes, and returns
def write_association(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil)
  require_external_referenced_association!
  association_view = nil
  pre_rendered = owner_viewmodel.transaction do
    update_hash, refs = parse_viewmodel_updates
    update_hash =
      ViewModel::ActiveRecord.add_reference_indirection(
        update_hash,
        association_data: association_data,
        references:       refs,
        key:              'write-association',
      )
    owner_view = owner_viewmodel.find(owner_viewmodel_id, eager_include: false, lock: lock_owner)
    association_view = owner_view.replace_associated(association_name, update_hash,
                                                     references: refs,
                                                     deserialize_context: deserialize_context)
    ViewModel::Callbacks.wrap_serialize(owner_view, context: serialize_context) do
      child_context = owner_view.context_for_child(association_name, context: serialize_context)
      ViewModel.preload_for_serialization(association_view)
      association_view = yield(association_view) if block_given?
      prerender_viewmodel(association_view, serialize_context: child_context)
    end
  end
  render_json_string(pre_rendered)
  association_view
end

def write_association_bulk(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil)

this case.
hash. This means it's only safe to use update hashes directly in
appear once so each is guaranteed to have a unique update
If an association is referenced and owned, each child may only

the input structure.
reference hashes for shared associations. The return value matches
This method takes direct update hashes for owned associations, and
def write_association_bulk(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil)
  require_external_referenced_association!
  updated_by_parent_viewmodel = nil
  pre_rendered = owner_viewmodel.transaction do
    updates_by_parent_id, references = parse_bulk_update
    if association_data.owned?
      updates_by_parent_id.transform_values!.with_index do |update_hash, index|
        ViewModel::ActiveRecord.add_reference_indirection(
          update_hash,
          association_data: association_data,
          references:       references,
          key:              "write-association-bulk-#{index}",
        )
      end
    end
    updated_by_parent_viewmodel =
      owner_viewmodel.replace_associated_bulk(
        association_name,
        updates_by_parent_id,
        references:          references,
        deserialize_context: deserialize_context,
      )
    views = updated_by_parent_viewmodel.flat_map { |_parent_viewmodel, updated_views| Array.wrap(updated_views) }
    ViewModel.preload_for_serialization(views)
    updated_by_parent_viewmodel = yield(updated_by_parent_viewmodel) if block_given?
    return_updates = updated_by_parent_viewmodel.map do |owner_view, updated_views|
      ParentProxyModel.new(owner_view, association_data, updated_views)
    end
    return_structure = {
      ViewModel::TYPE_ATTRIBUTE         => ViewModel::BULK_UPDATE_TYPE,
      ViewModel::BULK_UPDATES_ATTRIBUTE => return_updates,
    }
    prerender_viewmodel(return_structure, serialize_context: serialize_context)
  end
  render_json_string(pre_rendered)
  updated_by_parent_viewmodel
end