add types to ecto changeset specs

This commit is contained in:
shibao 2022-01-31 23:45:00 -05:00
parent 9ca69ae3e4
commit 8f130a7598
13 changed files with 74 additions and 58 deletions

View File

@ -9,7 +9,7 @@ contributing to Cannery (hopefully) as great of an experience as you found it!
inline `do:` blocks for short functions and make your aliases as short as
possible without introducing ambiguity.
- I.e. since there's only one `Changeset` in the app, please alias
`Changeset.t()` instead of using `Ecto.Changeset.t()`
`Changeset.t(Type.t())` instead of using `Ecto.Changeset.t(Long.Type.t())`
- Use pipelines when possible. If a function only calls a single method, a
pipeline isn't strictly necessary but still encouraged for future
modification.

View File

@ -89,7 +89,7 @@ defmodule Cannery.Accounts do
{:error, %Changeset{}}
"""
@spec register_user(map()) :: {:ok, User.t()} | {:error, Changeset.t()}
@spec register_user(map()) :: {:ok, User.t()} | {:error, Changeset.t(User.new_user())}
def register_user(attrs) do
# if no registered users, make first user an admin
attrs =
@ -109,8 +109,10 @@ defmodule Cannery.Accounts do
%Changeset{data: %User{}}
"""
@spec change_user_registration(User.t() | User.new_user()) :: Changeset.t()
@spec change_user_registration(User.t() | User.new_user(), map()) :: Changeset.t()
@spec change_user_registration(User.t() | User.new_user()) ::
Changeset.t(User.t() | User.new_user())
@spec change_user_registration(User.t() | User.new_user(), map()) ::
Changeset.t(User.t() | User.new_user())
def change_user_registration(user, attrs \\ %{}),
do: User.registration_changeset(user, attrs, hash_password: false)
@ -125,7 +127,7 @@ defmodule Cannery.Accounts do
%Changeset{data: %User{}}
"""
@spec change_user_email(User.t(), map()) :: Changeset.t()
@spec change_user_email(User.t(), map()) :: Changeset.t(User.t())
def change_user_email(user, attrs \\ %{}), do: User.email_changeset(user, attrs)
@doc """
@ -137,7 +139,7 @@ defmodule Cannery.Accounts do
%Changeset{data: %User{}}
"""
@spec change_user_role(User.t(), atom()) :: Changeset.t()
@spec change_user_role(User.t(), atom()) :: Changeset.t(User.t())
def change_user_role(user, role), do: User.role_changeset(user, role)
@doc """
@ -154,7 +156,7 @@ defmodule Cannery.Accounts do
"""
@spec apply_user_email(User.t(), String.t(), map()) ::
{:ok, User.t()} | {:error, Changeset.t()}
{:ok, User.t()} | {:error, Changeset.t(User.t())}
def apply_user_email(user, password, attrs) do
user
|> User.email_changeset(attrs)
@ -218,7 +220,7 @@ defmodule Cannery.Accounts do
%Changeset{data: %User{}}
"""
@spec change_user_password(User.t(), map()) :: Changeset.t()
@spec change_user_password(User.t(), map()) :: Changeset.t(User.t())
def change_user_password(user, attrs \\ %{}),
do: User.password_changeset(user, attrs, hash_password: false)
@ -235,7 +237,7 @@ defmodule Cannery.Accounts do
"""
@spec update_user_password(User.t(), String.t(), map()) ::
{:ok, User.t()} | {:error, Changeset.t()}
{:ok, User.t()} | {:error, Changeset.t(User.t())}
def update_user_password(user, password, attrs) do
changeset =
user
@ -399,7 +401,7 @@ defmodule Cannery.Accounts do
{:error, %Changeset{}}
"""
@spec reset_user_password(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
@spec reset_user_password(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t(User.t())}
def reset_user_password(user, attrs) do
Multi.new()
|> Multi.update(:user, User.password_changeset(user, attrs))

View File

@ -54,9 +54,9 @@ defmodule Cannery.Accounts.User do
validations on a LiveView form), this option can be set to `false`.
Defaults to `true`.
"""
@spec registration_changeset(t() | new_user(), attrs :: map()) :: Changeset.t()
@spec registration_changeset(t() | new_user(), attrs :: map()) :: Changeset.t(t() | new_user())
@spec registration_changeset(t() | new_user(), attrs :: map(), opts :: keyword()) ::
Changeset.t()
Changeset.t(t() | new_user())
def registration_changeset(user, attrs, opts \\ []) do
user
|> cast(attrs, [:email, :password, :role])
@ -68,12 +68,12 @@ defmodule Cannery.Accounts.User do
A user changeset for role.
"""
@spec role_changeset(t(), role :: atom()) :: Changeset.t()
@spec role_changeset(t(), role :: atom()) :: Changeset.t(t())
def role_changeset(user, role) do
user |> cast(%{"role" => role}, [:role])
end
@spec validate_email(Changeset.t()) :: Changeset.t()
@spec validate_email(Changeset.t(t() | new_user())) :: Changeset.t(t() | new_user())
defp validate_email(changeset) do
changeset
|> validate_required([:email])
@ -83,7 +83,8 @@ defmodule Cannery.Accounts.User do
|> unique_constraint(:email)
end
@spec validate_password(Changeset.t(), opts :: keyword()) :: Changeset.t()
@spec validate_password(Changeset.t(t() | new_user()), opts :: keyword()) ::
Changeset.t(t() | new_user())
defp validate_password(changeset, opts) do
changeset
|> validate_required([:password])
@ -94,7 +95,8 @@ defmodule Cannery.Accounts.User do
|> maybe_hash_password(opts)
end
@spec maybe_hash_password(Changeset.t(), opts :: keyword()) :: Changeset.t()
@spec maybe_hash_password(Changeset.t(t() | new_user()), opts :: keyword()) ::
Changeset.t(t() | new_user())
defp maybe_hash_password(changeset, opts) do
hash_password? = Keyword.get(opts, :hash_password, true)
password = get_change(changeset, :password)
@ -113,7 +115,7 @@ defmodule Cannery.Accounts.User do
It requires the email to change otherwise an error is added.
"""
@spec email_changeset(t(), attrs :: map()) :: Changeset.t()
@spec email_changeset(t(), attrs :: map()) :: Changeset.t(t())
def email_changeset(user, attrs) do
user
|> cast(attrs, [:email])
@ -136,8 +138,8 @@ defmodule Cannery.Accounts.User do
validations on a LiveView form), this option can be set to `false`.
Defaults to `true`.
"""
@spec password_changeset(t(), attrs :: map()) :: Changeset.t()
@spec password_changeset(t(), attrs :: map(), opts :: keyword()) :: Changeset.t()
@spec password_changeset(t(), attrs :: map()) :: Changeset.t(t())
@spec password_changeset(t(), attrs :: map(), opts :: keyword()) :: Changeset.t(t())
def password_changeset(user, attrs, opts \\ []) do
user
|> cast(attrs, [:password])
@ -148,7 +150,7 @@ defmodule Cannery.Accounts.User do
@doc """
Confirms the account by setting `confirmed_at`.
"""
@spec confirm_changeset(t() | Changeset.t()) :: Changeset.t()
@spec confirm_changeset(t() | Changeset.t(t())) :: Changeset.t(t())
def confirm_changeset(user_or_changeset) do
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
user_or_changeset |> change(confirmed_at: now)
@ -174,7 +176,7 @@ defmodule Cannery.Accounts.User do
@doc """
Validates the current password otherwise adds an error to the changeset.
"""
@spec validate_current_password(Changeset.t(), String.t()) :: Changeset.t()
@spec validate_current_password(Changeset.t(t()), String.t()) :: Changeset.t(t())
def validate_current_password(changeset, password) do
if valid_password?(changeset.data, password),
do: changeset,

