查找采用行为的所有模块

时间:2016-04-05 17:47:17

标签: elixir

是否可以找到已采用某种行为的每个已加载模块?

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

每个命令都是一个函数,它接受三个参数(message,author,chat_channel_ref)并返回true或false,无论它是匹配还是做某事。

当我浏览Elixir教程时,我发现Behaviors如果能够找到所有采用它们的模块,那么它可能非常适合我的需求。你以前有没有人这样做过?我还能做什么?我还想过“使用”(在使用中我会执行代码将当前模块添加到代理所持有的列表中)。

3 个答案:

答案 0 :(得分:8)

这是我的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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

我写了一个只在加载插件时才加载插件的库 功能调用。行为在Elixir中被折旧,仍然支持Erlang方式的@behaviour,但是比你想象的要少得多。 (如果缺少函数签名,基本上只有编译时警告)。

https://github.com/bbense/pluginator