Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Track Modules that `use` another module

Tags:

elixir

I'm trying to keep track of all modules that use another module at compile-time and get a list of all these module at runtime:

defmodule BaseModule do
  defmacro __using__(_) do
    quote do
      # common behaviour
    end
  end
end


defmodule A do
  use BaseModule
end

defmodule B do
  use BaseModule
end

defmodule C do
  use BaseModule
end

And get them at runtime by calling something like this:

BaseModule.children()
#=> [A, B, C]

I have been trying to find a way to accomplish this but still have absolutely no idea on how to do it. Going through this thread on the elixir-lang mailing list, @josevalim recommends using Protocols to do this. But after struggling this for about an hour, I can't get it to work with Protocols either.

I've also been looking in to the Registry module to see if I can accomplish this using that, but it looks like it's designed to work with processes mainly.

Any help would be highly appreciated. Thanks in advance!

like image 508
Sheharyar Avatar asked Sep 06 '25 03:09

Sheharyar


1 Answers

If your base module uses a behaviour then the following solution is possible.

defmodule BaseModule do
  @callback foo() :: any()

  defmacro __using__(_opts) do
    quote do
      # Fairly standard to use a behaviour and gives us something to detect later.
      @behaviour unquote(__MODULE__)

      # Any other setup required
    end
  end

  def children() do
    (for {module, _} <- :code.all_loaded(), do: module)
    |> Enum.filter(&is_child?/1)
  end

  def is_child?(module) do
    module.module_info[:attributes]
    |> Keyword.get(:behaviour, [])
    |> Enum.member?(__MODULE__)
  end
end

defmodule A do
  use BaseModule
end

BaseModule.children()
#=> [A]
like image 181
Peter Saxton Avatar answered Sep 07 '25 23:09

Peter Saxton