View File

@ -49,7 +49,8 @@ defmodule Cannery.Ammo do
{:error, %Ecto.Changeset{}}
"""
@spec create_ammo_type(attrs :: map()) :: {:ok, AmmoType.t()} | {:error, Changeset.t()}
@spec create_ammo_type(attrs :: map()) ::
{:ok, AmmoType.t()} | {:error, Changeset.t(AmmoType.new_ammo_type())}
def create_ammo_type(attrs \\ %{}),
do: %AmmoType{} |> AmmoType.changeset(attrs) |> Repo.insert()
@ -66,7 +67,7 @@ defmodule Cannery.Ammo do
"""
@spec update_ammo_type(AmmoType.t(), attrs :: map()) ::
{:ok, AmmoType.t()} | {:error, Changeset.t()}
{:ok, AmmoType.t()} | {:error, Changeset.t(AmmoType.t())}
def update_ammo_type(%AmmoType{} = ammo_type, attrs),
do: ammo_type |> AmmoType.changeset(attrs) |> Repo.update()
@ -82,7 +83,8 @@ defmodule Cannery.Ammo do
{:error, %Ecto.Changeset{}}
"""
@spec delete_ammo_type(AmmoType.t()) :: {:ok, AmmoType.t()} | {:error, Changeset.t()}
@spec delete_ammo_type(AmmoType.t()) ::
{:ok, AmmoType.t()} | {:error, Changeset.t(AmmoType.t())}
def delete_ammo_type(%AmmoType{} = ammo_type), do: ammo_type |> Repo.delete()
@doc """
@ -106,8 +108,10 @@ defmodule Cannery.Ammo do
%Ecto.Changeset{data: %AmmoType{}}
"""
@spec change_ammo_type(AmmoType.t() | AmmoType.new_ammo_type()) :: Changeset.t()
@spec change_ammo_type(AmmoType.t() | AmmoType.new_ammo_type(), attrs :: map()) :: Changeset.t()
@spec change_ammo_type(AmmoType.t() | AmmoType.new_ammo_type()) ::
Changeset.t(AmmoType.t() | AmmoType.new_ammo_type())
@spec change_ammo_type(AmmoType.t() | AmmoType.new_ammo_type(), attrs :: map()) ::
Changeset.t(AmmoType.t() | AmmoType.new_ammo_type())
def change_ammo_type(%AmmoType{} = ammo_type, attrs \\ %{}),
do: AmmoType.changeset(ammo_type, attrs)
@ -159,7 +163,8 @@ defmodule Cannery.Ammo do
{:error, %Ecto.Changeset{}}
"""
@spec create_ammo_group(attrs :: map()) :: {:ok, AmmoGroup.t()} | {:error, Changeset.t()}
@spec create_ammo_group(attrs :: map()) ::
{:ok, AmmoGroup.t()} | {:error, Changeset.t(AmmoGroup.new_ammo_group())}
def create_ammo_group(attrs \\ %{}),
do: %AmmoGroup{} |> AmmoGroup.changeset(attrs) |> Repo.insert()
@ -176,7 +181,7 @@ defmodule Cannery.Ammo do
"""
@spec update_ammo_group(AmmoGroup.t(), attrs :: map()) ::
{:ok, AmmoGroup.t()} | {:error, Changeset.t()}
{:ok, AmmoGroup.t()} | {:error, Changeset.t(AmmoGroup.t())}
def update_ammo_group(%AmmoGroup{} = ammo_group, attrs),
do: ammo_group |> AmmoGroup.changeset(attrs) |> Repo.update()
@ -192,7 +197,8 @@ defmodule Cannery.Ammo do
{:error, %Ecto.Changeset{}}
"""
@spec delete_ammo_group(AmmoGroup.t()) :: {:ok, AmmoGroup.t()} | {:error, Changeset.t()}
@spec delete_ammo_group(AmmoGroup.t()) ::
{:ok, AmmoGroup.t()} | {:error, Changeset.t(AmmoGroup.t())}
def delete_ammo_group(%AmmoGroup{} = ammo_group), do: ammo_group |> Repo.delete()
@doc """

View File

@ -47,7 +47,7 @@ defmodule Cannery.Ammo.AmmoGroup do
@type id :: UUID.t()
@doc false
@spec changeset(t() | new_ammo_group(), attrs :: map()) :: Changeset.t()
@spec changeset(t() | new_ammo_group(), attrs :: map()) :: Changeset.t(t() | new_ammo_group())
def changeset(ammo_group, attrs) do
ammo_group
|> cast(attrs, [:count, :price_paid, :notes, :tag_id, :ammo_type_id, :container_id, :user_id])

View File

@ -38,7 +38,7 @@ defmodule Cannery.Ammo.AmmoType do
@type id :: UUID.t()
@doc false
@spec changeset(t() | new_ammo_type(), attrs :: map()) :: Changeset.t()
@spec changeset(t() | new_ammo_type(), attrs :: map()) :: Changeset.t(t() | new_ammo_type())
def changeset(ammo_type, attrs) do
ammo_type
|> cast(attrs, [:name, :desc, :case_material, :bullet_type, :grain, :manufacturer])

View File

@ -50,7 +50,8 @@ defmodule Cannery.Containers do
{:error, %Ecto.Changeset{}}
"""
@spec create_container(attrs :: map()) :: {:ok, Container.t()} | {:error, Changeset.t()}
@spec create_container(attrs :: map()) ::
{:ok, Container.t()} | {:error, Changeset.t(Container.new_container())}
def create_container(attrs) do
%Container{} |> Container.changeset(attrs) |> Repo.insert()
end
@ -67,8 +68,8 @@ defmodule Cannery.Containers do
{:error, %Ecto.Changeset{}}
"""
@spec update_container(Container.t() | Ecto.Changeset.t(), attrs :: map()) ::
{:ok, Container.t()} | {:error, Ecto.Changeset.t()}
@spec update_container(Container.t(), attrs :: map()) ::
{:ok, Container.t()} | {:error, Changeset.t(Container.t())}
def update_container(container, attrs) do
container |> Container.changeset(attrs) |> Repo.update()
end
@ -85,8 +86,8 @@ defmodule Cannery.Containers do
{:error, %Ecto.Changeset{}}
"""
@spec delete_container(Container.t() | Ecto.Changeset.t()) ::
{:ok, Container.t()} | {:error, Ecto.Changeset.t()}
@spec delete_container(Container.t()) ::
{:ok, Container.t()} | {:error, Changeset.t(Container.t())}
def delete_container(container), do: container |> Repo.delete()
@doc """
@ -98,7 +99,7 @@ defmodule Cannery.Containers do
%Container{}
"""
@spec delete_container!(Container.t() | Ecto.Changeset.t()) :: Container.t()
@spec delete_container!(Container.t()) :: Container.t()
def delete_container!(container), do: container |> Repo.delete!()
@doc """
@ -113,9 +114,10 @@ defmodule Cannery.Containers do
%Ecto.Changeset{data: %Container{}}
"""
@spec change_container(Container.t() | Container.new_container()) :: Changeset.t()
@spec change_container(Container.t() | Container.new_container()) ::
Changeset.t(Container.t() | Container.new_container())
@spec change_container(Container.t() | Container.new_container(), attrs :: map()) ::
Changeset.t()
Changeset.t(Container.t() | Container.new_container())
def change_container(container, attrs \\ %{}), do: container |> Container.changeset(attrs)
@doc """

View File

@ -36,7 +36,7 @@ defmodule Cannery.Containers.Container do
@type id :: UUID.t()
@doc false
@spec changeset(t() | new_container(), attrs :: map()) :: Changeset.t()
@spec changeset(t() | new_container(), attrs :: map()) :: Changeset.t(t() | new_container())
def changeset(container, attrs) do
container
|> cast(attrs, [:name, :desc, :type, :location, :user_id])

View File

@ -31,7 +31,8 @@ defmodule Cannery.Containers.ContainerTag do
@type id :: UUID.t()
@doc false
@spec changeset(t() | new_container_tag(), attrs :: map()) :: Changeset.t()
@spec changeset(t() | new_container_tag(), attrs :: map()) ::
Changeset.t(t() | new_container_tag())
def changeset(container_tag, attrs) do
container_tag
|> cast(attrs, [:tag_id, :container_id])

View File

@ -93,11 +93,11 @@ defmodule Cannery.Invites do
{:ok, %Invite{}}
iex> create_invite(%User{id: "1"}, %{field: bad_value})
{:error, %Ecto.Changeset{}}
{:error, %Changeset{}}
"""
@spec create_invite(User.t() | User.id(), attrs :: map()) ::
{:ok, Invite.t()} | {:error, Changeset.t()}
{:ok, Invite.t()} | {:error, Changeset.t(Invite.new_invite())}
def create_invite(%{id: user_id}, attrs), do: create_invite(user_id, attrs)
def create_invite(user_id, attrs) when not (user_id |> is_nil()) do
@ -120,11 +120,11 @@ defmodule Cannery.Invites do
{:ok, %Invite{}}
iex> update_invite(invite, %{field: bad_value})
{:error, %Ecto.Changeset{}}
{:error, %Changeset{}}
"""
@spec update_invite(Invite.t(), attrs :: map()) ::
{:ok, Invite.t()} | {:error, Ecto.Changeset.t()}
{:ok, Invite.t()} | {:error, Changeset.t(Invite.t())}
def update_invite(invite, attrs), do: invite |> Invite.changeset(attrs) |> Repo.update()
@doc """
@ -136,10 +136,10 @@ defmodule Cannery.Invites do
{:ok, %Invite{}}
iex> delete_invite(invite)
{:error, %Ecto.Changeset{}}
{:error, %Changeset{}}
"""
@spec delete_invite(Invite.t()) :: {:ok, Invite.t()} | {:error, Ecto.Changeset.t()}
@spec delete_invite(Invite.t()) :: {:ok, Invite.t()} | {:error, Changeset.t(Invite.t())}
def delete_invite(invite), do: invite |> Repo.delete()
@doc """
@ -155,15 +155,17 @@ defmodule Cannery.Invites do
def delete_invite!(invite), do: invite |> Repo.delete!()
@doc """
Returns an `%Ecto.Changeset{}` for tracking invite changes.
Returns an `%Changeset{}` for tracking invite changes.
## Examples
iex> change_invite(invite)
%Ecto.Changeset{data: %Invite{}}
%Changeset{data: %Invite{}}
"""
@spec change_invite(Invite.t() | Invite.new_invite()) :: Ecto.Changeset.t()
@spec change_invite(Invite.t() | Invite.new_invite(), attrs :: map()) :: Ecto.Changeset.t()
@spec change_invite(Invite.t() | Invite.new_invite()) ::
Changeset.t(Invite.t() | Invite.new_invite())
@spec change_invite(Invite.t() | Invite.new_invite(), attrs :: map()) ::
Changeset.t(Invite.t() | Invite.new_invite())
def change_invite(invite, attrs \\ %{}), do: invite |> Invite.changeset(attrs)
end

