获取实现某个行为的所有模块的列表的最短方法是什么?
例如
defmodule X do
@callback foo()
end
defmodule A do
@behaviour X
def foo() do
"hello"
end
end
defmodule B do
@behaviour X
def foo() do
"world"
end
end
我想要一份
[A,B]
的列表。我尝试加载所有模块,然后使用关键属性behaviour
进行过滤,但我认为这会非常慢。还有其他办法吗?
不,没有更好的办法了。行为不会以任何方式索引,因为它们是编译时功能,用于检查是否已定义所有必需的回调函数。所以像这样的东西就已经是最好的了:
for {module, _} <- :code.all_loaded(),
X in (module.module_info(:attributes)[:behaviour] || []) do
module
end
继续@legoscia 提供的解决方案。 在某些情况下,您可能会为每个模块实现 1 个以上的行为,在这种情况下,如下所示:
for {module, _} <- :code.all_loaded(),
__MODULE__ in (module.module_info(:attributes)
|> Keyword.get_values(:behaviour)
|> List.flatten()) do
module
end
应该使用,因为关键字列表可能有重复的键,直接访问它们不会返回所需的结果。
谢谢@legoscia!
我找到了另一种方法,因为
:code.all_loaded()
不适合我。相反,我使用更具体的 :application.get_key/2
:
case :application.get_key(:my_app, :modules) do
{:ok, modules} ->
modules
|> Enum.filter(fn module -> (module.module_info(:attributes)[:behaviour] || []) |> Enum.member?(MyBehaviour) end)
error -> {:error, error}
end
这对我有用,希望对其他人也有帮助。
另一种方法是直接从
.beam
文件中提取模块属性。
defmodule BehaviourMapping do
@behaviour_mapping_key __MODULE__
@mapping_not_found :not_found
def recompute_mapping do
behaviours_by_module =
for {_, beam_file, _} <- :code.all_available(), reduce: %{} do
mapping ->
case :beam_lib.chunks(beam_file, [:attributes]) do
{:ok, {module, [attributes: attrs]}} ->
behaviours =
attrs
|> Keyword.get_values(:behaviour)
|> List.flatten()
Map.put(mapping, module, behaviours)
{:error, :beam_lib, _reason} ->
mapping
end
end
result =
for {module, behaviours} <- behaviours_by_module,
behaviour <- behaviours,
reduce: %{} do
mapping ->
Map.update(
mapping,
behaviour,
MapSet.new([module]),
&MapSet.put(&1, module)
)
end
:persistent_term.put(@behaviour_mapping_key, result)
result
end
def mapping do
case :persistent_term.get(@behaviour_mapping_key, @mapping_not_found) do
@mapping_not_found ->
recompute_mapping()
result ->
result
end
end
end
函数
BehaviourMapping.recompute_mapping/0
返回一个映射,其中键是行为模块,值是实现此行为的模块的MapSets。它还使用 persistent_term 来缓存计算结果。
函数
BehaviourMapping.mapping/0
使用尝试从持久项中获取结果或重新计算映射。