Skip to content

Commit

Permalink
Implemented asiniy#18
Browse files Browse the repository at this point in the history
  • Loading branch information
h4cc committed Nov 24, 2016
1 parent d8b5541 commit 9065a2e
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ erl_crash.dump
*.ez
.idea
*.iml
*.gv
*.png
10 changes: 10 additions & 0 deletions graphviz_example.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

# Generate and write Grpahviz file.
IO.puts "ESM Config:"
dot = Dummy.User.esm_config(:rules)
|> IO.inspect
|> EctoStateMachine.config_to_dot
File.write!("dummy_user_rules.gv", dot)

# Running dot to create PND
System.cmd("dot", ["-Tpng", "dummy_user_rules.gv", "-o", "dummy_user_rules.png"])
48 changes: 47 additions & 1 deletion lib/ecto_state_machine.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ defmodule EctoStateMachine do
states = Keyword.get(opts, :states)
events = Keyword.get(opts, :events)
|> Enum.map(fn(event) ->
Keyword.put_new(event, :callback, quote do: fn(model) -> model end)
event
|> Keyword.put(:is_custom_callback, Keyword.has_key?(event, :callback))
|> Keyword.put_new(:callback, quote do: fn(model) -> model end)
end)
|> Enum.map(fn(event) ->
Keyword.update!(event, :callback, &Macro.escape/1)
Expand Down Expand Up @@ -43,6 +45,50 @@ defmodule EctoStateMachine do
)
end
end

@doc "Returning the config from EctoStateMachine for given column atom."
def esm_config(unquote(column)) do
%{
module: __MODULE__,
column: unquote(column),
states: unquote(states),
events: unquote(events) |> Enum.map(&Enum.into(&1, %{})),
}
end
end
end

@doc """
Creating a GraphViz definition from a config given by esm_config.
"""
def config_to_dot(%{module: module, column: column, states: states, events: events}) when states != [] do

transition_strings = events
|> Enum.flat_map(fn(%{from: states_from, name: transition_name, to: state_to, is_custom_callback: custom_callback}) ->
states_from
|> Enum.map(fn(state_from) ->
{transition_name, state_from, state_to, custom_callback}
end)
end)
|> Enum.map(fn({transition_name, state_from, state_to, custom_callback}) ->
case custom_callback do
true -> "#{state_from} -> #{state_to} [style=\"bold\", label=< <B>#{transition_name}</B> >]"
false -> "#{state_from} -> #{state_to} [label=<#{transition_name}>]"
end
end)

"digraph G {
// initial node
\"\" [shape=none];
\"\" -> \"#{states |> List.first}\";
// Transitions.
#{transition_strings |> Enum.join(" ; ")}
// title
labelloc=\"t\";
label=\"#{module} #{column}\";
}"
end

end
21 changes: 21 additions & 0 deletions test/ecto_state_machine_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,25 @@ defmodule EctoStateMachineTest do
assert Dummy.User.can_make_admin?(context[:admin]) == false
end
end

describe "esm_config" do
it "can get config for :rules" do
assert match? %{
module: Dummy.User,
column: :rules,
events: [
%{from: [:unconfirmed], name: :confirm, to: :confirmed, callback: _, is_custom_callback: true},
%{from: [:confirmed, :admin], name: :block, to: :blocked, callback: _, is_custom_callback: false},
%{from: [:confirmed], name: :make_admin, to: :admin, callback: _, is_custom_callback: false},
],
states: [:unconfirmed, :confirmed, :blocked, :admin]
}, Dummy.User.esm_config(:rules)
end
end

describe "config_to_dot" do
it "can convert config to dot" do
assert "digraph G {\n // initial node\n \"\" [shape=none];\n \"\" -> \"unconfirmed\";\n\n // Transitions.\n unconfirmed -> confirmed [style=\"bold\", label=< <B>confirm</B> >] ; confirmed -> blocked [label=<block>] ; admin -> blocked [label=<block>] ; confirmed -> admin [label=<make_admin>]\n\n // title\n labelloc=\"t\";\n label=\"Elixir.Dummy.User rules\";\n }" == Dummy.User.esm_config(:rules) |> EctoStateMachine.config_to_dot
end
end
end

0 comments on commit 9065a2e

Please sign in to comment.