Confex
This is helper module that provides a nice way to read environment configuration at runtime. It's inspired by Phoenix {:system, value}
definition for HTTP port.
Installation
It's available on hex.pm and can be installed as project dependency:
- Add
confex
to your list of dependencies inmix.exs
:
def deps do
[{:confex, "~> 2.0.0"}]
end
- Ensure
confex
is started before your application:
def application do
[applications: [:confex]]
end
Usage
- Defining configurations
Define your configuration in config.ex of your application.
config :ap_cfpredictor, AssetProcessor.AMQP.Producer,
queue: [
name: {:system, "OUT_QUEUE_NAME", "MyQueueOut"},
error_name: {:system, "OUT_ERROR_QUEUE_NAME", "MyQueueOut.Errors"},
routing_key: {:system, "OUT_ROUTING_KEY", ""},
durable: {:system, "OUT_DURABLE", false},
port: {:system, :integer, "OUT_PORT", 1234},
],
List of supported formats:
var
- any bare values will be left as-is.{:system, "ENV_NAME", default}
- read string from system ENV, returndefault
if it's nil.{:system, "ENV_NAME"}
- read string from system ENV, returnsnil
if environment variables doesn't exist.{:system, :string, "ENV_NAME", default}
- same as{:system, "ENV_NAME", default}
.{:system, :string, "ENV_NAME"}
- same as{:system, "ENV_NAME"}
.{:system, :integer, "ENV_NAME", default}
- same as{:system, "ENV_NAME", default}
, but will convert value to integer if it's notnil
. Default value type will not be changed.{:system, :integer, "ENV_NAME"}
- same as{:system, :integer, "ENV_NAME", nil}
.{:system, :boolean, "ENV_NAME", default}
- same as{:system, "ENV_NAME", default}
, but will convert value to boolean if it's notnil
. Default value type will not be changed.{:system, :boolean, "ENV_NAME"}
- same as{:system, :boolean, "ENV_NAME", nil}
.{:system, :atom, "ENV_NAME"}
{:system, :atom, "ENV_NAME", :default}
{:system, :module, "ENV_NAME"}
{:system, :module, "ENV_NAME", Default}
{:system, :list, "ENV_NAME"}
- same as{:system, :list, "ENV_NAME", nil}
.{:system, :list, "ENV_NAME", Default}
- same as{:system, "ENV_NAME", default}
, but will convert value to list if it's notnil
, splitting at commas. Default value type will not be changed.
- Reading configuration
Read string values:
iex> Confex.get(:myapp, MyKey)
"abc"
Read integer values:
Confex.get(:myapp, MyIntKey)
123
Read map values:
Confex.get_map(:myapp, MyIntKey)
[a: 123, b: "abc"]
- Using macros
Confex is supplied with helper macros that allow to attach configuration to specific modules of your application.
defmodule Connection do
use Confex, otp_app: :myapp
end
Connection
in this case will read configuration from app :myapp
with key Connection
. Also it will provide helper function config/0
that will return values at run-time.
- Configuration validation
Sometimes you want to validate configuration, for this you can define def validate_config(config)
method, that will be called on each config/0
usage.
Confex doesn't give opinions on a validator to be used in overrided methods.
Integrating with Ecto
Ecto has a init/2
callback, you can use it with Confex to read environment variables. We used to have all our repos to look like this:
defmodule MyApp do
use Ecto.Repo, otp_app: :my_app
@doc """
Dynamically loads the repository configuration from the environment variables.
"""
def init(_, config) do
url = System.get_env("DATABASE_URL")
config = if url,
do: Keyword.merge(config, Ecto.Repo.Supervisor.parse_url(url)),
else: Confex.process_env(config)
unless config[:database] do
raise "Set DB_NAME environment variable!"
end
unless config[:username] do
raise "Set DB_USER environment variable!"
end
unless config[:password] do
raise "Set DB_PASSWORD environment variable!"
end
unless config[:hostname] do
raise "Set DB_HOST environment variable!"
end
unless config[:port] do
raise "Set DB_PORT environment variable!"
end
{:ok, config}
end
end
Integrating with Phoenix
-
Set
on_init
callback in yourprod.exs
:config :my_app, MyApp.Web.Endpoint, on_init: {MyApp.Web.Endpoint, :load_from_system_env, []}
-
Add
load_from_system_env
function to your endpoint:defmodule Mithril.Web.Endpoint do # Some code here @doc """ Dynamically loads configuration from the system environment on startup. It receives the endpoint configuration from the config files and must return the updated configuration. """ def load_from_system_env(config) do config = Confex.process_env(config) unless config[:secret_key_base] do raise "Set SECRET_KEY environment variable!" end {:ok, config} end end
Configuration priorities
By using Confex macro in your module, you allow to provide compile-time defaults for it.
- Declare module
defmodule MyModule do
use Confex
end
- Provide defaults when using it
use MyModule,
otp_all: :myapp,
host: {:system, "HOST"}
This configs will overwritten by any configuration that you have in your application env.