Module: Plushie::Tree
- Defined in:
- lib/plushie/tree.rb
Overview
Utilities for working with UI trees.
Provides normalization, search, and diffing for Node trees. The diff algorithm produces patch operations per the wire protocol spec (replace_node, update_props, insert_child, remove_child).
Class Method Summary collapse
-
.diff(old_tree, new_tree) ⇒ Array<Hash>
Diff two normalized trees, producing an array of patch operations.
-
.exists?(tree, id) ⇒ Boolean
Check if a node with the given ID exists.
-
.find(tree, id) ⇒ Node?
Find a node by ID (depth-first).
-
.find_all(tree) {|Node| ... } ⇒ Array<Node>
Find all nodes matching a predicate (depth-first).
-
.ids(tree) ⇒ Array<String>
Return all node IDs in depth-first order.
-
.node_to_wire(node) ⇒ Hash
Convert a Node to a plain wire-ready Hash (recursive).
-
.normalize(tree) ⇒ Array<Node>
Normalize a tree for wire transport.
Class Method Details
.diff(old_tree, new_tree) ⇒ Array<Hash>
Diff two normalized trees, producing an array of patch operations.
Each op is a Hash with string keys matching the wire protocol:
{ "op" => "replace_node", "path" => [...], "node" => {...} }
{ "op" => "update_props", "path" => [...], "props" => {...} }
{ "op" => "insert_child", "path" => [...], "index" => n, "node" => {...} }
{ "op" => "remove_child", "path" => [...], "index" => n }
107 108 109 110 111 112 113 114 |
# File 'lib/plushie/tree.rb', line 107 def self.diff(old_tree, new_tree) return [] if old_tree.nil? && new_tree.nil? return [{"op" => "replace_node", "path" => [], "node" => node_to_wire(new_tree)}] if old_tree.nil? return [{"op" => "remove_child", "path" => [], "index" => 0}] if new_tree.nil? return [{"op" => "replace_node", "path" => [], "node" => node_to_wire(new_tree)}] if old_tree.id != new_tree.id diff_node(old_tree, new_tree, []) end |
.exists?(tree, id) ⇒ Boolean
Check if a node with the given ID exists.
39 40 41 |
# File 'lib/plushie/tree.rb', line 39 def self.exists?(tree, id) !find(tree, id).nil? end |
.find(tree, id) ⇒ Node?
Find a node by ID (depth-first).
21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/plushie/tree.rb', line 21 def self.find(tree, id) return nil if tree.nil? trees = tree.is_a?(Array) ? tree : [tree] trees.each do |node| return node if node.id == id found = find(node.children, id) return found if found end nil end |
.find_all(tree) {|Node| ... } ⇒ Array<Node>
Find all nodes matching a predicate (depth-first).
64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/plushie/tree.rb', line 64 def self.find_all(tree, &predicate) result = [] trees = tree.is_a?(Array) ? tree : [tree] trees.each do |node| result << node if predicate.call(node) result.concat(find_all(node.children, &predicate)) end result end |
.ids(tree) ⇒ Array<String>
Return all node IDs in depth-first order.
47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/plushie/tree.rb', line 47 def self.ids(tree) result = [] trees = tree.is_a?(Array) ? tree : [tree] trees.each do |node| result << node.id result.concat(ids(node.children)) end result end |
.node_to_wire(node) ⇒ Hash
Convert a Node to a plain wire-ready Hash (recursive).
120 121 122 123 124 125 126 127 |
# File 'lib/plushie/tree.rb', line 120 def self.node_to_wire(node) { "id" => node.id, "type" => node.type, "props" => encode_props(node.props), "children" => node.children.map { |c| node_to_wire(c) } } end |
.normalize(tree) ⇒ Array<Node>
Normalize a tree for wire transport. Converts symbol prop values to strings via Encode, resolves scoped IDs, and validates tree structure.
86 87 88 89 90 |
# File 'lib/plushie/tree.rb', line 86 def self.normalize(tree) return [Node.new(id: "root", type: "container")] if tree.nil? trees = tree.is_a?(Array) ? tree : [tree] trees.compact.map { |node| normalize_node(node, "") } end |