I’m developing some features for https://github.com/tony612/protobuf-elixir/, but I need a way to know what modules are protobuf modules and even want to store more information for them
A protobuf module is defined like this
defmodule Foo do
use Protobuf, syntax: :proto3
end
defmodule Bar do
use Protobuf, syntax: :proto3
end
I want to get the information in runtime/compile time like
A direct way I can think of is to define a function for each protobuf module, and call them when the program starts. I can use ets or persistent_term to build “all_messages”. But I can’t find a callback like this.
I don’t know if this can be implemented in compiling time, but it seems hard because we can’t make sure every protobuf module is compiled every time.
If you want to do it dynamically, you could try :code.all_loaded(). It returns a list of tuples, the first element is an atom of each module name, the second is a string of their location. You could stringify and check the first element for a constant module string. So instead of Foo you could do FooProtobuf and check for the Protobuf string. A bit hackey, but it’d work.
@tony612 Last time I was thinking about really similar case. The best solution I found is to define a separate module:
defmodule Foo do
use Protobuf, syntax: :proto3
end
defmodule Bar do
use Protobuf, syntax: :proto3
end
defmodule Example do
use Protobuf.Agent
add_proto(Foo)
add_proto(Bar)
end
Each add_proto/1 call would append value to module attribute which should be registered as accumulate. Based on such attribute use Protobuf.Agent should generate some code which would be executed on before_compile callback. Finally Example should be added to app supervisor tree.
This allows you to:
Define multiple modules like Example which may be helpful in case developer do not want to have all modules from all libraries.
Possibility to further generate __using__/1 macro in order to allow “extend” list of modules.
defmodule Example do
use Protobuf.Agent
use MyLib.ProtobufAgent
use MyOtherLib.ProtobufAgent
add_proto(Foo)
add_proto(Bar)
end
Of course there are ways to work on .beam files, persistent attributes and so on, but it adds way too many magic to your code and I would personally avoid such practices.
Just for sure instead of Protobuf.Agent you may have Protobuf.ETS and so on …
This is interesting. A better way could be checking if the protobuf modules have some functions like def __metadata__(:is_protobuf). And once we have all the protobuf modules, it will be easy to get all metadata, we just need to call their function, like def __meta__() do.