I’m doing a library where I want to require some module dynamically to call some macros (which will generate dynamic functions to the current module). The code could be something like the following snippet:
defmodule A do
defmacro foo() do
end
end
defmodule B do
defmacro foo() do
end
end
defmodule MetaMacro do
defmacro __using__(modules) do
quote bind_quoted: [modules: modules] do
Enum.each(modules, fn module ->
require module
module.foo()
end)
end
end
end
defmodule Bar do
use MetaMacro, [A, B]
end
And it generates the following error:
invalid argument for require, expected a compile time atom or alias, got: module
I tried different solutions, but nothing works as I expected. Any help? Opinions?
I just discovered this myself when looking at your issue. Elixir doesn’t allow you to call require with a variable. You can see that with this iex session.
iex(51)> defmodule Foo do
...(51)> end
iex(52)> foo = Foo
Foo
iex(53)> require foo
** (CompileError) iex:53: invalid argument for require, expected a compile time atom or alias, got: foo
iex(53)> require Foo
Foo
The below works for me:
defmodule A do
defmacro foo() do
IO.inspect(__MODULE__, label: inspect(__CALLER__.module))
end
end
defmodule B do
defmacro foo() do
IO.inspect(__MODULE__, label: inspect(__CALLER__.module))
end
end
defmodule MetaMacro do
defmacro __using__(modules) do
Enum.map(modules, fn module ->
quote do
require unquote(module)
unquote(module).foo()
end
end)
end
end
defmodule Bar do
use MetaMacro, [A, B]
end
defmodule MetaMacro do
defmacro __using__(modules) do
result = Enum.map(modules, fn module ->
quote do
require unquote(module)
unquote(module).foo()
end
end)
result |> Macro.expand(__ENV__) |> Macro.to_string() |> Code.format_string!() |> IO.puts()
result
end
end