View File

@ -38,7 +38,7 @@ defmodule Cannery.Invites.Invite do
@type id :: UUID.t()
@doc false
@spec changeset(t() | new_invite(), attrs :: map()) :: Changeset.t()
@spec changeset(t() | new_invite(), attrs :: map()) :: Changeset.t(t() | new_invite())
def changeset(invite, attrs) do
invite
|> cast(attrs, [:name, :token, :uses_left, :disabled_at, :user_id])

View File

@ -49,7 +49,7 @@ defmodule Cannery.Tags do
{:error, %Changeset{}}
"""
@spec create_tag(attrs :: map()) :: {:ok, Tag.t()} | {:error, Changeset.t()}
@spec create_tag(attrs :: map()) :: {:ok, Tag.t()} | {:error, Changeset.t(Tag.new_tag())}
def create_tag(attrs), do: %Tag{} |> Tag.changeset(attrs) |> Repo.insert()
@doc """
@ -64,7 +64,7 @@ defmodule Cannery.Tags do
{:error, %Changeset{}}
"""
@spec update_tag(Tag.t(), attrs :: map()) :: {:ok, Tag.t()} | {:error, Changeset.t()}
@spec update_tag(Tag.t(), attrs :: map()) :: {:ok, Tag.t()} | {:error, Changeset.t(Tag.t())}
def update_tag(tag, attrs), do: tag |> Tag.changeset(attrs) |> Repo.update()
@doc """
@ -79,7 +79,7 @@ defmodule Cannery.Tags do
{:error, %Changeset{}}
"""
@spec delete_tag(Tag.t()) :: {:ok, Tag.t()} | {:error, Changeset.t()}
@spec delete_tag(Tag.t()) :: {:ok, Tag.t()} | {:error, Changeset.t(Tag.t())}
def delete_tag(tag), do: tag |> Repo.delete()
@doc """
@ -103,8 +103,9 @@ defmodule Cannery.Tags do
%Changeset{data: %Tag{}}
"""
@spec change_tag(Tag.t() | Tag.new_tag()) :: Changeset.t()
@spec change_tag(Tag.t() | Tag.new_tag(), attrs :: map()) :: Changeset.t()
@spec change_tag(Tag.t() | Tag.new_tag()) :: Changeset.t(Tag.t() | Tag.new_tag())
@spec change_tag(Tag.t() | Tag.new_tag(), attrs :: map()) ::
Changeset.t(Tag.t() | Tag.new_tag())
def change_tag(tag, attrs \\ %{}), do: Tag.changeset(tag, attrs)
@doc """

View File

@ -35,7 +35,7 @@ defmodule Cannery.Tags.Tag do
@type id() :: UUID.t()
@doc false
@spec changeset(t() | new_tag(), attrs :: map()) :: Changeset.t()
@spec changeset(t() | new_tag(), attrs :: map()) :: Changeset.t(t() | new_tag())
def changeset(tag, attrs) do
tag
|> cast(attrs, [:name, :bg_color, :text_color, :user_id])