Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically load/unload modules in Elixir, something like a plugin system

Tags:

elixir

I want to create something like a plugin system: a bunch of modules which get loaded/unloaded dynamically, add I insert/remove them.

Something like this:

# 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

They'll have the same common function - "plugin_main_func" - which implementation will be different for each plugin. And somewhere in my app I'll have this:

plugins = load_plugins()
Enum.each plugins, &(&1.plugin_main_func(1, "fdafdsfds"))

I then be able to add or remove plugins by creating or removing appropriate modules or files. Without having to hard-code/add/remove their names somewhere in an application as a list of strings as well.

How can I achive this?

Update:

Given that a plugin must live in MyApp.Plugins and have a function plugin_main_func, how can I observe a list of plugins they are currently present? I mere want to avoid hard-coding their names somewhere and load or run them only having these 2 conditions which are sufficient for finding all the plugins.

When I find all of them, dynamically or semi-dynamically, I want to somehow be able to call plugin_main_func in each plugin. Without knowing the number of plugins and their exact names. How?

I'm fine with loading them before compilation, not exactly at runtime.

like image 418
Rodi Avatar asked Dec 24 '22 12:12

Rodi


1 Answers

Answering the question, stated in the title: yes, it is possible to dynamically load/unload modules in Elixir. Compile code with e.g. Code.ensure_compiled/1 and purge it with Erlang’s code module:

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

Answering the whole question: you are doing it wrong. Elixir is a compiled language and you are abusing it. Compilation in a runtime is definitely not what Elixir is good for.

All you need is to keep a list of “loaded” plugins to fake the behavior you are trying to achieve. load would add a module name to a list and unload would remove it from there. And that is it. You do not need to load/unload anything in a runtime: it’s extremely ineffective, dangerous and counter-idiomatic. If you need good runtime plugin support—go for lua, ruby, python or even javascript.


To get the list of loaded modules in a runtime, one might use:

:application.get_key(:my_app, :modules)

You might restrict plugins to the desired namespace (e.g. MyApp.Plugins) and filter the list by that name.

like image 65
Aleksei Matiushkin Avatar answered Jan 17 '23 15:01

Aleksei Matiushkin