Moved the map to be an argument to using which seems cleaner
Code.eval_quoted the opts because of the way maps are passed to the macro as AST
Unquoted the prop since that comes from the outer context
defmodule MyApp do
defmacro __using__(opts) do
for prop <- opts do
{prop, _} = Code.eval_quoted(prop)
quote do
def get_id_name(unquote(prop[:id])) do
unquote(prop[:name])
end
end
end
end
end
defmodule MyModule do
use MyApp, [
%{name: "first_value", id: 0x01},
%{name: "second_value", id: 0x02},
]
end
The problem here is I also have other stuff in quote block and looking for an option to keep it while doing the for each. so by looking at your answer I came up with
defmodule MyApp do
defmacro __using__(opts) do
macro_props = for prop <- opts do
{prop, _} = Code.eval_quoted(prop)
quote do
def get_id_name(unquote(prop[:id])) do
unquote(prop[:name])
end
end
end
rest = quote do
def other_func do
:ok
end
end
[rest] ++ macro_props
end
end
Does it make sense or there is a better way to do that?
A better way would be, I think, to import the other functions so as to keep the macro code as concise as possible, vis:
defmodule MyApp do
defmacro __using__(opts) do
for prop <- opts do
{prop, _} = Code.eval_quoted(prop)
quote do
def get_id_name(unquote(prop[:id])) do
unquote(prop[:name])
end
end
end
end
def other_function(thing) do
IO.puts thing
end
end
defmodule MyModule do
import MyApp
use MyApp, [
%{name: "first_value", id: 0x01},
%{name: "second_value", id: 0x02},
]
end
If you want to omit the import you can have the macro do that too:
defmodule MyApp do
defmacro __using__(opts) do
imp = quote do
import MyApp
end
funs = for prop <- opts do
{prop, _} = Code.eval_quoted(prop)
quote do
def get_id_name(unquote(prop[:id])) do
unquote(prop[:name])
end
end
end
[imp | funs]
end
def other_function(thing) do
IO.puts thing
end
end
defmodule MyModule do
use MyApp, [
%{name: "first_value", id: 0x01},
%{name: "second_value", id: 0x02},
]
def test do
other_function("x")
end
end