Created
October 22, 2016 21:32
-
-
Save noam87/cd8f458bee96c161fd62e3ae13e4cef6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule UdioDb.Hooks do | |
@moduledoc """ | |
This module macro defines the following functions within the model, | |
delegating to `Repo`, but allowing us to add `after_*` hooks, | |
which are no longer supported in Ecto | |
(http://blog.plataformatec.com.br/2015/12/ecto-v1-1-released-and-ecto-v2-0-plans/) | |
delete, | |
delete!, | |
delete_all, | |
insert, | |
insert!, | |
insert_all, | |
insert_or_update, | |
insert_or_update!, | |
update, | |
update!, | |
update_all | |
## Hooks | |
`after_*` will perform action after transaction is done. | |
`atomic_after_*` will perform action within the same transaction, at the end. | |
When using `atomic_after`, make sure to return the resulting struct in | |
the success case, in its tuple format `{:ok, res}` or `{:error, error}`. | |
The return value of the transaction is what will be returned by the action. | |
## Usage | |
Will print console message after updating: | |
**NOTE:** Remember to cover general case after. | |
defmodule MyModel do | |
use UdioDb.Model # which calls `use UdioDb.Overrides` | |
defp after_update(result, changeset) do | |
IO.puts("updating!") | |
end | |
defp atomic_after_update(r = {:error, _}, _), do: r | |
defp atomic_after_update(res, ch) do | |
case OtherModel.get!(1).name do | |
"bob" -> Repo.rollback(:cant_update_if_name_is_bob) | |
_ -> res | |
end | |
end | |
end | |
MyModel.update!(ch) | |
# -> "updating!" | |
%MyModel{...} | |
""" | |
defmacro __using__(_) do | |
actions = [:delete, | |
:delete!, | |
:delete_all, | |
:insert, | |
:insert!, | |
:insert_all, | |
:insert_or_update, | |
:insert_or_update!, | |
:update, | |
:update!, | |
:update_all] | |
# For the love of God, do not touch until this is tested! | |
quote do | |
alias UdioDb.Repo | |
unquote do | |
Enum.map(actions, fn action -> | |
quote do | |
def unquote(action)(changeset, opts \\ []) do | |
fwd = fn -> | |
res = Repo.unquote(action)(changeset, opts) | |
after_res = unquote(:"atomic_after_#{action}")(res, changeset) | |
# Handle whether the action is with bang for or not | |
unquote do | |
if String.last(Atom.to_string(action)) == "!" do | |
quote do | |
transaction_result = | |
case after_res do | |
{:ok, res} -> res | |
{:error, error} -> raise error | |
end | |
end | |
else | |
quote do | |
transaction_result = after_res | |
end | |
end | |
end | |
transaction_result | |
end | |
result = | |
case Repo.transaction(fwd) do | |
# Covers e.g insert / update | |
{:ok, {:ok, res}} -> {:ok, res} | |
# Just in case | |
{:ok, {:error, error}} -> {:error, error} | |
# Covers insert! update! which return struct | |
{:ok, res} -> res | |
# Covers rollback which returns error | |
{:error, error} -> {:error, error} | |
end | |
unquote(:"after_#{action}")(result, changeset) | |
result | |
end | |
end | |
end) | |
end | |
unquote do | |
Enum.map(actions, fn action -> | |
quote do | |
defp unquote(:"after_#{action}")(_res, _original_struct), do: nil | |
end | |
end) | |
end | |
unquote do | |
Enum.map(actions, fn action -> | |
quote do | |
unquote do | |
if String.last(Atom.to_string(action)) == "!" do | |
quote do | |
defp unquote(:"atomic_after_#{action}")(res, _) do | |
{:ok, res} | |
end | |
end | |
else | |
quote do | |
defp unquote(:"atomic_after_#{action}")(res, _), do: res | |
end | |
end | |
end | |
end | |
end) | |
end | |
defoverridable [after_delete: 2, | |
atomic_after_delete: 2, | |
after_delete!: 2, | |
atomic_after_delete!: 2, | |
after_delete_all: 2, | |
atomic_after_delete_all: 2, | |
after_insert: 2, | |
atomic_after_insert: 2, | |
after_insert!: 2, | |
atomic_after_insert!: 2, | |
after_insert_all: 2, | |
atomic_after_insert_all: 2, | |
after_insert_or_update: 2, | |
atomic_after_insert_or_update: 2, | |
after_insert_or_update!: 2, | |
atomic_after_insert_or_update!: 2, | |
after_update: 2, | |
atomic_after_update: 2, | |
after_update!: 2, | |
atomic_after_update!: 2, | |
after_update_all: 2, | |
atomic_after_update_all: 2] | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
dumb derp at line 133