Skip to content

Commit

Permalink
Add new way to set from transitions
Browse files Browse the repository at this point in the history
* Add function `Machinist.from/2` that expects a state
  and a block of `to` statements
  • Loading branch information
norbajunior committed Apr 18, 2021
1 parent 1116f88 commit 37c981f
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 5 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ You can install `machinist` by adding it to your list of dependencies in `mix.e
```elixir
def deps do
[
{:machinist, "~> 0.3.0"}
{:machinist, "~> 0.4.0"}
]
end
```
Expand Down Expand Up @@ -77,6 +77,28 @@ iex> Door.transit(door_opened, event: "lock")
{:error, :not_allowed}
```

### Group transitions with repeated `from` state

In the example above we could group the `from :unlocked` definitions like this:

```elixir
# ...
transitions do
from :locked, to: :unlocked, event: "unlock"
from :unlocked do
to :locked, event: "lock"
to :opened, event: "open"
end
from :opened, to: :closed, event: "close"
from :closed, to: :opened, event: "open"
from :closed, to: :locked, event: "lock"
end
# ...
```

This is an option to help us organize and increase the readability when having
a large number of transition definitions

### Setting different attribute name that holds the state

By default `machinist` expects the struct being updated holds a `state` attribute, if you hold state in a different attribute, just pass the name as an atom, as follows:
Expand Down
40 changes: 40 additions & 0 deletions lib/machinist.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ defmodule Machinist do
iex> Door.transit(door_opened, event: "lock")
iex> {:error, :not_allowed}
### Group transitions of many repeated `from` state
In the example above we also could group the `from :unlocked` definitions:
# ...
transitions do
from :locked, to: :unlocked, event: "unlock"
from :unlocked do
to :locked, event: "lock"
to :opened, event: "open"
end
from :opened, to: :closed, event: "close"
from :closed, to: :opened, event: "open"
from :closed, to: :locked, event: "lock"
end
# ...
This is an option to help us organize and increase the readability when having
a large number of transition definitions
### Setting different attribute name that holds the state
By default `machinist` expects the struct being updated holds a `state` attribute,
Expand Down Expand Up @@ -346,7 +366,27 @@ defmodule Machinist do
from _state, to: :expired, event: "enrollment_expired"
"""
defmacro from(state, do: {_, _, to_statements}) do
define_transitions(state, to_statements)
end

defmacro from(state, to: new_state, event: event) do
define_transition(state, to: new_state, event: event)
end

@doc false
defp define_transitions(_state, []), do: []

@doc false
defp define_transitions(state, [{:to, _line, [new_state, [event: event]]} | transitions]) do
[
define_transition(state, to: new_state, event: event)
| define_transitions(state, transitions)
]
end

@doc false
defp define_transition(state, to: new_state, event: event) do
quote do
@impl true
def transit(%@__struct__{@__attr__ => unquote(state)} = resource, event: unquote(event)) do
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
defmodule Machinist.MixProject do
use Mix.Project

@version "0.3.0"
@version "0.4.0"
@repo_url "https://github.com/norbajunior/machinist"

def project do
[
app: :machinist,
version: "0.3.0",
version: @version,
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps(),
Expand Down
33 changes: 31 additions & 2 deletions test/machinist_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,12 @@ defmodule MachinistTest do
transitions do
from(:new, to: :registered, event: "register")
from(:registered, to: :interview_scheduled, event: "schedule_interview")
from(:interview_scheduled, to: :approved, event: "approve_interview")
from(:interview_scheduled, to: :repproved, event: "reprove_interview")

from :interview_scheduled do
to(:approved, event: "approve_interview")
to(:repproved, event: "reprove_interview")
end

from(:approved, to: :enrolled, event: "enroll")
from(_state, to: :application_expired, event: "application_expired")
end
Expand All @@ -152,4 +156,29 @@ defmodule MachinistTest do
Example5.transit(%Example5{state: :approved}, event: "application_expired")
end
end

describe "a example with passing a block of transitions to from" do
defmodule Example6 do
defstruct state: :test

use Machinist

transitions do
from(:test, to: :test1, event: "test1")

from :test1 do
to(:test2, event: "test2")
to(:test3, event: "test3")
to(:test4, event: "test4")
end
end
end

test "all transitions" do
assert {:ok, example} = Example6.transit(%Example6{}, event: "test1")
assert {:ok, %Example6{state: :test2}} = Example6.transit(example, event: "test2")
assert {:ok, %Example6{state: :test3}} = Example6.transit(example, event: "test3")
assert {:ok, %Example6{state: :test4}} = Example6.transit(example, event: "test4")
end
end
end

0 comments on commit 37c981f

Please sign in to comment.