module Exa class TreeNode attr_reader :name, :parent, :overlays, :links def initialize(name:, value: nil, parent: nil, virtual: false, symbolic: false) p [ :tree_node, name: name ] @name = name @value = value @parent = parent @virtual = virtual @symbolic = symbolic @children = [] @overlays = [] @links = [] end def value if virtual? constituents.first.value elsif symbolic? dereference_symbolic_link.value else @value end end def virtual? @virtual end def symbolic? @symbolic end def inspect "<#{@name}>" end def path if symbolic? dereference_symbolic_link.path else if @parent slash_name = "/#@name" if @parent.path == '/' slash_name else @parent.path + slash_name end else '/' #@name end end end def update(val) @value = val self end def create_child(child_name:) child = TreeNode.new(name: child_name, parent: self) @children << child child end def remove_child(child_name:) child = @children.detect { |c| c.name == child_name } @children.delete(child) self end def unify(overlay:) if overlay.virtual? raise "Won't union mount virtual paths! (Try `link(source: ...)` instead.)" end @overlays << overlay self end def link(source:) @links << source self end def children if virtual? virtualize(constituents.flat_map(&:children)) else @children + symbolize(symbolic_children) + virtualize(virtual_children) end end def recall(path) Visitor.new(self).seek(path) end alias :[] :recall def query(path) Visitor.new(self).query(path) end def copy(target) Copier.new(self, target).perform! end def delete Deleter.new(self).perform! end protected def constituents sources = if parent.virtual? parent.constituents.flat_map(&:children) else parent.overlays.flat_map(&:children) end sources.select do |candidate| candidate.name == @name end end def virtual_children @overlays.flat_map(&:children) end def symbolic_children # this is really a reference @links.flat_map(&:children) end def dereference_symbolic_link # okay, we're symbolic... so our parents created us # and have a link parent.links.flat_map(&:children).detect do |linked_child| linked_child.name == @name end end private def symbolize(schildren) schildren.map(&method(:symbolize_one)) end def symbolize_one(schild) TreeNode.new(name: schild.name, value: schild.value, parent: self, symbolic: true) end def virtualize(vchildren) vchildren.map(&method(:virtualize_one)) end def virtualize_one(vchild) TreeNode.new(name: vchild.name, value: vchild.value, parent: self, virtual: true) end end end