Packages

A bunch of common elixir config helpers to load config from environment variables or by executing a function.

Current section

Files

Jump to
config_ext lib config_ext.ex
Raw

lib/config_ext.ex

defmodule ConfigExt do
@moduledoc """
A helper module, which contains common functions used around loading config at runtime.
"""
@doc """
Returns the **evaluated** value for `key` in `app`'s environment as a tuple.
If the configuration parameter does not exist, or pattern fails the function returns :error.
A drop in replacement for `Application.fetch_env/2`.
"""
def fetch_env(app, key) do
case Application.fetch_env(app, key) do
{:ok, val} ->
case load(val) do
{:ok, value} -> {:ok, value}
{:error, _} -> :error
end
:error -> :error
end
end
@doc """
Returns the **evaluated** value for `key` in `app`'s environment.
If the configuration parameter does not exist or pattern will fail, raises ArgumentError.
A drop in replacement for `Application.fetch_env!/2`.
"""
def fetch_env!(app, key) do
Application.fetch_env!(app, key) |> load!
end
@doc """
Returns the **evaluated** value for `key` in `app`'s environment.
If the configuration parameter does not exist or pattern will fail, the function returns the default value.
A drop in replacement for `Application.get_env/3`.
"""
def get_env(app, key, default \\ nil) do
Application.get_env(app, key, default) |> load!(default)
end
@doc """
Same as `ConfigExt.load/1` but instead of tuple, returns the value directly and on `:error` raises `ArgumentError`.
## Examples
Given `CONFIG_EXT_TEST=foo` is set in environment.
iex> ConfigExt.load!({:system, "CONFIG_EXT_TEST"})
"foo"
When `CONFIG_EXT_TEST` is not set.
iex> ConfigExt.load!({:system, "CONFIG_EXT_TEST"})
** (ArgumentError) ENV Key: CONFIG_EXT_TEST is missing
(config_ext) lib/config_ext.ex:24: ConfigExt.load!/1
"""
def load!(value) do
case load(value) do
{:ok, val} -> val
{:error, msg} -> raise ArgumentError, msg
end
end
@doc """
Same as `ConfigExt.load!/1` but will return default value instead of raising exception.
## Examples
Given `CONFIG_EXT_TEST=foo` is set in environment.
iex> ConfigExt.load!({:system, "CONFIG_EXT_TEST"}, "bar")
"foo"
When `CONFIG_EXT_TEST` is not set.
iex> ConfigExt.load!({:system, "CONFIG_EXT_TEST"}, "bar")
"bar"
"""
def load!(value, default) do
case load(value, default) do
{:ok, value} -> value
{:error, _} -> default
end
end
@doc """
Looks for dynamic patterns in input, when found - evals them - otherwise passes input forward. Supported input formats are:
{:system, KEY}
{:system, KEY, default}
{:function, Module, function_name} # which expands to below version with empty list of arguments
{:function, Module, function_name, [arg1, ...]}
Returns a tuple with two elements:
- `{:ok, value}` for matched pattern if the environment variable is present, or default is present, or if the pattern wasn't found.
- `{:error, message}` if given environment variable was empty and there was no default value.
## Examples
Given `CONFIG_EXT_TEST=foo` is set in environment.
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST"})
{:ok, "foo"}
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST", "bar"})
{:ok, "foo"}
When `CONFIG_EXT_TEST` is not set.
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST"})
{:error, ""}
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST", "bar"})
{:ok, "bar"}
For input with function pattern.
defmodule Foo do
def bar, do: "baz"
end
iex> ConfigExt.load({:function, Foo, :bar, []})
{:ok, "baz"}
Function pattern should return a non `nil` value, otherwise it's an error.
defmodule Foo do
def bar, do: nil
end
iex> ConfigExt.load({:function, Foo, :bar, []})
{:error, "empty value"}
If the function doesn't exist or it's private you should get correct error message as well.
For input without pattern.
iex> ConfigExt.load(:error)
{:ok, :error} # for example logger level
iex> ConfigExt.load("baz")
{:ok, "baz"}
"""
def load({:system, key}) when is_bitstring(key) do
case System.get_env(key) do
nil -> error(key)
"" -> error(key)
val -> {:ok, val}
end
end
def load({:system, key}), do: {:error, "ENV Key: #{inspect(key)}, is not a string"}
def load({:system, key, default}), do: load({:system, key}, default)
def load({:function, module, function}) do
load({:function, module, function, []})
end
def load({:function, module, function, args})
when is_atom(function) and is_list(args) do
case Kernel.apply(module, function, args) do
nil -> {:error, "empty value"}
val -> {:ok, val}
end
rescue
e in UndefinedFunctionError -> {:error, Exception.message(e)}
end
def load({:function, _mod, _fun, _args}), do: {:error, "function needs to be an atom, and args a list of arguments"}
def load(value), do: {:ok, value}
defp error(key), do: {:error, "ENV Key: #{key} is missing"}
@doc """
An extension to `ConfigExt.load/1` function, which accepts default value as a second argument.
Returns:
- `{:ok, value}` as in `ConfigExt.load/1`
- `{:ok, default}` in case of:
- pattern failure
- `nil` input value
## Examples
Given `CONFIG_EXT_TEST=foo` is set in environment.
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST"}, "baz")
{:ok, "foo"}
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST", "bar"}, "baz")
{:ok, "foo"}
When `CONFIG_EXT_TEST` is not set, default value is used.
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST"}, "baz")
{:ok, "baz"}
When pattern comes with default, it takes precedence before the given one.
iex> ConfigExt.load({:system, "CONFIG_EXT_TEST", "bar"}, "baz")
{:ok, "bar"}
For input with function pattern.
defmodule Foo do
def bar, do: "baz"
end
iex> ConfigExt.load({:function, Foo, :bar, []}, "buz")
{:ok, "baz"}
Function pattern should return a non `nil` value, otherwise default value will get returned.
defmodule Foo do
def bar, do: nil
end
iex> ConfigExt.load({:function, Foo, :bar, []}, "buz")
{:ok, "buz"}
If the function doesn't exist or it's private you should get a `{:ok, default}` as well.
For input without pattern.
iex> ConfigExt.load(:error, "baz")
{:ok, :error} # as :error is a valid input
iex> ConfigExt.load("foo", "bar")
{:ok, "foo"}
"""
def load({:system, key}, default) do
case load({:system, key}) do
{:error, _} -> {:ok, default}
{:ok, val} -> {:ok, val}
end
end
def load({:system, key, user_default}, _default) do
load({:system, key}, user_default)
end
def load({:function, module, function}, default) do
load({:function, module, function, []}, default)
end
def load({:function, module, function, args}, default) do
case load({:function, module, function, args}) do
{:error, _msg} -> {:ok, default}
{:ok, val} -> {:ok, val}
end
end
def load(nil, default), do: {:ok, default}
def load(value, _default), do: {:ok, value}
end