Hakushi.Graph (Hakushi v0.1.0)

Copy Markdown View Source

The nodes and edges of a canvas.

graph =
  Hakushi.Graph.new(
    nodes: [
      %{id: "users", type: "table", x: 0, y: 0, data: %{name: "users"}},
      %{id: "orders", type: "table", x: 320, y: 40, data: %{name: "orders"}}
    ],
    edges: [%{source: "orders", source_handle: "user_id", target: "users", target_handle: "id"}]
  )

A graph keeps the order of its nodes, which is the order in which they are drawn: a node drawn later is over the ones before it. New nodes go last.

apply/2 applies a Hakushi.Change and gives back the change that undoes it; the canvas uses it for every change, and so can the app, to keep its own copy of the graph or to save it.

Summary

Types

Why a change does not apply.

t()

Functions

Applies a change. Gives back the graph, the change as it applied, and the change that undoes it

The box around the nodes, {x, y, width, height}, or nil for a graph without nodes. A node without a size counts as a point.

The ids of the nodes in a node (its children), in the order they are drawn.

The ids of the nodes in a node, and in those, all the way down, in the order they are drawn.

What changed from one graph to another: the nodes and edges that are new or not the same, and the ids of those that went away.

The graph as JSON-able maps, with string keys: for a column of the app's database (see Hakushi.Graph.Type) or a file. data goes as it is, so it must be JSON-able itself.

The edge of an id, or nil.

The edges, in the order they were made.

The edges of a node: those that start or end at it.

The graph of dump/1's maps.

Makes a graph of nodes and edges: Hakushi.Nodes and Hakushi.Edges, or maps and keyword lists for Hakushi.Node.new/1 and Hakushi.Edge.new/1. Raises on two nodes or two edges with one id, and on an edge to a node the graph does not have.

The node of an id, or nil.

The nodes, in the order they are drawn.

Types

reason()

@type reason() :: :missing | :exists | :locked | :dangling | :nested

Why a change does not apply.

t()

@type t() :: %Hakushi.Graph{
  edge_order: [Hakushi.Edge.id()],
  edges: %{required(Hakushi.Edge.id()) => Hakushi.Edge.t()},
  node_order: [Hakushi.Node.id()],
  nodes: %{required(Hakushi.Node.id()) => Hakushi.Node.t()}
}

Functions

apply(graph, change)

@spec apply(t(), Hakushi.Change.t()) ::
  {:ok, t(), Hakushi.Change.t(), Hakushi.Change.t()}
  | {:error, reason(), String.t()}

Applies a change. Gives back the graph, the change as it applied, and the change that undoes it:

  • a move of a node moves the nodes in it (parent) too, unless the change places them itself, and a move may put a node in a group or take it out (:parent); the change lists every node it moved;
  • a delete lists the nodes in its nodes, and the edges of all of them, which go with them;
  • the change that undoes a delete adds the nodes back, with their edges (over the other nodes, as new nodes are).

A change does not apply, and nothing changes, when it names a node or an edge that the graph does not have (:missing), adds one it has (:exists), moves, resizes or deletes a locked node (:locked), or adds an edge to a node it does not have, or a node to a parent it does not have (:dangling), or puts a node in itself or in a node inside it (:nested). The error has the id.

bounds(graph)

@spec bounds(t()) :: {number(), number(), number(), number()} | nil

The box around the nodes, {x, y, width, height}, or nil for a graph without nodes. A node without a size counts as a point.

children(graph, id)

@spec children(t(), Hakushi.Node.id()) :: [Hakushi.Node.id()]

The ids of the nodes in a node (its children), in the order they are drawn.

descendants(graph, id)

@spec descendants(t(), Hakushi.Node.id()) :: [Hakushi.Node.id()]

The ids of the nodes in a node, and in those, all the way down, in the order they are drawn.

diff(old, new)

@spec diff(t(), t()) :: %{
  nodes: [Hakushi.Node.t()],
  edges: [Hakushi.Edge.t()],
  deleted_nodes: [Hakushi.Node.id()],
  deleted_edges: [Hakushi.Edge.id()]
}

What changed from one graph to another: the nodes and edges that are new or not the same, and the ids of those that went away.

dump(graph)

@spec dump(t()) :: map()

The graph as JSON-able maps, with string keys: for a column of the app's database (see Hakushi.Graph.Type) or a file. data goes as it is, so it must be JSON-able itself.

edge(graph, id)

@spec edge(t(), Hakushi.Edge.id()) :: Hakushi.Edge.t() | nil

The edge of an id, or nil.

edges(graph)

@spec edges(t()) :: [Hakushi.Edge.t()]

The edges, in the order they were made.

edges_of(graph, id)

@spec edges_of(t(), Hakushi.Node.id()) :: [Hakushi.Edge.t()]

The edges of a node: those that start or end at it.

load(map)

@spec load(map()) :: t()

The graph of dump/1's maps.

new(opts \\ [])

@spec new(keyword()) :: t()

Makes a graph of nodes and edges: Hakushi.Nodes and Hakushi.Edges, or maps and keyword lists for Hakushi.Node.new/1 and Hakushi.Edge.new/1. Raises on two nodes or two edges with one id, and on an edge to a node the graph does not have.

node(graph, id)

@spec node(t(), Hakushi.Node.id()) :: Hakushi.Node.t() | nil

The node of an id, or nil.

nodes(graph)

@spec nodes(t()) :: [Hakushi.Node.t()]

The nodes, in the order they are drawn.