Draws an AshWorkflow.Charts.Graph as a Graphviz
DOT diagram: the steps, edges and labels of AshWorkflow.Charts.Mermaid,
with colour and line styles. The dot command of Graphviz draws DOT as
SVG, PNG or PDF.
# The DOT source:
AshWorkflow.Charts.render(MyApp.Candidate, :dot)
# The SVG. This runs `dot`, which must be installed:
AshWorkflow.Charts.render(MyApp.Candidate, :dot, svg: true)What the chart shows
| In the workflow | In the chart |
|---|---|
| Automatic step | Blue box, ⚙️ automatic under the name |
| Manual step | Amber box, ✋ manual under the name |
| Wait state | Lavender box with a dashed border, ⏳ wait state under the name |
| Terminal step | Double border. Green, red, amber or grey: see "Terminal steps" |
| Initial step | An edge from a small filled circle |
on_success | Thick blue edge, ⚙ and the action |
on_error | Red edge, ✖ on_error |
| Timeout | Dashed amber edge, ⏱, the timeout's name and its deadline |
| Undo | Dotted grey edge with a hollow arrowhead, ↶ undo, and the window when within is set |
| Transition | Plain edge, the transition's name |
| Step notes | Under a rule inside the step: policy:, retry:, ⏱ and ↻ |
Each edge kind other than a transition also has a symbol, so a reader who
cannot tell the colours apart still knows the kind. The colours come from
AshWorkflow.Charts.Palette. The label text comes from
AshWorkflow.Charts.Format, so it is the same in every format.
Terminal steps
The colour of a terminal step comes from the edges that reach it:
- Red when only
on_erroredges reach it. - Amber when only timeouts reach it.
- Green when only transitions and
on_successreach it. - Grey for a mix of these, or for no edge at all.
So a rejected step that a person chooses is green, the same as
approved: the chart cannot tell a good outcome from a bad one.
AshWorkflow.Charts.Palette.terminal_class/2 gives the class of one step.
Layout
dot puts the steps in ranks, from the initial step to the end steps. An
edge that closes a loop, such as a transition back to an earlier step,
goes back up the ranks.
An undo edge goes from a later step to an earlier step. The file writes it
from the earlier step, with dir="back" to put the arrowhead on the
earlier step. So dot ranks it as the edge that it undoes: the steps keep
their forward order, and the undo edge runs next to that edge. The
arrowhead is at the tail, so the :undo class sets arrowtail, not
arrowhead.
IDs and escaping
Each step is a node with the ID step_<name>, so no step can have the ID
of the start circle, start.
Every ID and every label is a double-quoted string, with " and \
escaped. So a step name or a condition cannot break the file, or start a
Graphviz escape sequence such as \N.
Graphviz reads an HTML entity in a label, such as &, as its
character. So the file writes each & in a label as &. An ID keeps
its &.
Options
:svg— rundotand return the SVG, not the DOT source. Defaults tofalse. The SVG sets its width and height, so an<img>tag shows it at full size. See "Graphviz".:direction—:down(the default) or:right. Right gives a long, thin chart. Use it only for a short chain of steps.:theme—:light(the default) or:dark. See "Dark theme".:classes— your own colours and line styles. See "Your own styles".
:theme and :classes can also go under config :ash_workflow, :dot, for
every chart. An option merges over the config. A nil option or config
key counts as not set. A config that is not a keyword list raises
ArgumentError.
Your own styles
A class holds the DOT attributes of one kind of step or edge. DOT has no
classes, so the file writes them on each node or edge. To change a class,
pass :classes:
AshWorkflow.Charts.render(MyApp.Candidate, :dot,
classes: [manual: [fillcolor: "#FFE4E6"], undo: [style: "dashed"]]
)Each class merges over its default, key by key, so manual steps keep their amber border. The classes are:
:automatic,:manualand:wait_state, for the step kinds.:done,:failed,:expiredand:end, for terminal steps.:on_success,:on_error,:timeoutand:undo, for edges.
A key is a Graphviz attribute name, such as :fillcolor, :penwidth or
:URL. A step class can set a node attribute, and an edge class can set an
edge attribute: see the "Used By" column of the
Graphviz attribute reference.
No class can set :label, which the chart writes, and :undo cannot set
:dir. See "Layout".
A value is a string, a number or a boolean. dot checks the value itself,
such as a colour. Any other key, a value of another type, or an unknown
class raises ArgumentError, and the message lists the keys that the class
can set. default_classes/1 gives the defaults.
A list of classes applies to both themes, so a fill for the light theme can
sit under the light text of the dark theme. To give each theme its own
colours, put the classes under :light and :dark:
AshWorkflow.Charts.render(MyApp.Candidate, :dot,
theme: :dark,
classes: [
light: [manual: [fillcolor: "#FFE4E6"]],
dark: [manual: [fillcolor: "#881337"]]
]
)Only the entry for the current theme applies. A theme given twice applies
both entries in order. A list that mixes :light or :dark with class
names raises ArgumentError.
Dark theme
theme: :dark selects a dark background and dark classes: deep fills,
light borders and light text. An SVG has one theme, so a page that follows
the reader's theme needs one chart for each theme.
Graphviz
svg: true runs the dot command of Graphviz. This library does not
download Graphviz. Install it with your package manager, such as
apt-get install graphviz or brew install graphviz.
config :ash_workflow, :dot, path: "/opt/graphviz/bin/dot":path— thedotto run, elsedotfrom thePATH. A name with no/, such as"dot", is a command on thePATH. A path with a/is an executable file, relative to the current directory, and~is the home directory.niland""count as no:path, sopath: System.get_env("DOT_PATH")usesdotfrom thePATHwhen the variable is not set or is empty. Any other value that is not a string raisesArgumentError.
With no dot, svg: true raises ArgumentError. executable/0 gives the
dot that svg: true runs. When dot draws the chart but prints a
message, such as a warning about an unknown colour, the chart logs it with
Logger.warning/1.
Contrast
By the WCAG 2 formula:
| Pair | Light theme | Dark theme |
|---|---|---|
| Step label text on its fill | above 16:1 | above 8.6:1 |
| Edge label text on the background | above 18.9:1 | above 15.6:1 |
| Any stroke on the background | above 3.6:1 | above 6.4:1 |
Summary
Functions
The default attributes of each class for a theme: a keyword list from the
class name to its DOT attributes. A step class starts with fillcolor,
color and fontcolor, and an edge class starts with color. See "Your
own styles" in the module documentation.
The absolute path of the dot that svg: true runs, or nil when there
is none: the configured :path, or dot from the PATH when there is no
:path. See "Graphviz" in the module documentation.
The theme a chart draws with: the :theme option, else the :theme under
config :ash_workflow, :dot, else :light.
Functions
The default attributes of each class for a theme: a keyword list from the
class name to its DOT attributes. A step class starts with fillcolor,
color and fontcolor, and an edge class starts with color. See "Your
own styles" in the module documentation.
@spec executable() :: Path.t() | nil
The absolute path of the dot that svg: true runs, or nil when there
is none: the configured :path, or dot from the PATH when there is no
:path. See "Graphviz" in the module documentation.
Raises ArgumentError when :path is not a string, or when
config :ash_workflow, :dot is not a keyword list.
@spec theme(keyword()) :: :light | :dark
The theme a chart draws with: the :theme option, else the :theme under
config :ash_workflow, :dot, else :light.
Raises ArgumentError for a theme that is not :light or :dark.