# Machinery
[![Build Status](https://travis-ci.org/joaomdmoura/machinery.svg?branch=master)](https://travis-ci.org/joaomdmoura/machinery)
[![Coverage Status](https://coveralls.io/repos/github/joaomdmoura/machinery/badge.svg?branch=master)](https://coveralls.io/github/joaomdmoura/machinery?branch=master)
[![Ebert](https://ebertapp.io/github/joaomdmoura/machinery.svg)](https://ebertapp.io/github/joaomdmoura/machinery)
![Machinery](https://github.com/joaomdmoura/machinery/blob/master/logo.png)
Machinery is a State Machine library for structs in general that integrates with
Pheonix out of the box.
It also aims to have (when implemented with Phoenix) an optional build-in GUI
that will represent each resource's state.
## DISCLAMER
Machinery is under heavy development, this README does't match the docs for the
current released version `0.2.0`, you can check the docs on the proper released
[Machinery Docs](https://hexdocs.pm/machinery)
- [Installing](#installing)
- [Declaring States](#declaring-states)
- [Changing States](#changing-states)
- [Guard Functions](#guard-functions)
- [Before and After Callbacks](#before-and-after-callbacks)
## Installing
The package can be installed by adding `machinery` to your list of
dependencies in `mix.exs`:
```elixir
def deps do
[
{:machinery, "~> 0.2.0"}
]
end
```
## Declaring States
Declare the states you need pasing it as an argment when importing `Machinery`
on the module that will control your states transitions.
Machinery expects a `Keyword` as argument with two keys `states` and `transitions`.
- `states`: A List of Atoms representing each state.
- `transitions`: A List of Maps, including two keys `from` and `to`, `to` might be an Atom or a List of Atoms.
### Example
```elixir
defmodule YourProject.UserStateMachine do
use Machinery,
# The first state declared will be considered the intial state
states: [:created, :partial, :complete],
transitions: %{
created: [:partial, :complete],
partial: :completed
}
end
```
## Changing States
To transit a struct into another state, you just need to call `Machinery.transition_to/2`
### `Machinery.transition_to/2`
It takes two arguments:
- `struct`: The struct you want to transit to another state
- `next_event`: An atom representing the next state you want the struct to transition to
```elixir
Machinery.transition_to(your_struct, :next_state)
# {:ok, updated_struct}
```
### Example:
```elixir
user = Accounts.get_user!(1)
UserStateMachine.transition_to(user, :partial)
```
Guard functions, before and after callbacks will be checked automatically.
## Guard functions
Create guard conditions by adding signatures of the `guard_transition/2`
function, pattern matching the desired state you want to guard.
Guard conditions should return a boolean:
- `true`: Guard clause will allow the transition.
- `false`: Transition won't be allowed.
```elixir
defmodule YourProject.UserStateMachine do
use Machinery,
# The first state declared will be considered the intial state
states: [:created, :partial, :complete],
transitions: %{
created: [:partial, :complete],
partial: :completed
}
def guard_transition(struct, :complete) do
Map.get(struct, :missing_fields) == false
end
end
```
## Before and After callbacks
You can also use before and after callbacks to handle desired side effects and
reactions to a specific state transition, the implementation is pretty similar to
guard functions, you can just declare `before_transition/2` and ``after_transition/2`.
Before and After callbacks should return the struct being manipulated.
```elixir
defmodule YourProject.UserStateMachine do
use Machinery,
# The first state declared will be considered the intial state
states: [:created, :partial, :complete],
transitions: %{
created: [:partial, :complete],
partial: :completed
}
def before_transition(struct, :partial) do
# ... overall desired side effect
struct
end
def after_transition(struct, :completed) do
# ... overall desired side effect
struct
end
end
```