Current section
Files
Jump to
Current section
Files
lib/ecto_postgres_enum.ex
defmodule EctoPostgresEnum do
@moduledoc false
defmacro __using__(opts) do
quote(bind_quoted: [opts: opts], unquote: false) do
default_type =
__MODULE__
|> Module.split()
|> List.last()
|> Macro.underscore()
|> String.to_atom()
schema = opts[:schema]
type = opts[:type] || default_type
is_atom(type) || raise "Type needs to be an atom"
values = opts[:values] || raise "Option values (list) is required"
values == Enum.uniq(values) || raise "Duplicates are not allowed in enum values"
Enum.count(values) > 0 || raise "Valid enums requires at least 1 different values"
Enum.all?(values, &is_atom/1) || raise "All values must be atoms"
schema && (is_atom(schema) || raise "Option schema must be atom")
alias Ecto.Migration
@behaviour Ecto.Type
@__input_values__ Enum.map(values, &Atom.to_string/1)
@__output_values__ values
@__schema__ schema
@__type__ if is_nil(@__schema__), do: type, else: :"#{@__schema__}.#{type}"
types = Enum.map_join(values, ", ", &"'#{&1}'")
@__create_sql__ "CREATE TYPE #{type} AS ENUM (#{types})"
@__drop_sql__ "DROP TYPE #{@__type__}"
list = Enum.zip(@__output_values__, @__input_values__)
[head | tail] = Enum.reverse(@__output_values__)
ast = Enum.reduce(tail, head, &{:|, [], [&1, &2]})
@type t :: unquote(ast)
@doc "Casts the given input to the custom type"
@spec cast(value) :: value when value: t()
@spec cast(String.t()) :: t() | :error
@spec cast(term) :: :error
for {atom, string} <- list do
def cast(unquote(atom)), do: {:ok, unquote(atom)}
def cast(unquote(string)), do: {:ok, unquote(atom)}
end
def cast(_term), do: :error
@doc "Creates database enum"
@spec create_db_enum :: :ok
def create_db_enum, do: Migration.execute(@__create_sql__)
@doc "Drops database enum"
@spec drop_db_enum :: :ok
def drop_db_enum, do: Migration.execute(@__drop_sql__)
@doc "Dumps the given term into an Ecto native type"
@spec dump(t()) :: {:ok, String.t()}
@spec dump(value) :: value | :error when value: String.t()
@spec dump(term) :: :error
for {atom, string} <- list do
def dump(unquote(atom)), do: {:ok, unquote(string)}
def dump(unquote(string)), do: {:ok, unquote(string)}
end
def dump(_term), do: :error
@doc "Loads the given term into a custom type"
@spec load(String.t()) :: {:ok, t()} | :error
@spec load(term) :: :error
for {atom, string} <- list do
def load(unquote(string)), do: {:ok, unquote(atom)}
end
def load(_value), do: :error
@doc """
Returns database enum type
"""
@spec type :: atom
def type, do: @__type__
@doc """
Returns list of values
"""
@spec values :: [term]
def values, do: @__output_values__
end
end
end