This can be handled through an argument to the supervisor. For example, Ecto's backend supervisor takes an argument called adapter
to specify which kind of database should be used:
# https://github.com/elixir-lang/ecto/blob/364d34bb135e2256fd48327464ada7f7fa2976f9/lib/ecto/repo/backend.ex#L13-L16
def start_link(repo, adapter) do
# Start Ecto, depending on the supplied <repo> and <adapter>
end
You could do the same in your application, probably a single argument to start_link
will be enough – let's call it backend
# my_app/lib/my_app/supervisor.ex
defmodule MyApp.Supervisor do
def start_link(backend) do
# use <backend> as you need to in here,
# it will contain the module that is
# specified in the configuration file.
end
end
Now, you can of course set that argument dynamically when you spin up your application, based on a configuration file:
# my_app/lib/my_app.ex
defmodule MyApp do
use Application
def start(_type, _args) do
MyApp.Supervisor.start_link(backend)
end
# get backend from configuration
def backend do
# ???
end
end
Now, the only piece that is missing is how to get the backend from a configuration file. There's no single answer to that because there are multiple ways of doing this.
Mix Configuration
You can simply use the existing Mix configuration, but it has the downside that you need to recompile the application every time the configuration changes:
# my_app/config/config.exs
use Mix.Config
config :my_app, backend: MyApp.SpecificBackend
Then adjust your application to read in the specified backend:
# my_app/lib/my_app.ex
defmodule MyApp do
use Application
def start(_type, _args) do
# same as above ...
end
def backend do
Application.get_env(:my_app, :backend)
end
end
Roll your own
You could also implement your own config file. I am not going into detail here, but this is the rough idea:
- Keep a config file somewhere
- Read an parse it in Elixir
- Convert the string to a module name with
String.to_existing_atom("Elixir.#{module_name}")
- This will raise an error if the atom (thus the module name) does not exist
- use it in your
def backend
function
Use an existing run-time configuration library
Basically a glorified version of the previous solution. Googling around a bit I found a library called Conform. It looks interesting but I can'T make any promises because I've never used it myself.