Dotenvy behaviour (Dotenvy v0.1.0) View Source
Dotenvy
is an Elixir implementation of the original dotenv Ruby gem.
It assists in setting environment variables in ways that are compatible with both
mix releases and with runtime configuration using conventions that should be familiar
to developers coming from other languages. Conveniences for reading the variables
and converting thier values are included (See Dotenvy/env!/2
, Dotenvy.env/3
,
and Dotenvy.Transformer.to/2
).
Unlike other configuration helpers, Dotenvy
enforces no convention for the naming
of your files: you may name your configuration files whatever you wish. .env
and
its variants is a common choice, but Dotenvy
does not have opinions. You must
only pass file paths as arguments to the Dotenvy.source/2
or Dotenvy.source!/2
functions.
Usage Suggestion
Dotenvy
is designed to help help set up your application at runtime. However,
saying "at runtime" isn't specific enough: an application's configuration must be
bootstrapped before it can actually start. Although there are other places
where Dotenvy
may prove useful, it was designed with the config/runtime.exs
in mind: that's where it helps make the hand-off from system environment variables
to the application configuration in the cleanest, most declarative way possible.
.env
for environment-specific config
It is possible to use only a config.exs
and and runtime.exs
file to configure
many Elixir applications: let the .env
files handle any differences between environments.
Consider the following setup:
config/config.exs
# compile-time config
import Config
config :myapp,
ecto_repos: [MyApp.Repo]
config :myapp, MyApp.Repo,
migration_timestamps: [
type: :utc_datetime,
inserted_at: :created_at
]
config/runtime.exs
import Config
import Dotenvy
source([".env", ".env.#{config_env()}"])
config :myapp, MyApp.Repo,
database: env!("DATABASE", :string),
username: env!("USERNAME", :string),
password: env!("PASSWORD", :string),
hostname: env!("HOSTNAME", :string),
pool_size: env!("POOL_SIZE", :integer),
adapter: env("ADAPTER", :module, Ecto.Adapters.Postgres),
pool: env!("POOL", :module?)
.env
DATABASE=myapp_dev
USERNAME=myuser
PASSWORD=mypassword
HOSTNAME=localhost
POOL_SIZE=10
POOL=
.env.test
DATABASE=myapp_test
USERNAME=myuser
PASSWORD=mypassword
HOSTNAME=localhost
POOL_SIZE=10
POOL=Ecto.Adapters.SQL.Sandbox
The above setup would expect .env
to be in the .gitignore
. The above example
demonstrates developer settings appropriate for local development, but a production
deployment would only differ in its values: the shape of the file would be the same.
The .env.test
file is loaded when running tests, so its values override any of the
values set in the .env
.
By using Dotenvy.env!/2
, there is a strong contract with the environment: the
system running this app must have the designated environment variables set somehow,
otherwise this app will not start (and a specific error will be raised).
Using the nil-able variants of the type-casting (those ending with ?
) is an easy
way to defer to default values: env!("POOL", :module?)
requires that the POOL
variable is set, but it will return a nil
if the value is an empty string.
See Dotenvy.Transformer
for more details.
Note for Mix Tasks
If you have authored your own Mix tasks, you must ensure that they are loading
application configuration in a way that is compatible with the runtime config.
A good way to do this is to include Mix.Task.run("app.config")
, e.g.
def run(_args) do
Mix.Task.run("app.config")
# ...
end
Link to this section Summary
Functions
Attempts to read the given system environment variable
; if it exists, its
value is converted to the given type
. If the variable is not found, the
provided default
is returned.
Reads the given system environment variable
and converts its value to the given
type
. This relies on System.fetch_env!/1
so it will raise if a variable is
not set.
Like Bash's source
command, this loads the given file(s) and sets the corresponding
system environment variables using a side effect function (&System.put_env/1
by default).
As source/2
, but returns map on success or raises on error.
Callbacks
A parser implementation should receive the contents
read from a file,
a map of vars
(with string keys, as would come from System.get_env/0
),
and a keyword list of opts
.
Link to this section Functions
Specs
Attempts to read the given system environment variable
; if it exists, its
value is converted to the given type
. If the variable is not found, the
provided default
is returned.
The default
value will not be converted: it will be returned as-is.
This allows greater control of the output.
Although this relies on System.fetch_env/1
, it may still raise an error
if an unsupported type
is provided.
Examples
iex> env("PORT", :integer, 5432)
5433
iex> env("NOT_SET", :boolean, %{not: "converted"})
%{not: "converted"}
Specs
Reads the given system environment variable
and converts its value to the given
type
. This relies on System.fetch_env!/1
so it will raise if a variable is
not set.
Examples
iex> env!("PORT", :integer)
5432
iex> env!("ENABLED", :boolean)
true
Specs
source(files :: binary() | [binary()], opts :: keyword()) :: {:ok, %{optional(String.t()) => String.t()}} | {:error, any()}
Like Bash's source
command, this loads the given file(s) and sets the corresponding
system environment variables using a side effect function (&System.put_env/1
by default).
Files are processed in the order they are given. Values parsed from one file may override
values parsed from previous files: the last file parsed has final say. The :overwrite?
option determines how the parsed values will be merged with the existing system values.
Options
:side_effect
an arity 1 function called after the successful parsing of each of the given files. The default is&System.put_env/1
, which will have the effect of setting system environment variables based on the results of the file parsing.:overwrite?
boolean indicating whether or not values parsed from provided.env
files should overwrite existing system environment variables. Default:false
:parser
module that parses the given file(s). Overridable for testing. Default:Dotenv.Parser
:require_files
specifies which of the givenfiles
(if any) must be present. Whentrue
, all the listed files must exist. Whenfalse
, none of the listed files must exist. When some of the files are required and some are optional, provide a list specifying which files are required.:side_effect
an arity 1 function called after the successful parsing of each of the given files. The default is&System.put_env/1
, which will have the effect of setting system environment variables based on the results of the file parsing.:vars
a map with string keys representing the starting pool of variables. Default: output ofSystem.get_env/0
.
Examples
iex> Dotenvy.source(".env")
{:ok, %{
"PWD" => "/users/home",
"DATABASE_URL" => "postgres://postgres:postgres@localhost/myapp",
# ...etc...
}
}
# If you only want to return the parsed contents of the listed files
# ignoring system environment variables altogether
iex> Dotenvy.source(["file1", "file2"], side_effect: false, vars: %{})
Specs
source!(files :: binary() | [binary()], opts :: keyword()) :: %{optional(String.t()) => String.t()} | no_return()
As source/2
, but returns map on success or raises on error.
Link to this section Callbacks
Specs
A parser implementation should receive the contents
read from a file,
a map of vars
(with string keys, as would come from System.get_env/0
),
and a keyword list of opts
.
This callback is provided to help facilitate testing. See Dotenvy.Parser
for the default implementation.