Enviable
- code :: https://github.com/halostatue/enviable
- issues :: https://github.com/halostatue/enviable/issues
Enviable is a small collection of functions to improve Elixir project configuration via environment variables as proposed under the 12-factor application model. It works well with configuration environment loaders like Dotenvy, Nvir, or Envious and provides robust value conversion like jetenv.
Enviable 3.0 introduces a new interface, Enviable.Codex, separating value
retrieval from conversion. The main new features in Codex:
- support for environment maps (returned from Dotenvy and Envious, or
System.get_env/0) withfetch_env_map/2,fetch_env_map!/2, andget_env_map/2,3; - once-only retrieval (get and erase) with
fetch_env_once/1,fetch_env_once!/1, andget_env_once/1,2; and - optional paranoid value handling, where retrieved values are stored in closures.
All Enviable.Codex value retrieval functions all return a struct implementing
String.Chars; it can be used in string interpolation just like values from
System.get_env/1,2. Outside of string interpolation, as_string/1 is
recommended.
There are breaking changes described in the CHANGELOG.
Elixir 1.17 or later is required.
Usage
Enviable or Enviable.Codex will typically be imported in config/runtime.exs
after Config, but may be used anywhere that environment variables are read.
Enviable.Codex with Nvir
The Nvir dotenv* functions always return an environment map, but it
automatically pushes all values back into the process environment table.
import Nvir
import Enviable.Codex
client = fetch_env!("CLIENT")
dotenv!([".env", ".env.#{client}"])
config :my_app,
key: fetch_env_once!("SECRET_KEY") |> as_string(),
port: fetch_env!("PORT") |> as_integer(),
ssl: get_env("SSL_ENABLED") |> as_boolean()
Enviable with Nvir
import Nvir
import Enviable
client = fetch_env!("CLIENT")
dotenv!([".env", ".env.#{client}"])
config :my_app,
key: fetch_env!("SECRET_KEY"),
port: fetch_env_as_integer!("PORT"),
ssl: get_env_as_boolean("SSL_ENABLED")
Enviable.Codex with Dotenvy
The use of a side_effect calling System.put_env/1 is discouraged with
Enviable.Codex.
import Config
import Enviable.Codex
client = fetch_env!("CLIENT")
values = Dotenvy.source!([".env", ".env.#{client}", get_env()])
config :my_app,
key: fetch_env_map!(values, "SECRET_KEY") |> as_string(),
port: fetch_env_map!(values, "PORT") |> as_integer(),
ssl: get_env_map(values, "SSL_ENABLED") |> as_boolean()
Enviable with Dotenvy
The use of a side_effect that calls System.put_env/1 is required with
Enviable.
# config/runtime.exs
import Config
import Enviable
client = fetch_env!("CLIENT")
Dotenvy.source([".env", ".env.#{client}", get_env()], side_effect: &put_env/1)
config :my_app,
key: fetch_env!("SECRET_KEY"),
port: fetch_env_as_integer!("PORT"),
ssl: get_env_as_boolean("SSL_ENABLED")
Enviable.Codex with Envious
Envious parses a single file at a time and doesn't write to the environment variable table.
import Config
import Enviable.Codex
client = fetch_env!("CLIENT")
env_files = [".env", ".env.#{client}"]
loaded_env =
Enum.reduce(env_files, %{}, fn file, acc ->
case File.read(file) do
{:ok, contents} -> Map.merge(acc, Envious.parse!(contents))
_ -> acc
end
end)
values = Map.merge(loaded_env, get_env())
config :my_app,
key: fetch_env_map!(values, "SECRET_KEY") |> as_string(),
port: fetch_env_map!(values, "PORT") |> as_integer(),
ssl: get_env_map(values, "SSL_ENABLED") |> as_boolean()
Enviable with Envious
Envious parses a single file at a time and doesn't write to the environment
variable table. To work with Enviable, we put the values into the environment
with put_env_new/2.
import Config
import Enviable
client = fetch_env!("CLIENT")
env_files = [".env", ".env.#{client}"]
loaded_env =
Enum.reduce(env_files, %{}, fn file, acc ->
case File.read(file) do
{:ok, contents} -> Map.merge(acc, Envious.parse!(contents))
_ -> acc
end
end)
for {key, value} <- loaded_env, do: put_env_new(key, value)
config :my_app,
key: fetch_env!("SECRET_KEY"),
port: fetch_env_as_integer!("PORT"),
ssl: get_env_as_boolean("SSL_ENABLED")
Installation
Enviable can be installed by adding enviable to your list of dependencies in
mix.exs:
def deps do
[
{:enviable, "~> 3.0"},
# Optional, required for decimal conversion
{:decimal, "~> 2.3"},
# Optional, required only if :json or JSON aren't available or another
# JSON engine is not configured
{:jason, "~> 1.4"},
# Optional, if using the Enviable credo checks
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
]
end
Documentation is found on HexDocs.
Semantic Versioning
Enviable follows Semantic Versioning 2.0.