Skip to content

Commit

Permalink
initial design
Browse files Browse the repository at this point in the history
  • Loading branch information
izelnakri committed Mar 18, 2017
1 parent 92d9714 commit 88387b9
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 63 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
164 changes: 102 additions & 62 deletions lib/paper_trail.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,62 +49,123 @@ 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])
{:error, Map.merge(changeset, %{repo: @repo, changes: filtered_changes})}
{: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
end
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)
Expand All @@ -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
Expand Down

0 comments on commit 88387b9

Please sign in to comment.