我想创建类似插件系统的东西:一堆动态加载/卸载的模块,添加我插入/删除它们。
这样的事情:
# plugin1.eex
defmodule MyApp.Plugins.Plugin1 do
def plugin_main_func(arg1, arg2) do
# some stuff
end
end
# plugin2.eex
defmodule MyApp.Plugins.Plugin2 do
def plugin_main_func(arg1, arg2) do
# some stuff 2
end
end
# plugin3.eex
defmodule MyApp.Plugins.Plugin3 do
def plugin_main_func(arg1, arg2) do
# some stuff 3
end
end
它们将具有相同的通用功能 - “plugin_main_func” - 每个插件的实现方式都不同。 在我的应用程序的某个地方,我会有这个:
plugins = load_plugins()
Enum.each plugins, &(&1.plugin_main_func(1, "fdafdsfds"))
然后我可以通过创建或删除适当的模块或文件来添加或删除插件。 没有必须在应用程序的某处硬编码/添加/删除他们的名称作为字符串列表以及。
我怎么能做到这一点?
更新
鉴于插件必须存在于MyApp.Plugins并且具有函数plugin_main_func
,我如何观察它们当前存在的插件列表?我只是想避免在某处硬编码他们的名字,加载或运行它们只有这两个条件足以找到所有的插件。
当我动态或半动态地找到所有这些内容时,我想以某种方式在每个插件中调用plugin_main_func
。不知道插件的数量和它们的确切名称。怎么样?
我可以在编译之前加载它们,而不是在运行时加载它们。
答案 0 :(得分:8)
回答标题中所述的问题:是的,可以在Elixir中动态加载/卸载模块。用例如编译代码Code.ensure_compiled/1
并使用Erlang的code
模块清除它:
:code.delete MyApp.Plugins.Plugin1
:code.purge MyApp.Plugins.Plugin1
回答整个问题:你做错了。 Elixir是一种编译语言,你正在滥用它。在运行时编译绝对不是Elixir的好处。
您只需要保留一个“已加载”插件列表,以伪造您尝试实现的行为。 load
会将模块名称添加到列表中,unload
会将其从列表中删除。就是这样。您不需要在运行时加载/卸载任何内容:它非常无效,危险且反惯用。如果您需要良好的运行时插件支持,请访问lua,ruby,python甚至javascript。
要在运行时获取已加载模块的列表,可以使用:
:application.get_key(:my_app, :modules)
您可以将插件限制在所需的命名空间(例如MyApp.Plugins
),并按该名称过滤列表。
答案 1 :(得分:2)
@mudasobwa提供的答案非常棒。我所能做的就是给你一个提示,告诉你如何在运行时实现你想要的东西(顺便说一下 - 你在尝试使用"多态性"在Elixir中,很好)。
假设加载了这些模块,我不会处理编译器加载/删除它们。考虑使用存储状态的任何东西 - public slots:
,GenServer,Agent等。
我会选择实现自己的GenServer,因为它可以灵活地自由地根据需要自定义它。想象一下:ets
在您的GenServer上创建load_plugins()
的情况,例如
call
您的GenServer以预定义的插件列表开始,您定义了API以在状态中添加/删除它们。绝对不需要在运行时加载/编译它们,即使它是可能的。
<强>更新强>
您始终可以使用def load_plugins do
GenServer.call(MyServer, :load_plugins)
end
检查模块是否已加载:
Code.ensure_loaded?