Skip to content

Commit

Permalink
Merge pull request ZennerIoT#46 from GhostGroup/add-configurable-stru…
Browse files Browse the repository at this point in the history
…ct-primitives

Add configurable struct primitives
  • Loading branch information
narrowtux authored May 24, 2020
2 parents 7d9434b + aa3e74e commit a013acf
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 3 deletions.
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Used by "mix format"
[
import_deps: [:ecto, :ecto_sql],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,36 @@ config :ex_audit,
]
```

Optionally, you can tell ExAudit to treat certain structs as primitives and not record internal changes for the
struct. Add these under the key `:primitive_structs` in your config. So for example, if you configured `Date` to be treated as a primitive:

```elixir
config :ex_audit,
ecto_repos: [ExAudit.Test.Repo],
version_schema: ExAudit.Test.Version,
tracked_schemas: [
ExAudit.Test.User,
ExAudit.Test.BlogPost,
ExAudit.Test.BlogPost.Section,
ExAudit.Test.Comment
],
primitive_structs: [
Date
]
```

then the patch would record the entire Date struct as a change:

```elixir
{:primitive_change, ~D[2000-01-01], ~D[2000-01-18]}
```

instead of descending into the struct to find the individual part that changed:

```elixir
{:changed, %{day: {:changed, {:primitive_change, 1, 18}}}}
```

### Version Schema and Migration

You need to copy the migration and the schema module for the versions table. This allows you to add custom fields
Expand Down
3 changes: 3 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ config :ex_audit,
ExAudit.Test.BlogPost,
ExAudit.Test.BlogPost.Section,
ExAudit.Test.Comment
],
primitive_structs: [
Date
]
5 changes: 3 additions & 2 deletions example/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule ExAudit.Test.User do
schema "users" do
field :email, :string
field :name, :string
field :birthday, :date

field :transient_field, :integer

Expand All @@ -17,6 +18,6 @@ defmodule ExAudit.Test.User do

def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:email, :name])
|> cast(params, [:email, :name, :birthday])
end
end
end
16 changes: 16 additions & 0 deletions lib/diff/diff.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ defmodule ExAudit.Diff do
:not_changed
end

def diff(%{__struct__: a_struct} = a, %{__struct__: b_struct} = b) do
if primitive_struct?(a_struct) and primitive_struct?(b_struct) do
{:primitive_change, a, b}
else
diff(Map.from_struct(a), Map.from_struct(b))
end
end

def diff(%{} = a, %{} = b) do
all_keys =
(Map.keys(a) ++ Map.keys(b))
Expand Down Expand Up @@ -129,4 +137,12 @@ defmodule ExAudit.Diff do
|> Enum.reverse()
|> Enum.map(&reverse/1)
end

## PRIVATE

defp primitive_struct?(type) do
primitive_structs = Application.get_env(:ex_audit, :primitive_structs, [])

type in primitive_structs
end
end
9 changes: 9 additions & 0 deletions priv/repo/migrations/20200519214139_add_birthday_to_user.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule ExAudit.Test.Repo.Migrations.AddBirthdayToUser do
use Ecto.Migration

def change do
alter table(:users) do
add :birthday, :date
end
end
end
22 changes: 21 additions & 1 deletion test/assoc_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule AssocTest do

import Ecto.Query

alias ExAudit.Test.{Repo, Version, BlogPost, Comment, Util, UserGroup}
alias ExAudit.Test.{Repo, Version, BlogPost, Comment, Util, User, UserGroup}

test "comment lifecycle tracked" do
user = Util.create_user()
Expand All @@ -29,6 +29,26 @@ defmodule AssocTest do
assert actor_id == user.id
end

test "structs configured as primitives are treated as primitives" do
{:ok, old_date} = Date.new(2000, 1, 1)
params = %{name: "Bob", email: "foo@bar.com", birthday: old_date}
changeset = User.changeset(%User{}, params)
{:ok, user} = Repo.insert(changeset)

new_date = Date.add(old_date, 17)
params = %{birthday: new_date}
changeset = User.changeset(user, params)
{:ok, user} = Repo.update(changeset)

[version | _] = Repo.history(user)

assert %{
patch: %{
birthday: {:changed, {:primitive_change, ^old_date, ^new_date}}
}
} = version
end

test "should track cascading deletions (before they happen)" do
user = Util.create_user()

Expand Down
10 changes: 10 additions & 0 deletions test/diff_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,14 @@ defmodule DiffTest do
baz: {:removed, 1}
}
end

test "structs configured as primitives are treated as primitives" do
val1 = Date.new(2020, 1, 1)
val2 = Date.new(2020, 2, 2)

a = %{foo: val1}
b = %{foo: val2}

assert %{foo: {:changed, {:primitive_change, val1, val2}}} == Diff.diff(a, b)
end
end

0 comments on commit a013acf

Please sign in to comment.