Sheshank Kodam
Sheshank Kodam

Reputation: 515

How to load config file based on command line args in Elixir

How to overwrite config in Elixir using command line flag provided? For example, the application is started by running:

./my_app --mode=mode1 or ./my_app --mode=mode2

Based on the mode provided, I would like to override config.exs with mode1.exs or mode2.exs as shown below

use Mix.Config

# Configures the endpoint
config :my_app,
  env: Mix.env

import_config "#{Mix.env}.exs"
import_config "mode1.exs" or import_config "mode2.exs"

Upvotes: 0

Views: 1118

Answers (3)

Sheharyar
Sheharyar

Reputation: 75840

Using Env Variables at Compile-Time

The problem with having different configs based on different command-line arguments is that Elixir applications are compiled, so once you package the application it will contain the configs of only the mode specified at compile time.

If that's not a problem and you still want to use separate configs, it is better to use Environment Variables instead of command-line flags.

use Mix.Config

# Get the Application Mode
default_mode = "1"
app_mode = System.get_env("APP_MODE") || default_mode
mode_config = "mode#{app_mode}.exs"

# Load external configs
import_config("#{Mix.env}.exs")
import_config(mode_config)

Now just pass the mode via environment variable:

$ APP_MODE=1 mix run

Upvotes: 2

Sheharyar
Sheharyar

Reputation: 75840

Switching Configs Dynamically

Like I mentioned in my other answer, elixir applications are compiled so once the application is packaged, it will only contain configs of that mode. A better solution would be to keep configs for all modes together, and dynamically load appropriate configs in your application.

Here's what you config.exs file could look like:

use Mix.Config

config :my_app, :app_modes,
  default: :mode_1

config :my_app, :mode_1,
  x: 1, y: 2, z: 3

config :my_app, :mode_2,
  x: 6, y: 7, z: 8

And you could use a custom ModeConfig module to load the mode configs:

defmodule MyApp.ModeConfig do
  @default_mode Application.get_env(:my_app, :app_modes)[:default_mode]

  # Get App Mode
  def mode do
    passed_mode = System.get_env("APP_MODE")
    # or you can use OptionParser for command-line flags

    String.to_atom(passed_mode) || @default_mode
  end

  def get,      do: Application.get_env(:my_app, mode())
  def get(key), do: get()[key]
end

You can set (and fetch) modes in two ways now:

And appropriate configs can be loaded using the custom Config module:

# App started in Mode 2
MyApp.ModeConfig.get(:x)       # => 6

# App started in Mode 1
MyApp.ModeConfig.get(:y)       # => 2

Note: If your application gets more complicated (OTP & Processes), you could even have different "adapters" corresponding to each mode, and switch to the appropriate one during bootup of your application supervision tree.

Upvotes: 0

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

To parse command line arguments one uses OptionParser.

To overwrite the values from config file one should use Application.put_env/4, since the configuration file is loaded and processed during the compile time and even more, Mix.Config as well as the whole Mix application does not exist in production. So whether you choose this approach, you are to either bring mix application to prod (not recommended and strongly discouraged,) or to parse your modeN files yourself and update application environment manually.

That topic was already largely discussed in Elixir community and the core team pretty fine understands the drawbacks of using compile-time configs.

The best solution at the moment (until we are given with the proper one,) would be to use system environment instead of configs and/or introduce your own JSON/YAML configs.

Upvotes: 0

Related Questions