A block document described in words: an outline of its blocks joined by the ways control passes between them, and one line of English per block and per edge (ADR-0016).
iex> alias StatifierBlocks.{Block, Describe, Document, Palette}
iex> root =
...> Block.new("core.sequence",
...> id: "root",
...> slots: %{
...> "body" => [
...> Block.new("core.wait", id: "wait", config: %{"duration" => "30s"}),
...> Block.new("core.send", id: "send", config: %{"event" => "order.paid"})
...> ]
...> }
...> )
iex> root |> Document.new() |> Describe.outline(Palette.core(), []) |> Describe.render([])
[
"Run its steps in order",
"Wait 30s",
"Send order.paid",
"Run its steps in order starts with Wait 30s",
"After Wait 30s (done), Send order.paid",
"Send order.paid (done) ends Run its steps in order"
]Pure, and no model anywhere
outline/3 is a function of the document, the palette and its options,
and render/2 of the outline and its options. Neither compiles the
document, reads a clock or a random source, starts or messages a
process, reaches a network or asks a language model, and equal input
answers byte-identical output. The only code either runs that this
package does not own is the palette's block-type callbacks and the
host's phrasing module, each held to be a pure function of its arguments
by its own contract (StatifierBlocks.BlockType,
StatifierBlocks.Describe.Phrasing).
Nodes
One StatifierBlocks.Describe.Node per block, every block exactly once,
in StatifierBlocks.ViewModel.outline/1's order. A block whose type the
palette cannot resolve is described as its placeholder rather than
refused.
Edges, from the document's structure
The edges are the block-level flow graph of the package's note
docs/block-level-flow-graph.md, read from the document's tree and the
block types' declarations; nothing is compiled. Edges are found inside a
resolved block of exactly the four types the note works through:
core.sequence: an:entryedge from its entry to its first child, a:sequenceedge from each child to the next, and an:exitedge from the last child to its exit. An empty body is one:entryedge from its entry straight to its exit.core.group,core.resumable_group: the same three over thebodyslot, then one:interruptedge per handler in theinterruptsslot whoseoutcomeisabandon(to the group's exit) orresume(to the group's body, carrying a resumable group'shistory).core.branch: per arm, in slot order, a:branchedge from the branch's entry to the arm's first child - or to the branch's exit for an empty arm - followed by that arm's:sequenceedges and the:exitedge from its last child to the branch's exit, where the arms converge.otherwisealways has its edge;undecidedhas one only when it holds a block.
A child here is one StatifierBlocks.ViewModel.flow_children/1 answers
for the slot: a drafts shelf is a node and takes part in no edge.
A :sequence or :exit edge carries every outcome name the source
block's type declares, on the one edge. They are the declared outcomes,
not a compile's finals: core.await declares received and timed_out
whatever its config, so both ride its edge even with no timeout set.
Every other type - core.parallel, core.foreach, core.map,
core.subchart, core.invoke, a composite, a host type - is described
by containment and its node's fan_label alone, and draws no edge among
its children; a block of one still takes part in its parent's edges. An
event name shared by a send and a handler is not an edge.
Lines
render/2 answers one line per node, then one per edge, in the
outline's orders; ADR-0016 decision 2 gives each default line's words.
Every line is non-blank English with no newline, carriage return or tab,
and uncapped: a newline, carriage return or tab inside an author's text
is written as one space. A block's id never appears in a default line.
Summary
Functions
The outline of document under palette: its id and revision, one node
per block and the edges between them, as the moduledoc describes.
One line per node in the outline's node order, then one line per edge in its edge order.
Types
@type t() :: %StatifierBlocks.Describe{ edges: [StatifierBlocks.Describe.Edge.t()], id: StatifierBlocks.Document.id(), nodes: [StatifierBlocks.Describe.Node.t()], revision: non_neg_integer() }
Functions
@spec outline(StatifierBlocks.Document.t(), StatifierBlocks.Palette.t(), keyword()) :: t()
The outline of document under palette: its id and revision, one node
per block and the edges between them, as the moduledoc describes.
opts is a keyword list; no key is read, and an unknown one is ignored.
One line per node in the outline's node order, then one line per edge in its edge order.
Every line is first written in the package's own words. With
phrasing: module, a module implementing
StatifierBlocks.Describe.Phrasing, each line is then offered to the
module's callback for the node's or edge's kind, where it declares one;
a usable answer replaces the line, and :default or a refused answer
keeps it (Phrasing gives the refusal set). Without the option every
line is the default.