【问题标题】:Track Modules that `use` another module跟踪“使用”另一个模块的模块
【发布时间】:2017-06-28 16:52:48
【问题描述】:

我正在尝试在编译时跟踪 use 另一个模块的所有模块,并在运行时获取所有这些模块的列表:

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

并在运行时通过调用类似这样的方法获取它们:

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

我一直在尝试找到一种方法来实现这一点,但仍然完全不知道如何去做。通过this thread on the elixir-lang mailing list@josevalim 建议使用Protocols 来执行此操作。但是在为此苦苦挣扎了大约一个小时后,我也无法让它与协议一起使用。

我也一直在研究 Registry 模块,看看我是否可以使用它来完成此任务,但它看起来主要是为处理进程而设计的。

任何帮助将不胜感激。提前致谢!

【问题讨论】:

  • 您的使用宏是否将行为包含到目标模块中。需要对目标模块产生一些影响,因为 __using__ 宏可能不会返回任何效果并且无法检测到

标签: elixir


【解决方案1】:

如果您的基本模块使用行为,则可以使用以下解决方案。

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]

【讨论】:

  • 是的,这很好!但我唯一的问题是 :code.all_loaded() 并不总是列出所有模块(因为 VM 会延迟加载它们),我希望还有其他东西总是能给出相同的结果。
  • 很公平,这不是我想过的事情。但是您将无法找到未加载的模块。从本质上讲,他们不会“使用” BaseModule,因为他们的使用行不会被调用。
  • 确实如此,这仍然是一个非常好的解决方案。谢谢你的回答。
猜你喜欢
  • 2020-02-02
  • 2013-09-24
  • 1970-01-01
  • 2016-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 2022-07-11
相关资源
最近更新 更多