Skip to content

Commit

Permalink
Ecto Multi Support Proposition (#62)
Browse files Browse the repository at this point in the history
* Changed API internals to allow Ecto.Multi manipulation after PaperTrail actions before commiting
  • Loading branch information
DiscoStarslayer authored and izelnakri committed Dec 16, 2019
1 parent 7b37739 commit 86e4f4c
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 142 deletions.
151 changes: 9 additions & 142 deletions lib/paper_trail.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule PaperTrail do
import Ecto.Changeset

alias Ecto.Multi
alias PaperTrail.Version
alias PaperTrail.RepoClient

Expand All @@ -17,72 +16,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, prefix: nil]) do
repo = RepoClient.repo()

transaction_order =
case RepoClient.strict_mode() do
true ->
Multi.new()
|> Multi.run(:initial_version, fn repo, %{} ->
version_id = get_sequence_id("versions") + 1

changeset_data =
Map.get(changeset, :data, changeset)
|> 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 repo, %{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 repo, %{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 repo, %{model: model} ->
version = make_version_struct(%{event: "insert"}, model, options)
repo.insert(version)
end)
end

transaction = repo.transaction(transaction_order)

case RepoClient.strict_mode() do
true ->
case transaction do
{:error, :model, changeset, %{}} ->
filtered_changes =
Map.drop(changeset.changes, [:current_version_id, :first_version_id])

{:error, Map.merge(changeset, %{repo: repo, changes: filtered_changes})}

{:ok, map} ->
{:ok, Map.drop(map, [:initial_version])}
end

_ ->
case transaction do
{:error, :model, changeset, %{}} -> {:error, Map.merge(changeset, %{repo: repo})}
_ -> transaction
end
end
PaperTrail.Multi.new()
|> PaperTrail.Multi.insert(changeset, options)
|> PaperTrail.Multi.commit()
end

@doc """
Expand Down Expand Up @@ -133,66 +69,9 @@ defmodule PaperTrail do
Updates a record from the database with a related version insertion in one transaction
"""
def update(changeset, options \\ [origin: nil, meta: nil, originator: nil, prefix: nil]) do
repo = PaperTrail.RepoClient.repo()
client = PaperTrail.RepoClient

transaction_order =
case client.strict_mode() do
true ->
Multi.new()
|> Multi.run(:initial_version, fn repo, %{} ->
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)
end)
|> Multi.run(:model, fn repo, %{initial_version: initial_version} ->
updated_changeset = changeset |> change(%{current_version_id: initial_version.id})
repo.update(updated_changeset)
end)
|> Multi.run(:version, fn repo, %{initial_version: initial_version} ->
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
end)

_ ->
Multi.new()
|> Multi.update(:model, changeset)
|> Multi.run(:version, fn repo, %{model: _model} ->
version = make_version_struct(%{event: "update"}, changeset, options)
repo.insert(version)
end)
end

transaction = repo.transaction(transaction_order)

case client.strict_mode() do
true ->
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
PaperTrail.Multi.new()
|> PaperTrail.Multi.update(changeset, options)
|> PaperTrail.Multi.commit()
end

@doc """
Expand Down Expand Up @@ -240,21 +119,9 @@ defmodule PaperTrail do
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
repo = PaperTrail.RepoClient.repo()

transaction =
Multi.new()
|> Multi.delete(:model, struct, options)
|> Multi.run(:version, fn repo, %{} ->
version = make_version_struct(%{event: "delete"}, struct, options)
repo.insert(version, options)
end)
|> repo.transaction(options)

case transaction do
{:error, :model, changeset, %{}} -> {:error, Map.merge(changeset, %{repo: repo})}
_ -> transaction
end
PaperTrail.Multi.new()
|> PaperTrail.Multi.delete(struct, options)
|> PaperTrail.Multi.commit()
end

@doc """
Expand Down
Loading

0 comments on commit 86e4f4c

Please sign in to comment.