Skogsrå
The Skogsrå was a mythical creature of the forest that appears in the form of a small, beautiful woman with a seemingly friendly temperament. However, those who are enticed into following her into the forest are never seen again.
This library attempts to improve the use of OS environment variables for application configuration:
- Automatic type casting of values.
- Variable defaults.
- Automatic documentation generation for variables.
- Runtime reloading.
- Setting variable's values at runtime.
- Fast cached values access by using
:persistent_term
as temporal storage. - YAML configuration provider for Elixir releases.
Small Example
You would create a settings module e.g:
defmodule MyApp.Settings do
use Skogsra
@envdoc "My hostname"
app_env :my_hostname, :myapp, :hostname,
default: "localhost"
end
Calling MyApp.Settings.my_hostname()
will retrieve the value for the
hostname in the following order:
- From the OS environment variable
$MYAPP_HOSTNAME
. - From the configuration file e.g:
config :myapp,
hostname: "my.custom.host"
- From the default value if it exists (In this case, it would return
"localhost"
).
Handling different environments
If it's necessary to keep several environments, it's possible to use a
namespace
e.g:
Calling MyApp.Settings.my_hostname(Test)
will retrieve the value for the
hostname in the following order:
- From the OS environment variable
$TEST_MYAPP_HOSTNAME
. - From the configuration file e.g:
config :myapp, Test,
hostname: "my.custom.test.host"
- From the OS environment variable
$MYAPP_HOSTNAME
. - From the configuraton file e.g:
config :myapp,
hostname: "my.custom.host"
- From the default value if it exists.
Required variables
It is possible to set a environment variable as required with the required
option e.g:
defmodule MyApp.Settings do
use Skogsra
@envdoc "My port"
app_env :my_port, :myapp, :port,
required: true
end
If the variable $MYAPP_PORT
is undefined and the configuration is missing,
calling to MyApp.Settings.my_port()
will return an error tuple. Calling
$MyApp.Settings.my_port!()
(with the bang) will raise a runtime
exception.
Automatic casting
If the default value is set, the variable value will be casted as the same type
of the default value. Otherwise, it is possible to set the type for the
variable with the option type
. The available types are:
:binary
(default).:integer
.:float
.:boolean
.:atom
.:module
(modules loaded in the system).:unsafe_module
(modules that might or might not be loaded in the system)
Note:
:module
and:unsafe_module
types cannot be derived from the default value type, so they need to be explicitly specified.
Additionally, you can create a Skogsra.Type
e.g. a naive implementation for
casting "1, 2, 3, 4"
to [1, 2, 3, 4]
would be:
defmodule MyList do
use Skogsra.Type
def cast(value) when is_binary(value) do
list =
value
|> String.split(~r/,/)
|> Stream.map(&String.trim/1)
|> Enum.map(String.to_integer/1)
{:ok, list}
end
def cast(_) do
:error
end
end
If then we define the following enviroment variable with Skogsra
:
defmodule Settings do
use Skogsra
app_env :my_integers, :myapp, :my_integers, type: MyList
end
We can then set $MYAPP_INTEGERS
's value as "1, 2, 3"
and it'll be casted
casted to [1, 2, 3]
when we call Settings.my_integers/1
function e.g:
iex> Settings.my_integers()
{:ok, [1, 2, 3]}
Setting and reloading variables
It's possible to set a value for the variable at runtime with e.g.
MyApp.Settings.put_my_hostname("my.other.hostname")
.
Also, for debugging purposes is possible to reload variables at runtime with
e.g. MyApp.Settings.reload_my_hostname()
.
Using with Hab
Hab is an Oh My ZSH plugin for loading OS environment variables automatically.
By default, Hab
will try to load .envrc
file, but it's possible to have
several of those files for different purposes e.g:
.envrc.prod
for production OS variables..envrc.test
for testing OS variables..envrc
for development variables.
Hab
will load the development variables by default, but it can load the
other files using the command hab_load <extension>
e.g. loading
.envrc.prod
would be as follows:
~/my_project $ hab_load prod
[SUCCESS] Loaded hab [/home/user/my_project/.envrc.prod]
YAML Config Provider
Skogsra
includes a simple YAML configuration provider compatible with
mix release
for Elixir ≥ 1.9.
The following is the supported configuration format:
# file: /etc/my_app/config.yml
- app: "my_app" # Name of the application.
module: "MyApp.Repo" # Optional module/namespace.
config: # Actual configuration.
- database: "my_app_db"
username: "postgres"
password: "postgres"
hostname: "localhost"
port: 5432
The previous configuration file would translate to:
config :my_app, MyApp.Repo,
database: "my_App_db",
username: "postgres"
password: "postgres"
hostname: "localhost"
port: 5432
For using this config provider, just add the following to your release configuration:
config_providers: [{Skogsra.Provider.Yaml, ["/path/to/config/file.yml"]}]
Note: If the
module
you're using in you're config does not exist, then change it tonamespace
e.g:namespace: "MyApp.Repo"
. Otherwise, it will fail loading it.
Installation
The package can be installed by adding skogsra
to your list of dependencies
in mix.exs
.
- For Elixir ≥ 1.8 and Erlang ≥ 22
def deps do
[{:skogsra, "~> 2.0"}]
end
- For Elixir ≥ 1.9, Erlang ≥ 22 and YAML config provider support:
def deps do
[
{:skogsra, "~> 2.0"},
{:yamerl, "~> 0.7"}
]
end
Author
Alexander de Sousa.
License
Skogsrå is released under the MIT License. See the LICENSE file for further details.