From c2b4fb6cff2d6ed545534f718454d2fa0a92370f Mon Sep 17 00:00:00 2001 From: miffy Date: Sat, 7 Sep 2019 02:36:37 +0200 Subject: [PATCH] Refactoring of Media context --- lib/mobilizon/media.ex | 125 ---------------------------- lib/mobilizon/media/file.ex | 26 ++++-- lib/mobilizon/media/media.ex | 90 ++++++++++++++++++++ lib/mobilizon/media/picture.ex | 15 +++- test/mobilizon/media/media_test.exs | 5 -- 5 files changed, 122 insertions(+), 139 deletions(-) delete mode 100644 lib/mobilizon/media.ex create mode 100644 lib/mobilizon/media/media.ex diff --git a/lib/mobilizon/media.ex b/lib/mobilizon/media.ex deleted file mode 100644 index 850b5274e..000000000 --- a/lib/mobilizon/media.ex +++ /dev/null @@ -1,125 +0,0 @@ -defmodule Mobilizon.Media do - @moduledoc """ - The Media context. - """ - - import Ecto.Query, warn: false - alias Mobilizon.Repo - - alias Mobilizon.Media.Picture - alias Mobilizon.Media.File - alias Ecto.Multi - - @doc false - def data() do - Dataloader.Ecto.new(Mobilizon.Repo, query: &query/2) - end - - @doc false - def query(queryable, _params) do - queryable - end - - @doc """ - Gets a single picture. - - Raises `Ecto.NoResultsError` if the Picture does not exist. - - ## Examples - - iex> get_picture!(123) - %Picture{} - - iex> get_picture!(456) - ** (Ecto.NoResultsError) - - """ - def get_picture!(id), do: Repo.get!(Picture, id) - - def get_picture(id), do: Repo.get(Picture, id) - - @doc """ - Get a picture by it's URL - """ - @spec get_picture_by_url(String.t()) :: Picture.t() | nil - def get_picture_by_url(url) do - from( - p in Picture, - where: fragment("? @> ?", p.file, ~s|{"url": "#{url}"}|) - ) - |> Repo.one() - end - - @doc """ - Creates a picture. - - ## Examples - - iex> create_picture(%{field: value}) - {:ok, %Picture{}} - - iex> create_picture(%{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def create_picture(attrs \\ %{}) do - %Picture{} - |> Picture.changeset(attrs) - |> Repo.insert() - end - - @doc """ - Updates a picture. - - ## Examples - - iex> update_picture(picture, %{field: new_value}) - {:ok, %Picture{}} - - iex> update_picture(picture, %{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def update_picture(%Picture{} = picture, attrs) do - picture - |> Picture.changeset(attrs) - |> Repo.update() - end - - @doc """ - Deletes a Picture. - - ## Examples - - iex> delete_picture(picture) - {:ok, %Picture{}} - - iex> delete_picture(picture) - {:error, %Ecto.Changeset{}} - - """ - def delete_picture(%Picture{} = picture) do - case Multi.new() - |> Multi.delete(:picture, picture) - |> Multi.run(:remove, fn _repo, %{picture: %Picture{file: %File{url: url}}} = _picture -> - MobilizonWeb.Upload.remove(url) - end) - |> Repo.transaction() do - {:ok, %{picture: %Picture{} = picture}} -> {:ok, picture} - {:error, :remove, error, _} -> {:error, error} - end - end - - @doc """ - Returns an `%Ecto.Changeset{}` for tracking picture changes. - - ## Examples - - iex> change_picture(picture) - %Ecto.Changeset{source: %Picture{}} - - """ - def change_picture(%Picture{} = picture) do - Picture.changeset(picture, %{}) - end -end diff --git a/lib/mobilizon/media/file.ex b/lib/mobilizon/media/file.ex index 2574a012b..611cd367d 100644 --- a/lib/mobilizon/media/file.ex +++ b/lib/mobilizon/media/file.ex @@ -1,9 +1,22 @@ defmodule Mobilizon.Media.File do @moduledoc """ - Represents a file entity + Represents a file entity. """ + use Ecto.Schema - import Ecto.Changeset + + import Ecto.Changeset, only: [cast: 3, validate_required: 2] + + @type t :: %__MODULE__{ + name: String.t(), + url: String.t(), + content_type: String.t(), + size: integer + } + + @required_attrs [:name, :url] + @optional_attrs [:content_type, :size] + @attrs @required_attrs ++ @optional_attrs embedded_schema do field(:name, :string) @@ -15,9 +28,10 @@ defmodule Mobilizon.Media.File do end @doc false - def changeset(picture, attrs) do - picture - |> cast(attrs, [:name, :url, :content_type, :size]) - |> validate_required([:name, :url]) + @spec changeset(t | Ecto.Changeset.t(), map) :: Ecto.Changeset.t() + def changeset(file, attrs) do + file + |> cast(attrs, @attrs) + |> validate_required(@required_attrs) end end diff --git a/lib/mobilizon/media/media.ex b/lib/mobilizon/media/media.ex new file mode 100644 index 000000000..70762091b --- /dev/null +++ b/lib/mobilizon/media/media.ex @@ -0,0 +1,90 @@ +defmodule Mobilizon.Media do + @moduledoc """ + The Media context. + """ + + import Ecto.Query + + alias Ecto.Multi + + alias Mobilizon.Media.{File, Picture} + alias Mobilizon.Repo + + @doc false + @spec data :: Dataloader.Ecto.t() + def data, do: Dataloader.Ecto.new(Mobilizon.Repo, query: &query/2) + + @doc false + @spec query(Ecto.Query.t(), map) :: Ecto.Query.t() + def query(queryable, _params), do: queryable + + @doc """ + Gets a single picture. + """ + @spec get_picture(integer | String.t()) :: Picture.t() | nil + def get_picture(id), do: Repo.get(Picture, id) + + @doc """ + Gets a single picture. + Raises `Ecto.NoResultsError` if the picture does not exist. + """ + @spec get_picture!(integer | String.t()) :: Picture.t() + def get_picture!(id), do: Repo.get!(Picture, id) + + @doc """ + Get a picture by it's URL. + """ + @spec get_picture_by_url(String.t()) :: Picture.t() | nil + def get_picture_by_url(url) do + url + |> picture_by_url_query() + |> Repo.one() + end + + @doc """ + Creates a picture. + """ + @spec create_picture(map) :: {:ok, Picture.t()} | {:error, Ecto.Changeset.t()} + def create_picture(attrs \\ %{}) do + %Picture{} + |> Picture.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a picture. + """ + @spec update_picture(Picture.t(), map) :: {:ok, Picture.t()} | {:error, Ecto.Changeset.t()} + def update_picture(%Picture{} = picture, attrs) do + picture + |> Picture.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a picture. + """ + @spec delete_picture(Picture.t()) :: {:ok, Picture.t()} | {:error, Ecto.Changeset.t()} + def delete_picture(%Picture{} = picture) do + transaction = + Multi.new() + |> Multi.delete(:picture, picture) + |> Multi.run(:remove, fn _repo, %{picture: %Picture{file: %File{url: url}}} -> + MobilizonWeb.Upload.remove(url) + end) + |> Repo.transaction() + + case transaction do + {:ok, %{picture: %Picture{} = picture}} -> {:ok, picture} + {:error, :remove, error, _} -> {:error, error} + end + end + + @spec picture_by_url_query(String.t()) :: Ecto.Query.t() + defp picture_by_url_query(url) do + from( + p in Picture, + where: fragment("? @> ?", p.file, ~s|{"url": "#{url}"}|) + ) + end +end diff --git a/lib/mobilizon/media/picture.ex b/lib/mobilizon/media/picture.ex index 62b811894..00f55e75d 100644 --- a/lib/mobilizon/media/picture.ex +++ b/lib/mobilizon/media/picture.ex @@ -1,11 +1,19 @@ defmodule Mobilizon.Media.Picture do @moduledoc """ - Represents a picture entity + Represents a picture entity. """ + use Ecto.Schema - import Ecto.Changeset - alias Mobilizon.Media.File + + import Ecto.Changeset, only: [cast: 3, cast_embed: 2] + alias Mobilizon.Actors.Actor + alias Mobilizon.Media.File + + @type t :: %__MODULE__{ + file: File.t(), + actor: Actor.t() + } schema "pictures" do embeds_one(:file, File, on_replace: :update) @@ -15,6 +23,7 @@ defmodule Mobilizon.Media.Picture do end @doc false + @spec changeset(t | Ecto.Changeset.t(), map) :: Ecto.Changeset.t() def changeset(picture, attrs) do picture |> cast(attrs, [:actor_id]) diff --git a/test/mobilizon/media/media_test.exs b/test/mobilizon/media/media_test.exs index 6ba645d6a..50b3b96ee 100644 --- a/test/mobilizon/media/media_test.exs +++ b/test/mobilizon/media/media_test.exs @@ -60,10 +60,5 @@ defmodule Mobilizon.MediaTest do "/" <> path ) end - - test "change_picture/1 returns a picture changeset" do - picture = insert(:picture) - assert %Ecto.Changeset{} = Media.change_picture(picture) - end end end