ok, that was stupid. Works now. Very nice.
defmodule EctoAtomId do
@callback dummy() :: any()
@optional_callbacks dummy: 0
defmacro __using__(opts) do
quote location: :keep, bind_quoted: [opts: opts] do
@behaviour EctoAtomId
use Ecto.Type
@ids Keyword.fetch!(opts, :ids)
@ids_as_string for(id <- @ids, do: Atom.to_string(id))
@type_name Keyword.fetch!(opts, :type_name)
def type, do: @type_name
def cast(data) when data in @ids_as_string, do: {:ok, String.to_existing_atom(data)}
def cast(data) when data in @ids, do: {:ok, data}
def cast(), do: :error
def load(data) when data in @ids_as_string, do: {:ok, String.to_existing_atom(data)}
def load(_), do: :error
def dump(id), do: {:ok, Atom.to_string(id)}
end
end
end
Example for using these types:
defmodule Person do
use Ecto.Schema
@primary_key false
embedded_schema do
field(:id, :integer)
field(:name, :string, null: false)
field(:age, :integer)
field(:job, Job)
field(:hobbies, {:array, Hobby})
field(:friends, {:array, :integer})
end
end
iex(2)> data = %{id: 1, name: "Bob", age: "18", job: "job_programmer", friends: [2, 4711], hobbies: ["hobby_freeclimbing", "hobby_painting"]}
...
iex(3)> p = Ecto.Changeset.cast(%Person{}, data, Map.keys(data)) |> Ecto.Changeset.apply_changes()
%Person{
age: 18,
friends: [2, 4711],
hobbies: [:hobby_freeclimbing, :hobby_painting],
id: 1,
job: :job_programmer,
name: "Bob"
}