From 76d594ca0ff86141043316144f4cc012879437c5 Mon Sep 17 00:00:00 2001 From: shibao Date: Fri, 11 Feb 2022 00:33:51 -0500 Subject: [PATCH] improve containers --- lib/cannery/containers.ex | 92 +++++++++---------- .../live/container_live/form_component.ex | 77 +++++++++------- lib/cannery_web/live/container_live/index.ex | 48 ++++------ lib/cannery_web/live/container_live/show.ex | 41 +++++---- 4 files changed, 130 insertions(+), 128 deletions(-) diff --git a/lib/cannery/containers.ex b/lib/cannery/containers.ex index 1e45e9b..ae22543 100644 --- a/lib/cannery/containers.ex +++ b/lib/cannery/containers.ex @@ -14,13 +14,13 @@ defmodule Cannery.Containers do ## Examples - iex> list_containers() + iex> list_containers(%User{id: 123}) [%Container{}, ...] """ - @spec list_containers(user_or_user_id :: User.t() | User.id()) :: [Container.t()] - def list_containers(%{id: user_id}), do: list_containers(user_id) - def list_containers(user_id), do: Repo.all(from c in Container, where: c.user_id == ^user_id) + @spec list_containers(User.t()) :: [Container.t()] + def list_containers(%User{id: user_id}), + do: Repo.all(from c in Container, where: c.user_id == ^user_id) @doc """ Gets a single container. @@ -29,25 +29,26 @@ defmodule Cannery.Containers do ## Examples - iex> get_container!(123) + iex> get_container!(123, %User{id: 123}) %Container{} - iex> get_container!(456) + iex> get_container!(456, %User{id: 123}) ** (Ecto.NoResultsError) """ - @spec get_container!(Container.id()) :: Container.t() - def get_container!(id), do: Repo.get!(Container, id) + @spec get_container!(Container.id(), User.t()) :: Container.t() + def get_container!(id, %User{id: user_id}), + do: Repo.one!(from c in Container, where: c.id == ^id and c.user_id == ^user_id) @doc """ Creates a container. ## Examples - iex> create_container(%{field: value}, user) + iex> create_container(%{field: value}, %User{id: 123}) {:ok, %Container{}} - iex> create_container(%{field: bad_value}, user) + iex> create_container(%{field: bad_value}, %User{id: 123}) {:error, %Changeset{}} """ @@ -63,10 +64,10 @@ defmodule Cannery.Containers do ## Examples - iex> update_container(container, user, %{field: new_value}) + iex> update_container(container, %User{id: 123}, %{field: new_value}) {:ok, %Container{}} - iex> update_container(container, user, %{field: bad_value}) + iex> update_container(container, %User{id: 123}, %{field: bad_value}) {:error, %Changeset{}} """ @@ -81,10 +82,10 @@ defmodule Cannery.Containers do ## Examples - iex> delete_container(container, user) + iex> delete_container(container, %User{id: 123}) {:ok, %Container{}} - iex> delete_container(container, user) + iex> delete_container(container, %User{id: 123}) {:error, %Changeset{}} """ @@ -100,23 +101,12 @@ defmodule Cannery.Containers do 0 -> container |> Repo.delete() - amount -> - error_string = - dngettext( - "errors", - "There is still %{amount} ammo group in this container", - "There are still %{amount} ammo groups in this container", - amount - ) + _amount -> + error = dgettext("errors", "Container must be empty before deleting") container |> change_container() - |> Changeset.add_error( - :ammo_groups, - error_string, - amount: amount, - count: amount - ) + |> Changeset.add_error(:ammo_groups, error) |> Changeset.apply_action(:delete) end end @@ -126,7 +116,7 @@ defmodule Cannery.Containers do ## Examples - iex> delete_container(container, user) + iex> delete_container(container, %User{id: 123}) %Container{} """ @@ -160,18 +150,16 @@ defmodule Cannery.Containers do ## Examples - iex> add_tag!(container, tag) + iex> add_tag!(container, tag, %User{id: 123}) %Container{} - iex> add_tag!(container_id, tag_id) - %Container{} """ - @spec add_tag!(Container.t(), Tag.t()) :: Container.t() - def add_tag!(%{id: container_id}, %{id: tag_id}), do: add_tag!(container_id, tag_id) - - @spec add_tag!(Container.id(), Tag.id()) :: Container.t() - def add_tag!(container_id, tag_id) - when not (container_id |> is_nil()) and not (tag_id |> is_nil()) do + @spec add_tag!(Container.t(), Tag.t(), User.t()) :: ContainerTag.t() + def add_tag!( + %Container{id: container_id, user_id: user_id}, + %Tag{id: tag_id, user_id: user_id}, + %User{id: user_id} + ) do %ContainerTag{} |> ContainerTag.changeset(%{"container_id" => container_id, "tag_id" => tag_id}) |> Repo.insert!() @@ -182,22 +170,24 @@ defmodule Cannery.Containers do ## Examples - iex> remove_tag!(container, tag) + iex> remove_tag!(container, tag, %User{id: 123}) %Container{} - iex> remove_tag!(container_id, tag_id) - %Container{} """ - @spec remove_tag!(Container.t(), Tag.t()) :: Container.t() - def remove_tag!(%{id: container_id}, %{id: tag_id}), do: remove_tag!(container_id, tag_id) + @spec remove_tag!(Container.t(), Tag.t(), User.t()) :: non_neg_integer() + def remove_tag!( + %Container{id: container_id, user_id: user_id}, + %Tag{id: tag_id, user_id: user_id}, + %User{id: user_id} + ) do + {count, _} = + Repo.delete_all( + from ct in ContainerTag, + where: ct.container_id == ^container_id, + where: ct.tag_id == ^tag_id, + where: ct.user_id == ^user_id + ) - @spec remove_tag!(Container.id(), Tag.id()) :: Container.t() - def remove_tag!(container_id, tag_id) - when not (container_id |> is_nil()) and not (tag_id |> is_nil()) do - Repo.delete_all( - from ct in ContainerTag, - where: ct.container_id == ^container_id, - where: ct.tag_id == ^tag_id - ) + if count == 0, do: raise("could not delete container tag"), else: count end end diff --git a/lib/cannery_web/live/container_live/form_component.ex b/lib/cannery_web/live/container_live/form_component.ex index 837c4cd..e4ed9c5 100644 --- a/lib/cannery_web/live/container_live/form_component.ex +++ b/lib/cannery_web/live/container_live/form_component.ex @@ -14,18 +14,25 @@ defmodule CanneryWeb.ContainerLive.FormComponent do Socket.t() ) :: {:ok, Socket.t()} def update(%{container: container} = assigns, socket) do - assigns = assigns |> Map.put(:changeset, container |> Containers.change_container()) - {:ok, socket |> assign(assigns)} + {:ok, socket |> assign(assigns) |> assign(:changeset, Containers.change_container(container))} end @impl true - def handle_event("validate", %{"container" => container_params}, socket) do - changeset = socket.assigns.container |> Containers.change_container(container_params) + def handle_event( + "validate", + %{"container" => container_params}, + %{assigns: %{container: container}} = socket + ) do + changeset = container |> Containers.change_container(container_params) {:noreply, socket |> assign(:changeset, changeset)} end - def handle_event("save", %{"container" => container_params}, socket) do - save_container(socket, socket.assigns.action, container_params) + def handle_event( + "save", + %{"container" => container_params}, + %{assigns: %{action: action}} = socket + ) do + save_container(socket, action, container_params) end @impl true @@ -89,36 +96,40 @@ defmodule CanneryWeb.ContainerLive.FormComponent do """ end - defp save_container(socket, :edit, container_params) do - Containers.update_container( - socket.assigns.container, - socket.assigns.current_user, - container_params - ) - |> case do - {:ok, _container} -> - {:noreply, - socket - |> put_flash(:info, dgettext("prompts", "Container updated successfully")) - |> push_redirect(to: socket.assigns.return_to)} + defp save_container( + %{assigns: %{container: container, current_user: current_user, return_to: return_to}} = + socket, + :edit, + container_params + ) do + socket = + case Containers.update_container(container, current_user, container_params) do + {:ok, %{name: container_name}} -> + prompt = dgettext("prompts", "%{name} updated successfully", name: container_name) + socket |> put_flash(:info, prompt) |> push_redirect(to: return_to) - {:error, %Changeset{} = changeset} -> - {:noreply, socket |> assign(:changeset, changeset)} - end + {:error, %Changeset{} = changeset} -> + socket |> assign(:changeset, changeset) + end + + {:noreply, socket} end - defp save_container(socket, :new, container_params) do - container_params - |> Containers.create_container(socket.assigns.current_user) - |> case do - {:ok, _container} -> - {:noreply, - socket - |> put_flash(:info, dgettext("prompts", "Container created successfully")) - |> push_redirect(to: socket.assigns.return_to)} + defp save_container( + %{assigns: %{current_user: current_user, return_to: return_to}} = socket, + :new, + container_params + ) do + socket = + case Containers.create_container(container_params, current_user) do + {:ok, %{name: container_name}} -> + prompt = dgettext("prompts", "%{name} created successfully", name: container_name) + socket |> put_flash(:info, prompt) |> push_redirect(to: return_to) - {:error, %Changeset{} = changeset} -> - {:noreply, socket |> assign(changeset: changeset)} - end + {:error, %Changeset{} = changeset} -> + socket |> assign(changeset: changeset) + end + + {:noreply, socket} end end diff --git a/lib/cannery_web/live/container_live/index.ex b/lib/cannery_web/live/container_live/index.ex index ab6f95a..59aa420 100644 --- a/lib/cannery_web/live/container_live/index.ex +++ b/lib/cannery_web/live/container_live/index.ex @@ -6,6 +6,7 @@ defmodule CanneryWeb.ContainerLive.Index do use CanneryWeb, :live_view import CanneryWeb.ContainerLive.ContainerCard alias Cannery.{Containers, Containers.Container} + alias Ecto.Changeset @impl true def mount(_params, session, socket) do @@ -13,30 +14,26 @@ defmodule CanneryWeb.ContainerLive.Index do end @impl true - def handle_params(params, _url, socket) do - {:noreply, apply_action(socket, socket.assigns.live_action, params)} + def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do + {:noreply, apply_action(socket, live_action, params)} end - defp apply_action(socket, :edit, %{"id" => id}) do + defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do socket |> assign(:page_title, gettext("Edit Container")) - |> assign(:container, Containers.get_container!(id)) + |> assign(:container, Containers.get_container!(id, current_user)) end defp apply_action(socket, :new, _params) do - socket - |> assign(:page_title, gettext("New Container")) - |> assign(:container, %Container{}) + socket |> assign(:page_title, gettext("New Container")) |> assign(:container, %Container{}) end defp apply_action(socket, :index, _params) do - socket - |> assign(:page_title, gettext("Listing Containers")) - |> assign(:container, nil) + socket |> assign(:page_title, gettext("Listing Containers")) |> assign(:container, nil) end @impl true - def handle_event("delete", %{"id" => id}, socket) do + def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do socket = socket.assigns.containers |> Enum.find(fn %{id: container_id} -> id == container_id end) @@ -45,27 +42,23 @@ defmodule CanneryWeb.ContainerLive.Index do socket |> put_flash(:error, dgettext("errors", "Could not find that container")) container -> - container - |> Containers.delete_container(socket.assigns.current_user) - |> case do - {:ok, container} -> - socket - |> put_flash( - :info, - dgettext("prompts", "%{name} has been deleted", name: container.name) - ) - |> display_containers() + case Containers.delete_container(container, current_user) do + {:ok, %{name: container_name}} -> + prompt = dgettext("prompts", "%{name} has been deleted", name: container_name) + socket |> put_flash(:info, prompt) |> display_containers() {:error, %{action: :delete, errors: [ammo_groups: _error], valid?: false} = changeset} -> ammo_groups_error = changeset |> changeset_errors(:ammo_groups) |> Enum.join(", ") - socket - |> put_flash( - :error, - dgettext("errors", "Could not delete container: %{error}", + prompt = + dgettext( + "errors", + "Could not delete %{name}: %{error}", + name: changeset |> Changeset.get_field(:name, "container"), error: ammo_groups_error ) - ) + + socket |> put_flash(:error, prompt) {:error, changeset} -> socket |> put_flash(:error, changeset |> changeset_errors()) @@ -76,7 +69,6 @@ defmodule CanneryWeb.ContainerLive.Index do end defp display_containers(%{assigns: %{current_user: current_user}} = socket) do - containers = Containers.list_containers(current_user) - socket |> assign(containers: containers) + socket |> assign(containers: Containers.list_containers(current_user)) end end diff --git a/lib/cannery_web/live/container_live/show.ex b/lib/cannery_web/live/container_live/show.ex index 0e35851..91cbe7c 100644 --- a/lib/cannery_web/live/container_live/show.ex +++ b/lib/cannery_web/live/container_live/show.ex @@ -6,6 +6,7 @@ defmodule CanneryWeb.ContainerLive.Show do use CanneryWeb, :live_view import CanneryWeb.AmmoGroupLive.AmmoGroupCard alias Cannery.{Containers, Repo} + alias Ecto.Changeset @impl true def mount(_params, session, socket) do @@ -13,39 +14,47 @@ defmodule CanneryWeb.ContainerLive.Show do end @impl true - def handle_params(%{"id" => id}, _, socket) do + def handle_params( + %{"id" => id}, + _, + %{assigns: %{current_user: current_user, live_action: live_action}} = socket + ) do socket = socket |> assign( - page_title: page_title(socket.assigns.live_action), - container: Containers.get_container!(id) |> Repo.preload(:ammo_groups) + page_title: page_title(live_action), + container: Containers.get_container!(id, current_user) |> Repo.preload(:ammo_groups) ) {:noreply, socket} end @impl true - def handle_event("delete", _, socket) do + def handle_event( + "delete", + _, + %{assigns: %{container: container, current_user: current_user}} = socket + ) do socket = - socket.assigns.container - |> Containers.delete_container(socket.assigns.current_user) + Containers.delete_container(container, current_user) |> case do - {:ok, container} -> + {:ok, %{name: container_name}} -> + prompt = dgettext("prompts", "%{name} has been deleted", name: container_name) + socket - |> put_flash( - :info, - dgettext("prompts", "%{name} has been deleted", name: container.name) - ) + |> put_flash(:info, prompt) |> push_redirect(to: Routes.container_index_path(socket, :index)) {:error, %{action: :delete, errors: [ammo_groups: _error], valid?: false} = changeset} -> ammo_groups_error = changeset |> changeset_errors(:ammo_groups) |> Enum.join(", ") - socket - |> put_flash( - :error, - dgettext("errors", "Could not delete container: %{error}", error: ammo_groups_error) - ) + prompt = + dgettext("errors", "Could not delete %{name}: %{error}", + name: changeset |> Changeset.get_field(:name, "container"), + error: ammo_groups_error + ) + + socket |> put_flash(:error, prompt) {:error, changeset} -> socket |> put_flash(:error, changeset |> changeset_errors())