What is this about?
There are few functions that I end up adding to every single Elixir project I work on. They are not included in Ecto by default but I find them very useful.
fetch/2
and fetch_by/3
are similar to get/3
and get_by/3
but they return {:ok, record}
or {:error, :not_found}
which makes pattern matching easier.
count/2
is just a shortcut for aggregate/3
(I can never remember the syntax for aggregate/3
).
transact/1
is a wrapper around transaction/3
that allows you to use a with
statement to perform multiple operations with less code than when using Ecto.Multi
. This article by Tom Konidas explains the idea in more detail.
How to?
See the code below:
defmodule MyApp.Repo do
use Ecto.Repo,
otp_app: :myapp,
adapter: Ecto.Adapters.Postgres
@spec fetch(Ecto.Queryable.t()) :: {:ok, Ecto.Schema.t()} | {:error, :not_found}
def fetch(query), do: fetch(query, [])
@spec fetch(Ecto.Queryable.t(), keyword() | any()) ::
{:ok, Ecto.Schema.t()} | {:error, :not_found}
def fetch(query, opts) when is_keyword(opts) do
case one(query, opts) do
nil -> {:error, :not_found}
rec -> {:ok, rec}
end
end
def fetch(query, id), do: fetch(query, id, [])
@spec fetch(Ecto.Queryable.t(), any(), keyword()) ::
{:ok, Ecto.Schema.t()} | {:error, :not_found}
def fetch(queryable, id, opts \\ []) do
case get(queryable, id, opts) do
nil -> {:error, :not_found}
record -> {:ok, record}
end
end
@spec fetch_by(Ecto.Queryable.t(), keyword() | map(), keyword()) ::
{:ok, Ecto.Schema.t()} | {:error, :not_found}
def fetch_by(queryable, clauses, opts \\ []) do
case get_by(queryable, clauses, opts) do
nil -> {:error, :not_found}
record -> {:ok, record}
end
end
@spec count(Ecto.Queryable.t(), atom) :: term | nil
def count(queryable, key \\ :id), do: aggregate(queryable, :count, key)
@spec trasnsact((() -> any()), keyword()) :: {:ok, any()} | {:error, any()}
def transact(fun, opts \\ []) do
transaction(
fn ->
case fun.() do
{:ok, value} -> value
:ok -> :transaction_commited
{:error, reason} -> rollback(reason)
:error -> rollback(:transaction_rollback_error)
end
end,
opts
)
end
end