Elixir 查找采用行为的所有模块
是否有可能找到采用了特定行为的每个已加载模块 我正在构建一个非常简单的聊天机器人,我想制作一些很酷的命令,但要实现这一点,我需要一种实现多个命令的方法,最好不用硬编码 每个命令都是一个函数,它接受三个参数(message、author、chat\u channel\u ref)并返回true或false,无论它是否匹配并执行某些操作Elixir 查找采用行为的所有模块,elixir,Elixir,是否有可能找到采用了特定行为的每个已加载模块 我正在构建一个非常简单的聊天机器人,我想制作一些很酷的命令,但要实现这一点,我需要一种实现多个命令的方法,最好不用硬编码 每个命令都是一个函数,它接受三个参数(message、author、chat\u channel\u ref)并返回true或false,无论它是否匹配并执行某些操作 当我浏览Elixir教程时,我发现如果我能找到所有采用它们的模块,它可能会很好地满足我的需要。你们以前有人做过吗?我还能做什么?我还考虑过“使用”(在使用中,我会执
当我浏览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模块属性。功能仍然存在,只是删除了长生不老药包装,因为它没有添加任何有用的东西来证明它的合理性。