如何使用提供的命令行标志覆盖Elixir中的配置?例如,通过运行以下命令启动应用程序:
./my_app --mode=mode1 or ./my_app --mode=mode2
根据提供的模式,我想用mode1.exs
或mode2.exs
覆盖config.exs
,如下所示
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"
在编译时使用 env 变量
基于不同的命令行参数使用不同配置的问题在于Elixir应用程序是编译的,因此一旦打包应用程序,它将仅包含编译时指定的模式的配置。
如果这不是问题,并且您仍然希望使用单独的配置,则最好使用环境变量而不是命令行标志。
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)
现在只需通过环境变量传递模式:
$ APP_MODE=1 mix run
要解析命令行参数,请使用OptionParser
.
要覆盖配置文件中的值,应该使用Application.put_env/4
,因为配置文件是在编译时加载和处理的,甚至更多,Mix.Config
以及整个Mix
应用程序在生产中不存在。因此,无论选择此方法,您要么mix
应用程序prod
(不推荐,强烈建议不要这样做(,要么自己解析modeN
文件并手动更新应用程序环境。
这个话题已经在Elixir社区中得到了广泛的讨论,核心团队非常了解使用编译时配置的缺点。
目前最好的解决方案(直到我们得到合适的解决方案(是使用系统环境而不是配置和/或引入您自己的 JSON/YAML 配置。
动态切换配置
就像我在另一个答案中提到的,elixir 应用程序是编译的,所以一旦应用程序被打包,它将只包含该模式的配置。更好的解决方案是将所有模式的配置放在一起,并在应用程序中动态加载适当的配置。
以下是config.exs
文件的外观:
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
您可以使用自定义ModeConfig
模块来加载模式配置:
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
您现在可以通过两种方式设置(和获取(模式:
- 命令行参数:使用
OptionParser.parse/2
- 环境变量:使用
System.get_env/1
并且可以使用自定义配置模块加载适当的配置:
# App started in Mode 2
MyApp.ModeConfig.get(:x) # => 6
# App started in Mode 1
MyApp.ModeConfig.get(:y) # => 2
注意:如果您的应用程序变得更加复杂(OTP 和进程(,您甚至可以为每个模式使用不同的"适配器",并在应用程序监督树启动期间切换到相应的适配器。