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]) do
schema = opts[:schema] || :public
type = opts[:type] || raise "Option type (atom) is required"
values = opts[:values] || raise "Option values (list) is required"
values == Enum.uniq(values) || raise "Duplicates are not allowed in enum values"
Enum.count(values) >= 2 || raise "Valid enums requires at least 2 different values"
Enum.all?(values, &is_atom/1) || raise "All values must be atoms"
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}"
@__valid_values__ @__input_values__ ++ @__output_values__
types = Enum.map_join(values, ", ", &"'#{&1}'")
@__create_sql__ "CREATE TYPE #{type} AS ENUM (#{types})"
@__drop_sql__ "DROP TYPE #{@__type__}"
@doc """
Check if specifed argument is valid element of enum.
"""
@spec valid_value?(term) :: boolean
defmacro valid_value?(value) do
quote do
unquote(value) in unquote(@__valid_values__)
end
end
@spec cast(value) :: value when value: atom
def cast(atom) when is_atom(atom) and valid_value?(atom), do: {:ok, atom}
@spec cast(String.t()) :: atom
def cast(binary) when is_binary(binary) and valid_value?(binary),
do: {:ok, String.to_existing_atom(binary)}
@spec cast(term) :: :error
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__)
@spec dump(atom) :: {:ok, String.t()}
def dump(atom) when is_atom(atom) and valid_value?(atom), do: {:ok, Atom.to_string(atom)}
@spec dump(value) :: value when value: String.t()
def dump(string) when is_binary(string) and valid_value?(string), do: {:ok, string}
@spec dump(term) :: :error
def dump(_term), do: :error
@spec load(value) :: {:ok, value} when value: atom
def load(value) when is_binary(value) and valid_value?(value), do: {:ok, value}
@spec load(term) :: :error
def load(_value), do: :error
@doc """
Returns database enum type
"""
@spec type :: atom
def type, do: @__type__
end
end
end