class ViewModel::ActiveRecord::AssociationData

TODO consider rephrase scope for consistency

def accepts?(viewmodel_class)

def accepts?(viewmodel_class)
  viewmodel_classes.include?(viewmodel_class)
end

def collection?

def collection?
  through? || direct_reflection.collection?
end

def direct_viewmodel

def direct_viewmodel
  @direct_viewmodel ||= begin
    raise 'not a through association' unless through?
    # Join table viewmodel class
    # For A has_many B through T; where this association is defined on A
    # Copy into scope for new class block
    direct_reflection   = self.direct_reflection    # A -> T
    indirect_reflection = self.indirect_reflection  # T -> B
    through_order_attr  = @through_order_attr
    viewmodel_classes   = self.viewmodel_classes
    Class.new(ViewModel::ActiveRecord) do
      self.synthetic = true
      self.model_class = direct_reflection.klass
      self.view_name = direct_reflection.klass.name
      association indirect_reflection.name, shared: true, optional: false, viewmodels: viewmodel_classes
      acts_as_list through_order_attr if through_order_attr
    end
  end
end

def indirect_association_data

def indirect_association_data
  direct_viewmodel._association_data(indirect_reflection.name)
end

def indirect_reflection

def indirect_reflection
  @indirect_reflection ||=
    direct_reflection.klass.reflect_on_association(ActiveSupport::Inflector.singularize(@through_to))
end

def initialize(association_name, direct_reflection, viewmodel_classes, shared, optional, through_to, through_order_attr)

def initialize(association_name, direct_reflection, viewmodel_classes, shared, optional, through_to, through_order_attr)
  @association_name   = association_name
  @direct_reflection  = direct_reflection
  @shared             = shared
  @optional           = optional
  @through_to         = through_to
  @through_order_attr = through_order_attr
  if viewmodel_classes
    @viewmodel_classes = Array.wrap(viewmodel_classes).map! do |v|
      case v
      when String, Symbol
        ViewModel::Registry.for_view_name(v.to_s)
      when Class
        v
      else
        raise ArgumentError.new("Invalid viewmodel class: #{v.inspect}")
      end
    end
  end
  if through?
    # Through associations must always be an owned direct association to a
    # shared indirect target. We expect the user to set shared: true to
    # express the ownership of the indirect target, but this direct
    # association to the intermediate is in fact owned. This ownership
    # property isn't directly used anywhere: the synthetic intermediate
    # viewmodel is only used in the deserialization update operations, which
    # directly understands the semantics of through associations.
    raise ArgumentError.new("Through associations must be to a shared target") unless @shared
    raise ArgumentError.new("Through associations must be `has_many`") unless direct_reflection.macro == :has_many
  end
end

def model_to_viewmodel

def model_to_viewmodel
_to_viewmodel ||= viewmodel_classes.each_with_object({}) do |vm, h|
.model_class] = vm

def name_to_viewmodel

def name_to_viewmodel
to_viewmodel ||= viewmodel_classes.each_with_object({}) do |vm, h|
.view_name] = vm
iew_aliases.each do |view_alias|
view_alias] = vm

def optional?

def optional?
  @optional
end

def pointer_location # TODO name

TODO name
def pointer_location # TODO name
  case direct_reflection.macro
  when :belongs_to
    :local
  when :has_one, :has_many
    :remote
  end
end

def polymorphic?

def polymorphic?
  target_reflection.polymorphic?
end

def shared?

def shared?
  @shared
end

def target_reflection

reflection for the target of this association: indirect if through, direct otherwise
def target_reflection
  if through?
    indirect_reflection
  else
    direct_reflection
  end
end

def through?

def through?
  @through_to.present?
end

def viewmodel_class

def viewmodel_class
  unless viewmodel_classes.size == 1
    raise ArgumentError.new("More than one possible class for association '#{target_reflection.name}'")
  end
  viewmodel_classes.first
end

def viewmodel_class_for_model(model_class)

def viewmodel_class_for_model(model_class)
  model_to_viewmodel[model_class]
end

def viewmodel_class_for_model!(model_class)

def viewmodel_class_for_model!(model_class)
  vm_class = viewmodel_class_for_model(model_class)
  if vm_class.nil?
    raise ArgumentError.new(
            "Invalid viewmodel model for association '#{target_reflection.name}': '#{model_class.name}'")
  end
  vm_class
end

def viewmodel_class_for_name(name)

def viewmodel_class_for_name(name)
  name_to_viewmodel[name]
end

def viewmodel_class_for_name!(name)

def viewmodel_class_for_name!(name)
  vm_class = viewmodel_class_for_name(name)
  if vm_class.nil?
    raise ArgumentError.new(
            "Invalid viewmodel name for association '#{target_reflection.name}': '#{name}'")
  end
  vm_class
end

def viewmodel_classes

def viewmodel_classes
  # If we weren't given explicit viewmodel classes, try to work out from the
  # names. This should work unless the association is polymorphic.
  @viewmodel_classes ||=
    begin
      model_class = target_reflection.klass
      if model_class.nil?
        raise "Couldn't derive target class for association '#{target_reflection.name}"
      end
      inferred_view_name = ViewModel::Registry.default_view_name(model_class.name)
      viewmodel_class = ViewModel::Registry.for_view_name(inferred_view_name) # TODO: improve error message to show it's looking for default name
      [viewmodel_class]
    end
end