Yes, it’s because __CALLER__
is not yet compiled. You can create a MyApp.Queries
module and pass MyApp.MySchema
as second argument instead.
I’m writing it from memory, so I could make a mistake, but it should work:
defmodule Example do
defmacro defquery(type, module_ast) do
module = Macro.expand(module_ast, __ENV__)
primary_keys = module.__schema__(:primary_key)
module_snake_case = module |> Module.split() |> List.last() |> Macro.underscore()
call_args = Enum.map(primary_keys, &Macro.var(&1, module))
func_name = :"#{type}_#{module_snake_case}"
repo_args = Enum.map(call_args, &{elem(&1, 0), &1})
repo_func = get_repo_func(type)
quote do
def unquote(func_name)(unquote_splicing(call_args)) do
apply(MyApp.Repo, unquote(repo_func), [unquote(module), unquote(repo_args)])
end
end
end
defp get_repo_func(:get), do: :get_by
end
defmodule MyApp.Queries do
require Example
Example.defquery(:get, MyApp.MySchema)
end
MyApp.Queries.get_my_schema(…)
This is fully generic macro. Feel free to edit it as you wish.
It’s also possible to change this code to work inside MyApp.MySchema
, but that’s not recommend as such modules are designed to handle different things (field definitions, changesets etc.) and business logic should be inside context modules. That was already well described on forum.
For me it looks like that author want to have instead: Schema.get(pk1, pk2)
. For me it does not looks that bad, because in Repo.get_by/2
you don’t know which keys are primary (h
helper in iex
and on documentation level). Here you could see them in generated documentation.
The only problem is that author probably want to generate such functions inside modules with schema definition. Instead I proposed example with same functions, but in context module.
In order words instead of:
MyApp.Repo.get_by(MyApp.Comment, [id: comment_id])
MyApp.Repo.get_by(MyApp.PostComment, [comment_id: comment_id, post_id: post_id])
MyApp.Repo.get_by(MyApp.Post, [id: post_id])
author wants:
MyApp.Comment.get(comment_id)
MyApp.Post.get(post_id)
MyApp.PostComment.get(comment_id, post_id)
and my macro allows to call like:
MyApp.Queries.get_comment(comment_id)
MyApp.Queries.get_post(post_id)
MyApp.Queries.get_post_comment(comment_id, post_id)