From 88387b90c218be17a87b562b636a1816bb20219a Mon Sep 17 00:00:00 2001 From: Izel Nakri Date: Sat, 18 Mar 2017 03:32:50 +0100 Subject: [PATCH] initial design --- README.md | 2 +- lib/paper_trail.ex | 164 ++++++++++++++++++++++++++++----------------- 2 files changed, 103 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 474ded61..32b07c8b 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ user = create_user() PaperTrail.insert(changeset, originator: user) {:ok, result} = PaperTrail.update(edit_changeset, originator: user) # or you can use :user in the params instead of :originator if this is your config: -# paper_trail originator: [name: :user, model: YourApplication.User] +# config :paper_trail, originator: [name: :user, model: YourApplication.User] {:ok, result} = PaperTrail.update(edit_changeset, user: user) result[:version] |> Repo.preload(:user) |> Map.get(:user) # we can access the user who made the change from the version thanks to originator relationships! PaperTrail.delete(edit_changeset, user: user) diff --git a/lib/paper_trail.ex b/lib/paper_trail.ex index c80a61fe..ff589b62 100644 --- a/lib/paper_trail.ex +++ b/lib/paper_trail.ex @@ -49,31 +49,9 @@ defmodule PaperTrail do Inserts a record to the database with a related version insertion in one transaction """ def insert(changeset, options \\ [origin: nil, meta: nil, originator: nil]) do + transaction = insert_transaction(changeset, options) case @client.strict_mode() do true -> - transaction = Multi.new - |> Multi.run(:initial_version, fn %{} -> - version_id = get_sequence_id("versions") + 1 - changeset_data = changeset.data |> Map.merge(%{ - id: get_sequence_from_model(changeset) + 1, - first_version_id: version_id, - current_version_id: version_id - }) - initial_version = make_version_struct(%{event: "insert"}, changeset_data, options) - @repo.insert(initial_version) - end) - |> Multi.run(:model, fn %{initial_version: initial_version} -> - updated_changeset = changeset |> change(%{ - first_version_id: initial_version.id, current_version_id: initial_version.id - }) - @repo.insert(updated_changeset) - end) - |> Multi.run(:version, fn %{initial_version: initial_version, model: model} -> - target_version = make_version_struct(%{event: "insert"}, model, options) |> serialize() - Version.changeset(initial_version, target_version) |> @repo.update - end) - |> @repo.transaction - case transaction do {:error, :model, changeset, %{}} -> filtered_changes = Map.drop(changeset.changes, [:current_version_id, :first_version_id]) @@ -81,14 +59,6 @@ defmodule PaperTrail do {:ok, map} -> {:ok, Map.drop(map, [:initial_version])} end _ -> - transaction = Multi.new - |> Multi.insert(:model, changeset) - |> Multi.run(:version, fn %{model: model} -> - version = make_version_struct(%{event: "insert"}, model, options) - @repo.insert(version) - end) - |> @repo.transaction - case transaction do {:error, :model, changeset, %{}} -> {:error, Map.merge(changeset, %{repo: @repo})} _ -> transaction @@ -96,15 +66,106 @@ defmodule PaperTrail do end end + # add doc + def insert!(changeset, options \\ [origin: nil, meta: nil, originator: nil]) do + transaction = insert_transaction(changeset, options) + case transaction do + {:error, :model, changeset, %{}} -> raise "ERROR" # change the name + _ -> transaction |> elem(1) |> Map.get(:model) + end + end + @doc """ Updates a record from the database with a related version insertion in one transaction """ def update(changeset, options \\ [origin: nil, meta: nil, originator: nil]) do + transaction = update_transaction(changeset, options) case @client.strict_mode() do true -> - transaction = Multi.new + case transaction do + {:error, :model, changeset, %{}} -> + filtered_changes = Map.drop(changeset.changes, [:current_version_id]) + {:error, Map.merge(changeset, %{repo: @repo, changes: filtered_changes})} + {:ok, map} -> {:ok, Map.delete(map, :initial_version)} + end + _ -> + case transaction do + {:error, :model, changeset, %{}} -> {:error, Map.merge(changeset, %{repo: @repo})} + _ -> transaction + end + end + end + + def update!(changeset, options \\ [origin: nil, meta: nil, originator: nil]) do + transaction = update_transaction(changeset, options) + case transaction do + {:error, :model, changeset, %{}} -> raise "ERROR" # change the name + _ -> transaction |> elem(1) |> Map.get(:model) + end + 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]) do + transaction = delete_transaction(struct, options) + case transaction do + {:error, :model, changeset, %{}} -> {:error, Map.merge(changeset, %{repo: @repo})} + _ -> transaction + end + end + + def delete!(struct, options \\ [origin: nil, meta: nil, originator: nil]) do + transaction = delete_transaction(struct, options) + case transaction do + {:error, :model, changeset, %{}} -> raise "ERROR" # change the name + _ -> transaction |> elem(1) |> Map.get(:model) + end + end + + defp insert_transaction(changeset, options) do + multi_order = case @client.strict_mode() do + true -> + Multi.new |> Multi.run(:initial_version, fn %{} -> - version_data = changeset.data |> Map.merge(%{current_version_id: get_sequence_id("versions")}) + version_id = get_sequence_id("versions") + 1 + changeset_data = changeset.data |> Map.merge(%{ + id: get_sequence_from_model(changeset) + 1, + first_version_id: version_id, + current_version_id: version_id + }) + initial_version = make_version_struct(%{event: "insert"}, changeset_data, options) + @repo.insert(initial_version) + end) + |> Multi.run(:model, fn %{initial_version: initial_version} -> + updated_changeset = changeset |> change(%{ + first_version_id: initial_version.id, current_version_id: initial_version.id + }) + @repo.insert(updated_changeset) + end) + |> Multi.run(:version, fn %{initial_version: initial_version, model: model} -> + target_version = make_version_struct(%{event: "insert"}, model, options) |> serialize() + Version.changeset(initial_version, target_version) |> @repo.update + end) + _ -> + Multi.new + |> Multi.insert(:model, changeset) + |> Multi.run(:version, fn %{model: model} -> + version = make_version_struct(%{event: "insert"}, model, options) + @repo.insert(version) + end) + end + @repo.transaction(multi_order) + end + + defp update_transaction(changeset, options) do + multi_order = case @client.strict_mode() do + true -> + Multi.new + |> Multi.run(:initial_version, fn %{} -> + 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) @repo.insert(target_version) @@ -119,48 +180,27 @@ defmodule PaperTrail do }) initial_version |> change(%{item_changes: new_item_changes}) |> @repo.update end) - |> @repo.transaction - - case transaction do - {:error, :model, changeset, %{}} -> - filtered_changes = Map.drop(changeset.changes, [:current_version_id]) - {:error, Map.merge(changeset, %{repo: @repo, changes: filtered_changes})} - {:ok, map} -> {:ok, Map.delete(map, :initial_version)} - end _ -> - transaction = Multi.new + Multi.new |> Multi.update(:model, changeset) |> Multi.run(:version, fn %{model: _model} -> - version = make_version_struct(%{event: "update"}, changeset, options) - @repo.insert(version) - end) - |> @repo.transaction - - case transaction do - {:error, :model, changeset, %{}} -> {:error, Map.merge(changeset, %{repo: @repo})} - _ -> transaction - end + version = make_version_struct(%{event: "update"}, changeset, options) + @repo.insert(version) + end) end + @repo.transaction(multi_order) 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]) do - transaction = Multi.new + defp delete_transaction(struct, options) do + Multi.new |> Multi.delete(:model, struct) |> Multi.run(:version, fn %{} -> version = make_version_struct(%{event: "delete"}, struct, options) @repo.insert(version) end) |> @repo.transaction - - case transaction do - {:error, :model, changeset, %{}} -> {:error, Map.merge(changeset, %{repo: @repo})} - _ -> transaction - end end - + defp make_version_struct(%{event: "insert"}, model, options) do originator_ref = options[@originator[:name]] || options[:originator] originator_id = case originator_ref do