skogsra v0.2.1 Skogsra View Source

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.
  • Options documentation.
  • Variables defaults.

Small Example

You would create a settings module and define the e.g:

defmodule MyApp.Settings do
  use Skogsra

  system_env :some_service_port,
    default: 4000

  app_env :some_service_hostname, :my_app, :hostname,
    domain: MyApp.Repo,
    default: "localhost"
end

and you would use it in a module as follows:

defmodule MyApp.SomeModule do
  alias MyApp.Setting

  (...)

  def connect do
    hostname = Settings.some_service_hostname()
    port = Settings.some_service_port()

    SomeService.connect(hostname, port)
  end

  (...)
end

Example Explanation

The module MyApp.Settings will have two functions e.g:

  • some_service_port/0: Returns the port as an integer. Calling this function is roughly equivalent to the following code (without the automatic type casting):

    System.get_env("SOME_SERVICE_PORT") || 4000
  • some_service_hostname/0: Returns the hostname as a binary. Calling this function is roughly equivalent to the following code (without the automatic type casting):

    case System.get_env("SOME_SERVICE_HOSTNAME") do
      nil ->
        :my_app
        |> Application.get_env(MyApp.Domain, [])
        |> Keyword.get(:hostname, "localhost")
      value ->
        value
    end

Things to note:

  1. The functions have the same name as the OS environment variable, but in lower case.
  2. The functions infer the type from the default value. If no default value is provided, it will be casted as binary by default.
  3. Both functions try to retrieve and cast the value of an OS environment variable, but the one declared with app_env searches for :my_app configuration if the OS environment variable is empty:

    config :my_app, MyApp.Domain,
      hostname: "some_hostname"

If the default value is not present, Skogsra cannot infer the type, unless the type is set with the option type. The possible values for type are :integer, :float, :boolean, :atom and :binary.

The recommended way of using this project is to define a .env file in the root of your project with the variables that you want to define e.g:

export SOME_SERVICE_PORT=1234

and then when sourceing the file right before you execute your application. In bash (or zsh) would be like this:

$ source .env

The previous step can be automated by adding the following code to your ~/.bashrc (or ~/.zshrc):

#################
# BEGIN: Auto env

export LAST_ENV=

function auto_env_on_chpwd() {
  env_type="$1"
  env_file="$PWD/.env"
  if [ -n "$env_type" ]
  then
    env_file="$PWD/.env.$env_type"
    if [ ! -r "$env_file" ]
    then
      echo -e "File $env_file does not exist."
      env_file="$PWD/.env"
    fi
  fi

  if [ -n "$LAST_ENV" ] && [ -r "$LAST_ENV" ]
  then
    UNSET=$(cat $LAST_ENV | sed -e 's/^export ([0-9a-zA-Z_]*)=.*$/unset 1/')
    source <(echo "$UNSET")
    echo -e "Unloaded ENV VARS defined in "$LAST_ENV""
    export LAST_ENV=
  fi

  if [ -r "$env_file" ]
  then
    export LAST_ENV="$env_file"
    source $LAST_ENV
    echo -e "Loaded "$LAST_ENV""
  fi
}

chpwd_functions=(${chpwd_functions[@]} "auto_env_on_chpwd")

if [ -n "$TMUX" ]
then
  auto_env_on_chpwd
fi

alias change_to='function _change_to() {auto_env_on_chpwd $1}; _change_to'

# END: Auto env
###############

The previous code will attempt to source any .env file every time you change directory e.g:

/home/alex $ cd my_app
Loaded "/home/alex/my_app/.env"

/home/alex/my_app $ echo "$SOME_SERVICE_PORT"
1234

Additionally, the command change_to <ENV> is included. To keep your prod, dev and test environment variables separated, just create a .env.${MIX_ENV} in the root directory of your project. And when you want to use the variables set in one of those files, just run the following:

$ change_to dev # Will use `.env.dev` instead of `.env`

Link to this section Summary

Functions

For now is just equivalent to use import Skogsra

Macro that receives the name of the OS environment variable, the name of the app, the name of the option key and some optional Keyword list with options and generates a function with arity 0 with the same name of the OS environment variable, but in lower case e.g. "FOO" would generate the function foo/0

Gets the OS environment variable by its name if present in the option :name. If it’s not found, attempts to get the application configuration option by the app name and the option key. Optionally receives a Keyword list of options

Gets the OS environment variable by its name and cast it the the type of the default value. If no default value is provided, returns a string. If the OS environment variable is not found, returns the default value.

Gets the OS environment variable by its name and casts it to the provided type. If the OS environment variable is not found, returns the default value

