Skip to content

Instantly share code, notes, and snippets.

View rugyoga's full-sized avatar

Guy Argo rugyoga

View GitHub Profile
@spec audit_fun(struct(), Macro.Env.t()) :: struct()
def audit_fun(r, e) do
r |> struct([{@key, payload(r, e)}])
end
@spec payload(struct(), Macro.Env) :: trail_t()
def payload(r, e), do: {r, e.file, e.line}
@rugyoga
rugyoga / audit_types.ex
Created September 5, 2022 01:05
types and constants for audit
@key :__audit_trail__
@enabled? Application.compile_env(:audit, :enabled?, false)
@type file_t :: binary()
@type line_t :: non_neg_integer()
@type trail_t :: {struct(), file_t(), line_t()}
@type change_t :: {struct(), trail_t()}
@rugyoga
rugyoga / empty.ex
Created September 4, 2022 22:28
Is this value empty?
@empty [false, "", nil, [], 0, 0.0]
@spec empty?(any()) :: boolean()
def empty?(value) when value in @empty, do: true
def empty?(value) when is_list(value), do: Enum.all?(value, &empty?/1)
def empty?(value) when is_tuple(value), do: value |> Tuple.to_list() |> empty?()
def empty?(value) when is_struct(value), do: value |> Map.from_struct() |> empty?()
def empty?(value) when is_map(value), do: value |> Map.values() |> empty?()
def empty?(_), do: false
@rugyoga
rugyoga / delta_simple.ex
Created September 4, 2022 22:26
Take the delta of two simple velues
@spec delta_simple(path(), term(), term()) :: [delta()]
defp delta_simple(_path, a, b) when a == b, do: []
defp delta_simple(path, a, b) do
cond do
empty?(a) -> [{path, {:add, b}}]
empty?(b) -> [{path, {:delete, a}}]
true -> [{path, {:update, a, b}}]
end
end
@rugyoga
rugyoga / delta_tuple.ex
Created September 4, 2022 22:22
Take the delta of two tuples
@spec delta_tuple(path(), tuple(), tuple()) :: [delta()]
defp delta_tuple(path, a, b) when tuple_size(a) != tuple_size(b), do: delta_simple(path, a, b)
defp delta_tuple(path, a, b), do: delta_list(path, Tuple.to_list(a), Tuple.to_list(b))
@rugyoga
rugyoga / delta_list.ex
Last active September 4, 2022 23:13
Take delta of two lists
@spec delta_list(path(), list(), list()) :: [delta()]
defp delta_list(path, as, bs) when length(as) != length(bs), do: delta_simple(path, as, bs)
defp delta_list(path, as, bs) do
as
|> indexed_zip(bs)
|> Enum.flat_map(fn {i, {a, b}} -> delta([i | path], a, b) end)
end
def indexed_zip(as, bs, i \\ 0)
@rugyoga
rugyoga / delta_map.ex
Created September 4, 2022 21:56
Take the delta of two maps
@spec delta_map(path(), map(), map()) :: [delta()]
def delta_map(path, a, b) do
a_keys = MapSet.new(Map.keys(a))
b_keys = MapSet.new(Map.keys(b))
common = MapSet.intersection(a_keys, b_keys)
a_only = a_keys |> MapSet.difference(common)
b_only = b_keys |> MapSet.difference(common)
Enum.flat_map(common, fn key -> delta([key | path], a[key], b[key]) end) ++
Enum.flat_map(a_only, fn key -> [{[key | path], {:delete, a[key]}}] end) ++
@rugyoga
rugyoga / delta_struct.ex
Created September 4, 2022 21:49
Take delta of two structs
@spec delta_struct(path(), struct(), struct()) :: delta_spec()
def delta_struct(path, %a_s{} = a, %b_s{} = b) when a_s != b_s, do: delta_simple(path, a, b)
def delta_struct(path, a, b), do: delta_map(path, Map.from_struct(a), Map.from_struct(b))
@rugyoga
rugyoga / delta3.ex
Created September 4, 2022 21:47
Three argument version of delta.
@spec delta(path(), any(), any()) :: delta_spec()
defp delta(path, a, b) when is_struct(a) and is_struct(b), do: delta_struct(path, a, b)
defp delta(path, a, b) when is_map(a) and is_map(b), do: delta_map(path, a, b)
defp delta(path, a, b) when is_list(a) and is_list(b), do: delta_list(path, a, b)
defp delta(path, a, b) when is_tuple(a) and is_tuple(b), do: delta_tuple(path, a, b)
defp delta(path, a, b), do: delta_simple(path, a, b)
@rugyoga
rugyoga / delta.ex
Created September 4, 2022 21:42
Main entry point for difference engine
@spec delta(any(), any()) :: delta_spec()
def delta(a, b) do
[]
|> delta(a, b)
|> List.flatten()
|> Enum.map(fn {path, element} -> {Enum.reverse(path), element} end)
|> Enum.sort_by(fn {path, _} -> {length(path), path} end)
end