From b3d2300b8bf135c9215e106391d773595d070dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20M=C3=A4nnchen?= Date: Mon, 30 Nov 2020 18:38:11 +0100 Subject: [PATCH] Refactor Duplicated Code --- lib/paper_trail.ex | 135 ++++++------------ .../bang_functions_simple_mode_test.exs | 2 +- .../bang_functions_strict_mode_test.exs | 4 +- 3 files changed, 50 insertions(+), 91 deletions(-) diff --git a/lib/paper_trail.ex b/lib/paper_trail.ex index 1fea2558..f6ede331 100644 --- a/lib/paper_trail.ex +++ b/lib/paper_trail.ex @@ -1,8 +1,5 @@ defmodule PaperTrail do - import Ecto.Changeset - alias PaperTrail.Version - alias PaperTrail.RepoClient alias PaperTrail.Serializer defdelegate get_version(record), to: PaperTrail.VersionQueries @@ -22,6 +19,9 @@ defmodule PaperTrail do @doc """ Inserts a record to the database with a related version insertion in one transaction """ + @spec insert(changeset :: Ecto.Changeset.t(model), options :: Keyword.t()) :: + {:ok, %{model: model, version: Version.t()}} | {:error, Ecto.Changeset.t(model) | term} + when model: struct def insert( changeset, options \\ [ @@ -42,6 +42,8 @@ defmodule PaperTrail do @doc """ Same as insert/2 but returns only the model struct or raises if the changeset is invalid. """ + @spec insert!(changeset :: Ecto.Changeset.t(model), options :: Keyword.t()) :: model + when model: struct def insert!( changeset, options \\ [ @@ -54,50 +56,17 @@ defmodule PaperTrail do ecto_options: [] ] ) do - repo = RepoClient.repo() - ecto_options = options[:ecto_options] || [] - - repo.transaction(fn -> - case RepoClient.strict_mode() do - true -> - version_id = get_sequence_id("versions") + 1 - - changeset_data = - Map.get(changeset, :data, changeset) - |> Map.merge(%{ - id: get_sequence_id(changeset) + 1, - first_version_id: version_id, - current_version_id: version_id - }) - - initial_version = - make_version_struct(%{event: "insert"}, changeset_data, options) - |> repo.insert! - - updated_changeset = - changeset - |> change(%{ - first_version_id: initial_version.id, - current_version_id: initial_version.id - }) - - model = repo.insert!(updated_changeset, ecto_options) - target_version = make_version_struct(%{event: "insert"}, model, options) |> serialize() - Version.changeset(initial_version, target_version) |> repo.update! - model - - _ -> - model = repo.insert!(changeset, ecto_options) - make_version_struct(%{event: "insert"}, model, options) |> repo.insert! - model - end - end) - |> elem(1) + changeset + |> insert(options) + |> model_or_error() end @doc """ Updates a record from the database with a related version insertion in one transaction """ + @spec update(changeset :: Ecto.Changeset.t(model), options :: Keyword.t()) :: + {:ok, %{model: model, version: Version.t()}} | {:error, Ecto.Changeset.t(model) | term} + when model: struct def update(changeset, options \\ [origin: nil, meta: nil, originator: nil, prefix: nil]) do PaperTrail.Multi.new() |> PaperTrail.Multi.update(changeset, options) @@ -107,65 +76,55 @@ defmodule PaperTrail do @doc """ Same as update/2 but returns only the model struct or raises if the changeset is invalid. """ + @spec update!(changeset :: Ecto.Changeset.t(model), options :: Keyword.t()) :: model + when model: struct def update!(changeset, options \\ [origin: nil, meta: nil, originator: nil, prefix: nil]) do - repo = PaperTrail.RepoClient.repo() - client = PaperTrail.RepoClient - - repo.transaction(fn -> - case client.strict_mode() do - true -> - version_data = - changeset.data - |> Map.merge(%{ - current_version_id: get_sequence_id("versions") - }) - - target_changeset = changeset |> Map.merge(%{data: version_data}) - target_version = make_version_struct(%{event: "update"}, target_changeset, options) - initial_version = repo.insert!(target_version) - updated_changeset = changeset |> change(%{current_version_id: initial_version.id}) - model = repo.update!(updated_changeset) - - new_item_changes = - initial_version.item_changes - |> Map.merge(%{ - current_version_id: initial_version.id - }) - - initial_version |> change(%{item_changes: new_item_changes}) |> repo.update! - model - - _ -> - model = repo.update!(changeset) - version_struct = make_version_struct(%{event: "update"}, changeset, options) - repo.insert!(version_struct) - model - end - end) - |> elem(1) + changeset + |> update(options) + |> model_or_error() end @doc """ Deletes a record from the database with a related version insertion in one transaction """ - def delete(struct, options \\ [origin: nil, meta: nil, originator: nil, prefix: nil]) do + @spec delete(model_or_changeset :: model | Ecto.Changeset.t(model), options :: Keyword.t()) :: + {:ok, %{model: model, version: Version.t()}} | {:error, Ecto.Changeset.t(model) | term} + when model: struct + def delete( + model_or_changeset, + options \\ [origin: nil, meta: nil, originator: nil, prefix: nil] + ) do PaperTrail.Multi.new() - |> PaperTrail.Multi.delete(struct, options) + |> PaperTrail.Multi.delete(model_or_changeset, options) |> PaperTrail.Multi.commit() end @doc """ Same as delete/2 but returns only the model struct or raises if the changeset is invalid. """ - def delete!(struct, options \\ [origin: nil, meta: nil, originator: nil, prefix: nil]) do - repo = PaperTrail.RepoClient.repo() + @spec delete!(model_or_changeset :: model | Ecto.Changeset.t(model), options :: Keyword.t()) :: + model + when model: struct + def delete!( + model_or_changeset, + options \\ [origin: nil, meta: nil, originator: nil, prefix: nil] + ) do + model_or_changeset + |> delete(options) + |> model_or_error() + end + + @spec model_or_error({:ok, %{model: model}}) :: model when model: struct() + defp model_or_error({:ok, %{model: model}}) do + model + end + + @spec model_or_error({:error, reason :: term}) :: no_return + defp model_or_error({:error, %Ecto.Changeset{} = changeset}) do + raise Ecto.InvalidChangesetError, action: :update, changeset: changeset + end - repo.transaction(fn -> - model = repo.delete!(struct, options) - version_struct = make_version_struct(%{event: "delete"}, struct, options) - repo.insert!(version_struct, options) - model - end) - |> elem(1) + defp model_or_error({:error, reason}) do + raise reason end end diff --git a/test/paper_trail/bang_functions_simple_mode_test.exs b/test/paper_trail/bang_functions_simple_mode_test.exs index cde4779d..ba6cef55 100644 --- a/test/paper_trail/bang_functions_simple_mode_test.exs +++ b/test/paper_trail/bang_functions_simple_mode_test.exs @@ -989,7 +989,7 @@ defmodule PaperTrailTest.SimpleModeBangFunctions do |> PaperTrail.insert!(opts_with_prefix) end - defp update_company_with_version(company, params \\ @update_company_params, options \\ nil) do + defp update_company_with_version(company, params \\ @update_company_params, options \\ []) do Company.changeset(company, params) |> PaperTrail.update!(options) end diff --git a/test/paper_trail/bang_functions_strict_mode_test.exs b/test/paper_trail/bang_functions_strict_mode_test.exs index 1d3af3a9..baa91e3f 100644 --- a/test/paper_trail/bang_functions_strict_mode_test.exs +++ b/test/paper_trail/bang_functions_strict_mode_test.exs @@ -1006,14 +1006,14 @@ defmodule PaperTrailTest.StrictModeBangFunctions do |> PaperTrail.insert!(opts_with_prefix) end - defp update_company_with_version(company, params \\ @update_company_params, options \\ nil) do + defp update_company_with_version(company, params \\ @update_company_params, options \\ []) do Company.changeset(company, params) |> PaperTrail.update!(options) end defp update_company_with_version_multi( company, params \\ @update_company_params, - options \\ nil + options \\ [] ) do opts_with_prefix = Keyword.put(options || [], :prefix, MultiTenant.tenant())