README.md

# EctoTaggedUnion

**TODO: Add description**

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `ecto_tagged_union` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:ecto_tagged_union, "~> 0.1.0"}
  ]
end
```

## Usage

First, define variant schemas:

```elixir
defmodule Shape.Circle do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key false
  embedded_schema do
    field :radius, :integer
  end

  @fields [:radius]
  def changeset(data, attrs) do
    data
    |> cast(attrs, @fields)
  end
end

defmodule Shape.Rectangle do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key false
  embedded_schema do
    field :height, :integer
    field :width, :integer
  end

  @fields [:height, :width]
  def changeset(data, attrs) do
    data
    |> cast(attrs, @fields)
  end
end
```

### Internally tagged

By default, tag is stored next to other fields of the variant.

```elixir
defmodule Shape do
  import EctoTaggedUnion
  alias Shape.Circle
  alias Shape.Rectangle

  defunion Circle | Rectangle
end

iex> Shape.dump(%Shape.Circle{radius: 10})
%{radius: 10, tag: "Circle"}

iex> Shape.load(%{"radius" => 10, "tag" => "Circle"})
%Shape.Circle{radius: 10}

iex> Shape.cast(%{radius: 10, tag: "Circle"})
%Shape.Circle{radius: 10}
```

### Externally tagged

```elixir
defmodule Shape do
  import EctoTaggedUnion
  alias Shape.Circle
  alias Shape.Rectangle

  defunion Circle | Rectangle, :external
end

iex> Shape.dump(%Shape.Circle{radius: 10})
%{"Circle" => %{radius: 10}}

iex> Shape.load(%{"Circle" => %{"radius" => 10}})
%Shape.Circle{radius: 10}

iex> Shape.cast(%{"Circle" => %{radius: 10}})
%Shape.Circle{radius: 10}
```

### Adjacently tagged

The tag and the content are adjacent to each other as two fields within the same map.

```elixir
defmodule Shape do
  import EctoTaggedUnion
  alias Shape.Circle
  alias Shape.Rectangle

  defunion Circle | Rectangle, :adjacent, tag: :t, content: :c
end

iex> Shape.dump(%Shape.Circle{radius: 10})
%{c: %{radius: 10}, t: "Circle"}

iex> Shape.load(%{"c" => %{"radius" => 10}, "t" => "Circle"})
%Shape.Circle{radius: 10}

iex> Shape.cast(%{t: "Circle", c: %{radius: 10}})
%Shape.Circle{radius: 10}
```