在Elixir中动态加载/卸载模块,类似于插件系统



我想创建一个类似插件系统的东西:一堆动态加载/卸载的模块,添加、插入/删除它们。

类似这样的东西:

# 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,我如何观察它们当前存在的插件列表?我只是想避免在某个地方对它们的名称进行硬编码,并在只有这2个条件的情况下加载或运行它们,这两个条件足以找到所有插件。

当我找到所有这些插件时,无论是动态的还是半动态的,我都希望能够在每个插件中调用plugin_main_func。不知道插件的数量和它们的确切名称。怎样

我可以在编译前加载它们,而不是在运行时加载。

回答标题中的问题:是的,可以在Elixir中动态加载/卸载模块。使用例如Code.ensure_compiled/1编译代码,并使用Erlang的code模块清除代码

:code.delete MyApp.Plugins.Plugin1
:code.purge MyApp.Plugins.Plugin1

回答整个问题:你做错了。Elixir是一种编译语言,你在滥用它。运行时编译绝对不是Elixir的优点。

你所需要的只是保留一个"加载"的插件列表,以伪造你试图实现的行为。CCD_ 5会将模块名称添加到列表中,而CCD_。就是这样。你不需要在运行时加载/卸载任何东西:这是非常无效、危险和反惯用的。如果您需要良好的运行时插件支持,请选择lua、ruby、python甚至javascript。


要在运行时中获得加载模块的列表,可以使用:

:application.get_key(:my_app, :modules)

您可以将插件限制为所需的命名空间(例如MyApp.Plugins),并按该名称过滤列表。

@mudasobwa提供的答案真的很棒。我所能做的就是给你一个提示,告诉你如何在运行时实现你想要的(顺便说一句,你正试图在Elixir中使用"多态性",很好)。

假设这些模块已经加载,我就不会用编译器来加载/删除它们。考虑使用任何存储状态的东西——:ets、GenServer、Agent等。

我会实现我自己的GenServer,因为它提供了灵活性和自由度,可以随心所欲地定制它。想象一下load_plugins()在GenServer上制作call的情况,例如

def load_plugins do
GenServer.call(MyServer, :load_plugins)
end

您的GenServer从预定义的插件列表开始,并定义API以在状态中添加/删除它们。绝对没有必要在运行时加载/编译它们,即使这是可能的。

更新

您可以随时使用Code.ensure_loaded?检查模块是否已加载:

iex(1)> Code.ensure_loaded?(MyApp.Repo)
true
iex(2)> Code.ensure_loaded?(MyAppppppppp.Reeeeppo)
false

最新更新