While everything suggested above is absolutely correct, I’m to answer your original question.
One does not technically need module attributes, nor hooks here. The issue is scopes and we have unquote fragments for that.
rule_ids_ is defines in the outer scope for rule_ids/0 call, therefore one needs to unquote it there. THe code below would work (I also fixed the issue with condition/3 head mistakenly falling under a match during the parse stage.)
defmodule Rules do
# Collect the rule ids at compile time
{_ast, rule_ids_} = File.read!(__ENV__.file)
|> Code.string_to_quoted()
|> Macro.traverse([],
fn
{:def, _, [{:condition, _, [rule_id, {:v, _, nil}, {:site, _, nil}]} | _]} = x, acc
when not is_tuple(rule_id) ->
{x, [rule_id | acc]}
other, acc ->
{other, acc}
end,
fn other, acc -> {other, acc} end
)
# print it out and see how it goes, this line is to be removed
|> tap(& &1 |> Macro.to_string() |> IO.puts())
# unquoting from the outer scope
# HERE ⇓⇓⇓⇓⇓⇓⇓
def rule_ids(), do: unquote(rule_ids_)
def condition(rule_id, v, site)
def condition(1018, v, site) do
v[site][7018] < 50 && v[site][7019] < 50 && v[site][7020] < 50
end
def condition(1017, v, site) do
v[site][7027] > 42
end
# ...
end


