Macro that receives the name of the OS environment variable and some optional Keyword list with options and generates a function with arity 0 with the same name of the OS environment variable, but in lower case e.g. "FOO" would generate the function foo/0

Link to this section Functions

For now is just equivalent to use import Skogsra.

Link to this macro app_env(name, app, key, opts \\ []) View Source (macro)

Macro that receives the name of the OS environment variable, the name of the app, the name of the option key and some optional Keyword list with options and generates a function with arity 0 with the same name of the OS environment variable, but in lower case e.g. "FOO" would generate the function foo/0.

The available options are:

  • :static - Whether the computation of the OS environment variable or application configuration option is done on compiling time or not. By default its value is false.
  • :default - Default value in case the OS environment variable and the application configuration option don’t exist. By default is nil.
  • :type - The type of the OS environment variable. By default is the type of the default value. If there is no default value, the default type is binary. The available types are :binary, :integer, :float, :boolean and :atom.
  • :domain - The key to search in the configuration file e.g in:

    config :my_app, MyApp.Repo,
      (...)

    the domain would be MyApp.Repo. By default, there is no domain.

e.g

defmodule Settings do
  use Skogsra

  app_env :foo, :my_app, :foo,
    default: 42,
    type: :integer,
    domain: MyApp.Domain
end

This would generate the function Settings.foo/0 that would search for the OS environment variable "FOO" and cast it to integer on runtime. If the OS environment variable is not found, attempts to search for the :foo configuration option for the application :my_app and the domain MyApp.Domain. If nothing is found either, it defaults to 42.

Calling Settings.foo/0 without a domain set is equivalent to:

with value when not is_nil(value) <- System.get_env("FOO"),
     {number, _} <- Integer.parse(value) do
  number
else
  _ ->
    Application.get_env(:my_app, :foo, 42)
end

Calling Settings.foo/0 with a domain set is equivalent to:

with value when not is_nil(value) <- System.get_env("FOO"),
     {number, _} <- Integer.parse(value) do
  number
else
  _ ->
    opts = Application.get_env(:my_app, MyApp.Domain, [])
    Keyword.get(opts, :foo, 42)
end
Link to this function get_app_env(app, key, options \\ []) View Source
get_app_env(atom(), atom(), list()) :: term()

Gets the OS environment variable by its name if present in the option :name. If it’s not found, attempts to get the application configuration option by the app name and the option key. Optionally receives a Keyword list of options

i.e:

  • :name - Name of the OS environment variable. By default is "".
  • :default - Default value in case the OS environment variable and the application configuration option don’t exist. By default is nil.
  • :type - The type of the OS environment variable. By default is the type of the default value. If there is no default value, the default type is binary. The available types are :binary, :integer, :float, :boolean and :atom.
  • :domain - The key to search in the configuration file e.g in:

    config :my_app, MyApp.Repo,
      hostname: "localhost",
      (...)

    the domain would be MyApp.Repo. By default, there is no domain. If the domain is a list of atoms forces the algorithm to go recursively in the structure to find the key.

e.g:

defmodule MyApp do
  def get_hostname do
    Skogsra.get_app_env(:my_app, :hostname, domain: MyApp.Repo, name: "SOME_SERVICE_HOSTNAME")
  end
end
Link to this function get_env(name, default \\ nil) View Source
get_env(binary(), term()) :: term()

Gets the OS environment variable by its name and cast it the the type of the default value. If no default value is provided, returns a string. If the OS environment variable is not found, returns the default value.

Link to this function get_env_as(type, name, default \\ nil) View Source
get_env_as(atom(), binary(), term()) :: term()

Gets the OS environment variable by its name and casts it to the provided type. If the OS environment variable is not found, returns the default value.

Link to this macro system_env(name, opts \\ []) View Source (macro)

Macro that receives the name of the OS environment variable and some optional Keyword list with options and generates a function with arity 0 with the same name of the OS environment variable, but in lower case e.g. "FOO" would generate the function foo/0.

The available options are:

  • :static - Whether the computation of the OS environment variable is done on compiling time or not. By default its value is false.
  • :default - Default value in case the OS environment variable doesn’t exist. By default is nil.
  • :type - The type of the OS environment variable. By default is the type of the default value. If there is no default value, the default type is binary. The available types are :binary, :integer, :float, :boolean and :atom.

e.g

defmodule Settings do
  use Skogsra

  system_env :foo,
    default: 42,
    type: :integer
end

This would generate the function Settings.foo/0 that would search for the OS environment variable "FOO" and cast it to integer on runtime and defaults to 42.