Packages

State machines, statecharts and workflows for Ecto models.

Current section

Files

Jump to
ex_state_ecto lib ex_state ecto model.ex
Raw

lib/ex_state/ecto/model.ex

defmodule ExState.Ecto.Model do
defmacro __using__(opts \\ []) do
primary_key = Keyword.get(opts, :primary_key, [])
primary_key_type = Keyword.get(primary_key, :type, Ecto.UUID)
autogenerate = Keyword.get(primary_key, :autogenerate, false)
read_after_writes = !autogenerate
quote do
use Ecto.Schema
@primary_key {:id, unquote(primary_key_type),
autogenerate: unquote(autogenerate),
read_after_writes: unquote(read_after_writes)}
@foreign_key_type Ecto.UUID
@timestamps_opts [type: :utc_datetime_usec]
import Ecto.Changeset
import unquote(__MODULE__)
@type t :: %__MODULE__{}
def new(attrs) do
new(__MODULE__, attrs)
end
defoverridable new: 1
end
end
import Ecto.Changeset
def new(mod, attrs) do
mod.changeset(struct(mod), attrs)
end
def put_assoc_maybe(changeset, assoc, attrs, transform) do
case Map.fetch(attrs, assoc) do
:error ->
changeset
{:ok, nil} ->
put_assoc(changeset, assoc, nil)
{:ok, assoc_attrs} ->
case transform_assoc(assoc_attrs, transform) do
{:ok, transformed} ->
put_assoc(changeset, assoc, transformed)
{:error, %Ecto.Changeset{} = changeset} ->
add_error(changeset, assoc, get_error(changeset))
{:error, reason} ->
add_error(changeset, assoc, reason)
end
end
end
defp get_error(changeset) do
changeset
|> traverse_errors(fn {message, _opts} -> message end)
|> Enum.map(fn {k, v} -> "#{k} #{v}" end)
|> Enum.join(",")
end
defp transform_assoc(attrs, transform) when is_list(attrs) do
Enum.reduce(attrs, {:ok, []}, fn
next, {:ok, transformed} ->
case transform_assoc(next, transform) do
{:ok, value} ->
{:ok, transformed ++ [value]}
e ->
e
end
_, e ->
e
end)
end
defp transform_assoc(attrs, transform) do
transform.(attrs)
end
end