EctoResource
Eliminate boilerplate involved in defining basic CRUD functions in a Phoenix context or Elixir module.
Turn this:
defmodule MyApp.Accounts do
@moduledoc """
The Accounts context.
"""
import Ecto.Query, warn: false
alias MyApp.Repo
alias MyApp.Accounts.User
@doc """
Returns the list of users.
## Examples
iex> all_users()
[%User{}, ...]
"""
def all_users do
Repo.all(User)
end
@doc """
Gets a single user.
## Examples
iex> get_user(123)
{:ok, %User{}}
iex> get_user(456)
{:error, %Ecto.Changeset{}}
"""
def get_user(id), do: Repo.get(User, id)
@doc """
Gets a single user.
Raises [`Ecto.NoResultsError`](https://hexdocs.pm/ecto/3.1.7/Ecto.NoResultsError.html) if the User does not exist.
## Examples
iex> get_user!(123)
%User{}
iex> get_user!(456)
** (Ecto.NoResultsError)
"""
def get_user!(id), do: Repo.get!(User, id)
@doc """
Creates a user.
## Examples
iex> create_user(%{field: value})
{:ok, %User{}}
iex> create_user(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
@doc """
Creates a user.
Raises [`Postgrex.Error`](https://hexdocs.pm/postgrex/0.15.0/Postgrex.Error.html) if the insert fails.
## Examples
iex> create_user!(%{field: value})
{:ok, %User{}}
iex> create_user!(%{field: bad_value})
** (Postgrex.Error)
"""
def create_user!(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert!()
end
@doc """
Updates a user.
## Examples
iex> update_user(user, %{field: new_value})
{:ok, %User{}}
iex> update_user(user, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update()
end
@doc """
Updates a user.
Raises [`Postgrex.Error`](https://hexdocs.pm/postgrex/0.15.0/Postgrex.Error.html) if the update fails.
## Examples
iex> update_user!(user, %{field: new_value})
{:ok, %User{}}
iex> update_user!(user, %{field: bad_value})
** (Postgrex.Error)
"""
def update_user!(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update!()
end
@doc """
Deletes a User.
## Examples
iex> delete_user(user)
{:ok, %User{}}
iex> delete_user(user)
{:error, %Ecto.Changeset{}}
"""
def delete_user(%User{} = user) do
Repo.delete(user)
end
@doc """
Deletes a User.
Raises [`Postgrex.Error`](https://hexdocs.pm/postgrex/0.15.0/Postgrex.Error.html) if the delete fails.
## Examples
iex> delete_user!(user)
{:ok, %User{}}
iex> delete_user!(user)
** (Postgrex.Error)
"""
def delete_user!(%User{} = user) do
Repo.delete!(user)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking user changes.
## Examples
iex> change_user(user)
%Ecto.Changeset{source: %User{}}
"""
def change_user(%User{} = user) do
User.changeset(user, %{})
end
end
Into this:
defmodule MyApp.Accounts do
use EctoResource
alias MyApp.Repo
alias MyApp.Accounts.User
using_repo(Repo) do
resource(User)
end
end
Installation
If available in Hex, the package can be installed
by adding ecto_resource
to your list of dependencies in mix.exs
:
def deps do
[
{:ecto_resource, "~> 0.1.0"}
]
end
Basic Usage
All examples are using a "context" module Accounts
and a schema of User
. This can be substituted for your Phoenix context, or any other module and schema.
defmodule Accounts do
use EctoResource
using_repo(Repo) do
resource(User)
end
end
This will generate the functions:
def create_user(attributes) do
EctoResource.create(Repo, User, attributes)
end
def create_user!(attributes) do
EctoResource.create!(Repo, User, attributes)
end
def all_users(options) do
EctoResource.all(Repo, User, options)
end
def get_user(id, options) do
EctoResource.get(Repo, User, id, options)
end
def get_user!(id, options) do
EctoResource.get!(Repo, User, id, options)
end
def change_user(struct_or_changeset) do
EctoResource.change(User, struct_or_changeset)
end
def update_user(%User{id: 1}, changeset) do
EctoResource.update(Repo, User, struct, changeset)
end
def update_user!(%User{id: 1}, changeset) do
EctoResource.update!(Repo, User, struct, changeset)
end
def delete_user(struct_or_changeset) do
EctoResource.delete(Repo, struct_or_changeset)
end
def delete_user!(struct_or_changeset) do
EctoResource.delete!(Repo, struct_or_changeset)
end
There are also introspection functions to understand what is generated by the macro
Accounts.__resource__(:resources) == [
{Repo, User,
[
"all_users/1",
"change_user/1",
"create_user/1",
"create_user!/1",
"delete_user/1",
"delete_user!/1",
"get_user/2",
"get_user!/2",
"update_user/2",
"update_user!/2"
]}
]
Advanced usage
More granular control is available through options
:read
defmodule Accounts do
use EctoResource
using_repo(Repo) do
resource(User, :read)
end
end
This will generate the functions:
def all_users(options) do
EctoResource.all(Repo, User, options)
end
def get_user(id, options) do
EctoResource.get(Repo, User, id, options)
end
def get_user!(id, options) do
EctoResource.get!(Repo, User, id, options)
end
There are also introspection functions to understand what is generated by the macro
Accounts.__resource__(:resources) == [
{Repo, User,
[
"all_users/1",
"get_user/2",
"get_user!/2"
]}
]
:write
defmodule Accounts do
use EctoResource
using_repo(Repo) do
resource(User, :write)
end
end
This will generate the functions:
def create_user(attributes) do
EctoResource.create(Repo, User, attributes)
end
def create_user!(attributes) do
EctoResource.create!(Repo, User, attributes)
end
def change_user(struct_or_changeset) do
EctoResource.change(User, struct_or_changeset)
end
def update_user(%User{id: 1}, changeset) do
EctoResource.update(Repo, User, struct, changeset)
end
def update_user!(%User{id: 1}, changeset) do
EctoResource.update!(Repo, User, struct, changeset)
end
There are also introspection functions to understand what is generated by the macro
Accounts.__resource__(:resources) == [
{Repo, User,
[
"change_user/1",
"create_user/1",
"create_user!/1",
"update_user/2",
"update_user!/2"
]}
]
:delete
defmodule Accounts do
use EctoResource
using_repo(Repo) do
resource(User, :delete)
end
end
This will generate the functions:
def delete_user(struct_or_changeset) do
EctoResource.delete(Repo, struct_or_changeset)
end
def delete_user!(struct_or_changeset) do
EctoResource.delete!(Repo, struct_or_changeset)
end
There are also introspection functions to understand what is generated by the macro
Accounts.__resource__(:resources) == [
{Repo, User,
[
"delete_user/1",
"delete_user!/1"
]}
]
:only
defmodule Accounts do
use EctoResource
using_repo(Repo) do
resource(User, only: [:change])
end
This will generate the functions:
def change_user(struct_or_changeset) do
EctoResource.change(User, struct_or_changeset)
end
There are also introspection functions to understand what is generated by the macro
Accounts.__resource__(:resources) == [
{Repo, User,
[
"change_user/1"
]}
]
:except
defmodule Accounts do
use EctoResource
using_repo(Repo) do
resource(User, except: [:change])
end
This will generate the functions:
def create_user(attributes) do
EctoResource.create(Repo, User, attributes)
end
def create_user!(attributes) do
EctoResource.create!(Repo, User, attributes)
end
def all_users(options) do
EctoResource.all(Repo, User, options)
end
def get_user(id, options) do
EctoResource.get(Repo, User, id, options)
end
def get_user!(id, options) do
EctoResource.get!(Repo, User, id, options)
end
def update_user(%User{id: 1}, changeset) do
EctoResource.update(Repo, User, struct, changeset)
end
def update_user!(%User{id: 1}, changeset) do
EctoResource.update!(Repo, User, struct, changeset)
end
def delete_user(struct_or_changeset) do
EctoResource.delete(Repo, struct_or_changeset)
end
def delete_user!(struct_or_changeset) do
EctoResource.delete!(Repo, struct_or_changeset)
end
There are also introspection functions to understand what is generated by the macro
Accounts.__resource__(:resources) == [
{Repo, User,
[
"all_users/1",
"create_user/1",
"create_user!/1",
"delete_user/1",
"delete_user!/1",
"get_user/2",
"get_user!/2",
"update_user/2",
"update_user!/2"
]}
]
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ecto_resource.