Elixir 查找采用行为的所有模块

Elixir 查找采用行为的所有模块,elixir,Elixir,是否有可能找到采用了特定行为的每个已加载模块 我正在构建一个非常简单的聊天机器人,我想制作一些很酷的命令,但要实现这一点,我需要一种实现多个命令的方法,最好不用硬编码 每个命令都是一个函数,它接受三个参数(message、author、chat\u channel\u ref)并返回true或false,无论它是否匹配并执行某些操作 当我浏览Elixir教程时,我发现如果我能找到所有采用它们的模块,它可能会很好地满足我的需要。你们以前有人做过吗?我还能做什么?我还考虑过“使用”(在使用中,我会执

是否有可能找到采用了特定行为的每个已加载模块

我正在构建一个非常简单的聊天机器人,我想制作一些很酷的命令,但要实现这一点,我需要一种实现多个命令的方法,最好不用硬编码

每个命令都是一个函数,它接受三个参数(message、author、chat\u channel\u ref)并返回true或false,无论它是否匹配并执行某些操作


当我浏览Elixir教程时,我发现如果我能找到所有采用它们的模块,它可能会很好地满足我的需要。你们以前有人做过吗?我还能做什么?我还考虑过“使用”(在使用中,我会执行代码将当前模块添加到代理持有的列表中)。

这是我的exrm项目的一个摘录,它基本上就是这样做的:它会找到任何实现插件行为的模块:

  @doc """
  Loads all plugins in all code paths.
  """
  @spec load_all() :: [] | [atom]
  def load_all, do: get_plugins(ReleaseManager.Plugin)

  # Loads all modules that extend a given module in the current code path.
  @spec get_plugins(atom) :: [] | [atom]
  defp get_plugins(plugin_type) when is_atom(plugin_type) do
    available_modules(plugin_type) |> Enum.reduce([], &load_plugin/2)
  end

  defp load_plugin(module, modules) do
    if Code.ensure_loaded?(module), do: [module | modules], else: modules
  end

  defp available_modules(plugin_type) do
    # Ensure the current projects code path is loaded
    Mix.Task.run("loadpaths", [])
    # Fetch all .beam files
    Path.wildcard(Path.join([Mix.Project.build_path, "**/ebin/**/*.beam"]))
    # Parse the BEAM for behaviour implementations
    |> Stream.map(fn path ->
      {:ok, {mod, chunks}} = :beam_lib.chunks('#{path}', [:attributes])
      {mod, get_in(chunks, [:attributes, :behaviour])}
    end)
    # Filter out behaviours we don't care about and duplicates
    |> Stream.filter(fn {_mod, behaviours} -> is_list(behaviours) && plugin_type in behaviours end)
    |> Enum.uniq
    |> Enum.map(fn {module, _} -> module end)
  end

另一个考虑与行为相似的选择是。制定一个协议,然后每个新命令都必须实现该协议

我编写了一个库,只有在插件支持特定的 函数调用。行为在长生不老药中被贬低,@behavior在Erlang方式中仍然被支持,但是买给你的东西比你预期的要少。(基本上只有在缺少函数签名时才会发出编译时警告)


所以它只是运行时?您也可以在编译时运行相同的代码,尽管可能有一些警告(我不需要这样做)-在exrm的情况下,这是不必要的。您还可以在第一次运行时缓存结果,方法是将结果存储在应用程序环境中。这实际上取决于需要调用它的频率。我在运行时和编译时尝试了这种方法。在这两种情况下,我都遇到了dev/test和prod环境之间的问题。对于我的用例,我决定使用一个简单的配置,因此当我编写一个新模块时,我只需将它添加到
config/config.ex
中,并使用
Application.get_env
阅读列表。您将拥有多少个?也许在源代码中的某个地方保存一个列表更简单,也许在一个主管规范中,然后每个都在一个单独的进程中运行。是的,我考虑过。但是协议是按照typr实现的(例如:list等),每个函数的性质都是相同的。这太糟糕了,它被弃用了。Elixir中的行为“macro”被弃用,而只使用@behavior模块属性。功能仍然存在,只是删除了长生不老药包装,因为它没有添加任何有用的东西来证明它的合理性。