# Codex
## [![Hex pm](http://img.shields.io/hexpm/v/codex.svg?style=flat)](https://hex.pm/packages/codex) [![Hex Docs](https://img.shields.io/badge/hex-docs-9768d1.svg)](https://hexdocs.pm/codex) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)![.github/workflows/elixir.yml](https://github.com/maartenvanvliet/codex/workflows/.github/workflows/elixir.yml/badge.svg)
<!-- MDOC !-->
Library to facilitate control flow, providing a Plug like interface.
## Installation
```elixir
def deps do
[
{:codex, "~> 0.9.0"}
]
end
```
## Usage
In it's simplest form Codex gives you a module with a `call/2` function that will be
with `run/2`
```elixir
defmodule SimpleFlow do
use Codex
def call(params, _) do
{:ok, %{params | test: false}}
end
end
iex> Simple.run(%{test: true})
{:ok, %{test: false}}
```
### Adding function steps
You can add extra steps that are run before the `call/2`. These are passed the params and should return
an `:ok/:error` tuple. It halts the flow when an `:error` tuple is returned. The second element of the :ok tuple is passed to the next step.
```elixir
defmodule FunctionFlow do
use Codex
step :double
step :add_one
def call(params, _) do
{:ok, params}
end
def double(params) do
{:ok, %{params | test: params[:test] * 2}}
end
def add_one(params) do
{:ok, %{params | test: params[:test] + 1}}
end
end
iex> FunctionFlow.run(%{test: 2})
{:ok, %{test: 5}}
```
#### Adding module steps
Additionaly, you can also extract common functionality into modules and use those as steps.
```elixir
defmodule Double do
use Codex
# optional init
def init(opts) do
opts[:key]
end
def call(params, key) do
params = Map.put(params, key, params[key] * 2)
{:ok, params}
end
end
defmodule ModuleFlow do
use Codex
# the second argument is passed to the init function
step Double, key: :test
def call(params, _) do
{:ok, params}
end
end
iex> ModuleFlow.run(%{test: 2})
{:ok, %{test: 4}}
```
A module step can also contain steps itself, allowing you to compose steps. These are executed before the `call/2` function. E.g. you can add an `inspect` step.
```elixir
defmodule Double do
use Codex
step :inspect
def call(params, opts) do
params = Map.put(params, opts[:key], params[opts[:key]] * 2)
{:ok, params}
end
def inspect(params) do
IO.inspect(params, label: "double")
end
end
# outputs: "double: %{test: 2}" in the inspect step
iex> ModuleFlow.run(%{test: 2})
{:ok, %{test: 4}}
```
#### Checking parameters
Codex modules can optionally check their input before execution. This is by default done with `Norm`.
```elixir
defmodule ParamCheck do
use Codex
params schema(%{
name: spec(is_binary())
})
def call(params, _) do
{:ok, params.name}
end
end
iex> ParamCheck.run(%{name: "Miles"})
{:ok, "Miles"}
iex> ParamCheck.run(%{name: 1})
{:error, [%{input: 1, path: [:name], spec: "is_binary()"}]}
```
## Inspiration
This library draws inspiration from https://github.com/zorbash/opus and https://github.com/madeinussr/exop