Reputation: 15343
I know I must be something blindlingly obvious but for the life of me I can't see it. I'm trying to use a module attribute across modules. This is my first module:
defmodule PublishModuleAttributes do
Module.register_attribute(__MODULE__, :author_name, persist: true)
Module.register_attribute(__MODULE__, :author_email, persist: true)
@author_name "Onorio Catenacci"
@author_email "[email protected]" #Yes it's a fake email
end
and this is the module where I'm consuming the attributes:
defmodule UseModuleAttributes do
alias PublishModuleAttributes
defp get_author_name() do
[name] = PublishModuleAttributes.__info__(:attributes)[:author_name]
name
end
defp get_author_email() do
[email] = PublishModuleAttributes.__info__(:attributes)[:author_email]
email
end
@author_name get_author_name()
@author_email get_author_email()
def show_results() do
IO.inspect("Author name is #{@author_name}")
IO.inspect("Author email is #{author_email}")
end
end
But when I attempt to compile that second module I keep getting this message:
** (CompileError) lib/UseModuleAttributes.ex:14: undefined function get_author_name/0 (there is no such import)
I've tried changing it to def (as opposed to defp) and it makes no difference. I comment out the first (get_author_name) and then it has the same error on the second. If I comment out the two attribute lines I can get this to build and run under iex (with -S mix) and I can call the UseModuleAttributes.get_author_name
call directly and it works as expected. What super-obvious, simple thing have I missed here? I've looked at the documentation for using a function with an attribute and I didn't see anything I've done wrong but I must be missing something.
EDIT:
Just a bit more on this. It definitely seems to be related to the way module attributes are handled by the compiler because this also works:
defmodule UseModuleAttributes do
defp get_author_name() do
[name] = PublishModuleAttributes.__info__(:attributes)[:author_name]
name
end
defp get_author_email() do
[email] = PublishModuleAttributes.__info__(:attributes)[:author_email]
email
end
def show_results() do
IO.inspect("Author name is #{get_author_name()}")
IO.inspect("Author email is #{get_author_email()}")
end
end
Upvotes: 0
Views: 332
Reputation: 3029
Try defining these functions in a separate module and include this module before using them: module attributes must be resolved at compile time, module is a compilation "unit", so even if you define these functions lexically before referring to them in attributes (but within the same module) the compiler will not be able to resolve them.
Something like this might work (disclaimer: I'm not discussing is it good or not - just how to make it work based on your initial code):
defmodule PublishModuleAttributes do
Module.register_attribute(__MODULE__, :author_name, persist: true)
Module.register_attribute(__MODULE__, :author_email, persist: true)
@author_name "Onorio Catenacci"
@author_email "[email protected]" #Yes it's a fake email
end
defmodule MetaMethods do
def get_author_name() do
[name] = PublishModuleAttributes.__info__(:attributes)[:author_name]
name
end
def get_author_email() do
[email] = PublishModuleAttributes.__info__(:attributes)[:author_email]
email
end
end
defmodule UseModuleAttributes do
import PublishModuleAttributes
import MetaMethods
@author_name get_author_name()
@author_email get_author_email()
def show_results() do
IO.inspect("Author name is #{@author_name}")
IO.inspect("Author email is #{@author_email}")
end
end
Upvotes: 1