iex> alias Data.Post
iex> Post |> Post.by_status(:published) |> Repo.all()
iex> Post |> Post.by_status([:draft, :published]) |> Repo.all()
Last active
December 27, 2016 09:10
-
-
Save imranismail/1d854716b66502086e300f8a71f6aace to your computer and use it in GitHub Desktop.
Enum for Ecto
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| defmodule Enumecto do | |
| defmacro defenum(module, values) do | |
| values = atomify(values) | |
| quote bind_quoted: binding() do | |
| [_|type_parts] = | |
| __MODULE__ | |
| |> Module.concat(module) | |
| |> Module.split() | |
| type = | |
| type_parts | |
| |> Enum.map_join("_", &Macro.underscore/1) | |
| |> String.to_atom() | |
| defmodule Module.concat(__MODULE__, module) do | |
| @behaviour Ecto.Type | |
| @values values | |
| @string_lookup for value <- @values, into: %{}, do: {Atom.to_string(value), value} | |
| @atom_lookup for value <- @values, into: %{}, do: {value, Atom.to_string(value)} | |
| @valid_values @values ++ Map.keys(@string_lookup) | |
| def type, do: unquote(type) | |
| def cast(term) when is_binary(term), do: Map.fetch(@string_lookup, term) | |
| def cast(term) when term in @valid_values, do: {:ok, term} | |
| def cast(_), do: :error | |
| def load(term) when is_binary(term), do: Map.fetch(@string_lookup, term) | |
| def dump(term) when is_atom(term), do: Map.fetch(@atom_lookup, term) | |
| def dump(term) when term in @valid_values, do: {:ok, term} | |
| def dump(_), do: :error | |
| def values, do: @values | |
| def valid_values, do: @valid_values | |
| def create do | |
| Ecto.Migration.execute("CREATE TYPE #{type()} AS ENUM (#{escaped_values()})") | |
| end | |
| def drop do | |
| Ecto.Migration.execute("DROP TYPE #{type()}") | |
| end | |
| def alter do | |
| Enum.each(@values, fn value -> | |
| Ecto.Migration.execute("ALTER TYPE #{type()} ADD VALUE IF NOT EXISTS '#{value}'") | |
| end) | |
| end | |
| defp escaped_values do | |
| Enum.map_join(@values, ", ", &"'#{&1}'") | |
| end | |
| end | |
| end | |
| end | |
| defp atomify(values) do | |
| if Enum.all?(values, &is_atom/1) do | |
| values | |
| else | |
| Enum.map(values, &String.to_atom/1) | |
| end | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| defmodule Data.Migrations.CreatePostTable do | |
| use Ecto.Migration | |
| def up do | |
| Data.Post.Status.create | |
| create table(:posts) do | |
| add :title, :string | |
| add :body, :string | |
| add :status, Data.Post.Status.type | |
| timestamps() | |
| end | |
| end | |
| def down do | |
| drop table(:posts) | |
| Data.Post.Status.drop | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| defmodule Data.Post do | |
| use Ecto.Schema | |
| import Enumecto | |
| defenum Status, [ | |
| :draft, | |
| :unpublished, | |
| :published | |
| ] | |
| schema "posts" do | |
| field :status, Post.Status | |
| field :title, :string | |
| field :body, :string | |
| timestamps() | |
| end | |
| @allowed [ | |
| :status, | |
| :title, | |
| :body | |
| ] | |
| def changeset(schema, params \\ %{}) do | |
| schema | |
| |> cast(params, @allowed) | |
| end | |
| defdelegate statuses, to: Post.Status, as: :values | |
| def by_status(queryable, status) do | |
| from(queryable, where: ^has_status(status)) | |
| end | |
| defp has_status(status) do | |
| if is_list(status) do | |
| dynamic([q], q.status in ^status) | |
| else | |
| dynamic([q], q.status == ^status) | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment