class Eco::Data::Locations::NodeDiff::NodesDiff # treeify helper... with diffs you cannot expect all nodes # to be there (as not all generated a difference) # This class helps to cluster nodes that are related # (then each cluster has as a top only one diff) class ClusteredTreeify class DataIntegrityError < StandardError end class << self def mode(value) case value when :destination, :curr :curr when :source, :prev :prev else raise ArgumentError, "Unknown mode ':#{value}'" end end end attr_reader :diffs, :mode # @param on [Symbol] if it treeifies based on :prev (`:source`) # or `:curr` (`:destination`) # - default to `:prev` because it's mostly use to archive def initialize(diffs, mode: :prev) @diffs = diffs @mode = self.class.mode(mode) end # @return [Hash] where _key_ is the parent `diff` and _value_ are all # descendants thereof def clusters @clusters = only_present_parents_hash.reject do |_pid, tree| all_children_diffs_hash.key?(tree.id) end.each_with_object({}) do |(_pid, tree), clus| clus[tree.diff] = tree.all_descendants.map(&:diff).uniq end end private def all_children_diffs_hash @all_children_diffs_hash ||= all_children_diffs.each_with_object({}) do |node, out| out[node.id] = node.diff end end def all_children_diffs @all_children_diffs ||= only_present_parents_hash.values.map(&:all_descendants).flatten.uniq end # wipes out tree nodes that got there just as a token (diff not actually present) def only_present_parents_hash @only_present_parents_hash ||= parents_hash.select do |_pid, tree| tree.diff? end end # builds up the hierarchy def parents_hash @parents_hash ||= diffs.each_with_object({}) do |diff, out| id = node_id(diff) pid = parent_id(diff) if id == pid %i[unarchive? id? id_name? insert? move? archive?].each do |stage| puts "Stage #{stage}: #{diff.send(stage)}" end msg = "Node '#{id}' seems to be parent of itself" raise DataIntegrityError, msg end out[pid] ||= DiffsTree.new(id: pid) out[id] ||= DiffsTree.new(id: id) out[pid].add_child(out[id]) out[id].diff = diff end end def parent_id(diff) case mode when :prev diff.parent_id_prev else diff.parent_id end&.upcase end def node_id(diff) case mode when :prev diff.node_id_prev else diff.node_id end&.upcase end end end