improvement: make state diagrams the default chart

docs: docs improvements
This commit is contained in:
Zach Daniel 2023-04-23 00:58:34 -06:00
parent f86b2f6692
commit bd66b9236f
4 changed files with 54 additions and 19 deletions

View file

@ -107,13 +107,13 @@ end
run `mix ash_state_machine.generate_flow_charts` to generate flow charts for your resources. See the task documentation for more. Here is a chart generated from the example above: run `mix ash_state_machine.generate_flow_charts` to generate flow charts for your resources. See the task documentation for more. Here is a chart generated from the example above:
```mermaid ```mermaid
flowchart TD stateDiagram-v2
pending --> |confirm| confirmed pending --> confirmed: confirm
confirmed --> |begin_delivery| on_its_way confirmed --> on_its_way: begin_delivery
on_its_way --> |package_arrived| arrived on_its_way --> arrived: package_arrived
on_its_way --> |error| error on_its_way --> error: error
confirmed --> |error| error confirmed --> error: error
pending --> |error| error pending --> error: error
``` ```
## Learning more ## Learning more

View file

@ -3,18 +3,29 @@ defmodule AshStateMachine.Charts do
Returns a mermaid flow chart of a given state machine resource. Returns a mermaid flow chart of a given state machine resource.
""" """
@spec mermaid_state_diagram(Ash.Resource.t()) :: String.t()
def mermaid_state_diagram(resource) do
resource
|> AshStateMachine.Info.state_machine_initial_states!()
|> Enum.reduce({["stateDiagram-v2"], MapSet.new()}, fn state, {lines, checked} ->
add_to_chart(resource, state, lines ++ [], checked, :state_diagram)
end)
|> elem(0)
|> Enum.join("\n")
end
@spec mermaid_flowchart(Ash.Resource.t()) :: String.t() @spec mermaid_flowchart(Ash.Resource.t()) :: String.t()
def mermaid_flowchart(resource) do def mermaid_flowchart(resource) do
resource resource
|> AshStateMachine.Info.state_machine_initial_states!() |> AshStateMachine.Info.state_machine_initial_states!()
|> Enum.reduce({["flowchart TD"], MapSet.new()}, fn state, {lines, checked} -> |> Enum.reduce({["flowchart TD"], MapSet.new()}, fn state, {lines, checked} ->
add_to_chart(resource, state, lines ++ [], checked) add_to_chart(resource, state, lines ++ [], checked, :flow_chart)
end) end)
|> elem(0) |> elem(0)
|> Enum.join("\n") |> Enum.join("\n")
end end
defp add_to_chart(resource, state, lines, checked) do defp add_to_chart(resource, state, lines, checked, type) do
if state in checked do if state in checked do
{lines, checked} {lines, checked}
else else
@ -24,14 +35,28 @@ defmodule AshStateMachine.Charts do
|> transitions_from(resource) |> transitions_from(resource)
|> Enum.reduce({lines, checked}, fn event, {lines, checked} -> |> Enum.reduce({lines, checked}, fn event, {lines, checked} ->
Enum.reduce(List.wrap(event.to), {lines, checked}, fn to, {lines, checked} -> Enum.reduce(List.wrap(event.to), {lines, checked}, fn to, {lines, checked} ->
name = lines =
case event.action do case type do
:* -> "" :flow_chart ->
action -> "|#{action}|" name =
case event.action do
:* -> ""
action -> "|#{action}|"
end
lines ++ ["#{state} --> #{name} #{to}"]
:state_diagram ->
name =
case event.action do
:* -> ""
action -> ": #{action}"
end
lines ++ ["#{state} --> #{to}#{name}"]
end end
lines = lines ++ ["#{state} --> #{name} #{to}"] add_to_chart(resource, to, lines, checked, type)
add_to_chart(resource, to, lines, checked)
end) end)
end) end)
end end

View file

@ -11,6 +11,7 @@ defmodule Mix.Tasks.AshStateMachine.GenerateFlowCharts do
## Command line options ## Command line options
* `--type` - generates a given type. Valid values are `"state_diagram"` and `"flow_chart"`. Defaults to `"state_diagram"`.
* `--only` - only generates the given Flow file * `--only` - only generates the given Flow file
* `--format` - Can be set to one of either: * `--format` - Can be set to one of either:
* `plain` - Prints just the mermaid output as text. This is the default. * `plain` - Prints just the mermaid output as text. This is the default.
@ -30,8 +31,8 @@ defmodule Mix.Tasks.AshStateMachine.GenerateFlowCharts do
{opts, _} = {opts, _} =
OptionParser.parse!(argv, OptionParser.parse!(argv,
strict: [only: :keep, format: :string], strict: [only: :keep, format: :string, type: :string],
aliases: [o: :only, f: :format] aliases: [o: :only, f: :format, t: :type]
) )
only = only =
@ -47,11 +48,20 @@ defmodule Mix.Tasks.AshStateMachine.GenerateFlowCharts do
source = state_machine.module_info(:compile)[:source] source = state_machine.module_info(:compile)[:source]
if is_nil(only) || Path.expand(source) in only do if is_nil(only) || Path.expand(source) in only do
diagram =
case opts[:type] || "state_diagram" do
"state_diagram" ->
AshStateMachine.Charts.mermaid_state_diagram(state_machine)
"flow_chart" ->
AshStateMachine.Charts.mermaid_flowchart(state_machine)
end
Mix.Mermaid.generate_diagram( Mix.Mermaid.generate_diagram(
source, source,
"mermaid-flowchart", "mermaid-flowchart",
format, format,
AshStateMachine.Charts.mermaid_flowchart(state_machine), diagram,
"Generated Mermaid flowchart for #{inspect(state_machine)}" "Generated Mermaid flowchart for #{inspect(state_machine)}"
) )
end end

View file

@ -154,7 +154,7 @@ defmodule AshStateMachineTest do
describe "charts" do describe "charts" do
test "it generates the appropriate chart" do test "it generates the appropriate chart" do
AshStateMachine.Charts.mermaid_flowchart(Order) |> IO.puts() AshStateMachine.Charts.mermaid_state_diagram(Order) |> IO.puts()
assert AshStateMachine.Charts.mermaid_flowchart(ThreeStates) == assert AshStateMachine.Charts.mermaid_flowchart(ThreeStates) ==
""" """