Hello, I have a CRUD macro that I use everywhere I need to create a CRUD query, but I am always forced to load it in the middle of a function to be able to get parameters like this:
def create(attrs, allowed_fields) do
crud_add(attrs, allowed_fields)
end
is there a way to load it like this
defmodule User do
crud_add(attrs, allowed_fields)
....
end
but I have to use it like this: User.crud_add(%{name: "test", [:name])
You can define the function in a macro so if I’ve understood correctly, a small change to your macros will suffice (not tested):
defmacro crud_add(allowed_fields) do
quote do
def create(attrs) do
module_selected = Keyword.get(@interface_module, :module)
error_atom = Keyword.get(@interface_module, :error_atom)
repo = Keyword.get(@interface_module, :repo)
add = module_selected.__struct__
|> module_selected.changeset(Map.take(attrs, unquote(allowed_fields)))
|> repo.insert()
case add do
{:ok, data} -> {:ok, :add, error_atom, data}
{:error, changeset} -> {:error, :add, error_atom, changeset}
end
end
end
end
The in a module:
defmodule MyModule do
use MishkaDatabase.CRUD,
module: YOURschemaMODULE,
error_atom: :your_error_tag,
repo: Your.Repo
crud_add(attrs)
end
The idea is that the allowed_attrs is “baked in” to the generated function. But attrs is supplied at runtime and therefore doesn’t look like it should be a parameter to the macro.
In general, I would decompose this further in the interests of clarity and better runtime stack traces. Macros should delegate to functions where possible. So:
defmodule MishkaDatabase.CRUD do
defmacro crud_add(allowed_fields) do
quote do
def create(attrs) do
module_selected = Keyword.get(@interface_module, :module)
error_atom = Keyword.get(@interface_module, :error_atom)
repo = Keyword.get(@interface_module, :repo)
MishkaDatabase.CRUD.do_create(module_selected, repo, error_atom, attrs, unquote(allowed_fields))
end
end
end
def do_create(module_selected, repo, error_atom, attrs, allowed_fields) do
add = module_selected.__struct__
|> module_selected.changeset(Map.take(attrs, allowed_fields))
|> repo.insert()
case add do
{:ok, data} -> {:ok, :add, error_atom, data}
{:error, changeset} -> {:error, :add, error_atom, changeset}
end
end
end
Hello, @kip
First, thank you for improving my code, and thank you again for your suggestion
I wanted to separate map dropper out of my macro, but I had to decide to create a full tools or forced the developers to drop parameters of a map which do not want themselves, in my test it works as I understand your suggestion!!