class ViewModel::ActiveRecord::Cloner
named association from the cloned tree.
‘ignore_association!(name)` to prune the current model or the target of the
the new model, and additionally can call `ignore!` or
for each type they wish to affect. These callbacks may update attributes of
To customize, subclasses may define methods `visit_x_view(node, new_model)`
`association`s) will be copied from the original.
Attributes (including association foreign keys not covered by ViewModel
non-owned referenced associations will be copied directly as references.
ViewModel::ActiveRecord. Owned associations will be followed and cloned, while
Simple visitor for cloning models through the tree structure defined by
def clone(node)
def clone(node) reset_state! new_model = node.model.dup pre_visit(node, new_model) return nil if ignored? if node.class.name class_name = node.class.name.underscore.gsub('/', '__') visit = :"visit_#{class_name}" end_visit = :"end_visit_#{class_name}" end if visit && respond_to?(visit, true) self.send(visit, node, new_model) return nil if ignored? end # visit the underlying viewmodel for each association, ignoring any # customization ignored_associations = @ignored_associations node.class._members.each do |name, association_data| next unless association_data.is_a?(ViewModel::ActiveRecord::AssociationData) reflection = association_data.direct_reflection if ignored_associations.include?(name) new_associated = association_data.collection? ? [] : nil else # Load the record associated with the old model associated = node.model.public_send(reflection.name) if associated.nil? new_associated = nil elsif !association_data.owned? && !association_data.through? # simply attach the associated target to the new model new_associated = associated else # Otherwise descend into the child, and attach the result build_vm = ->(model) do vm_class = if association_data.through? # descend into the synthetic join table viewmodel association_data.direct_viewmodel else association_data.viewmodel_class_for_model!(model.class) end vm_class.new(model) end new_associated = if ViewModel::Utils.array_like?(associated) associated.map { |m| clone(build_vm.(m)) }.compact else clone(build_vm.(associated)) end end end new_association = new_model.association(reflection.name) new_association.writer(new_associated) end if end_visit && respond_to?(end_visit, true) self.send(end_visit, node, new_model) end post_visit(node, new_model) new_model end
def ignore!
def ignore! @ignored = true end
def ignore_association!(name)
def ignore_association!(name) @ignored_associations.add(name.to_s) end
def ignored?
def ignored? @ignored end
def post_visit(node, new_model); end
def post_visit(node, new_model); end
def pre_visit(node, new_model); end
def pre_visit(node, new_model); end
def reset_state!
def reset_state! @ignored = false @ignored_associations = Set.new end