# Confispex
A tool which allows to define specs for runtime configuration, cast values according to specified types and inspect them.

## Motivation
We needed a tool for managing complexity of runtime configuration. 
We have a lot of environment variables in monolithic application.  > 150+ to be more precise.
In such situation `runtime.exs` quickly becomes polluted with badly designed anonymous functions which convert data to needed Elixir terms. 
Also, these functions have bad error reporting, because in a case of exception stacktrace isn't available in `runtime.exs` file.
Environment variable names are flat, it is essential to want to categorize them.
We can't switch to yaml-like configuration file, because existing infrastructure forces to use environment variables.
Variables can used only in certain `env`, can have aliases, can be required/optional and this is needed to be documented somehow.
The easiest way to specify that variable is required is by calling `System.fetch_env!/1`, but to see all required variables
if they aren't documented, you have to run application `n` times where `n` is a number of required variables.
Team uses [`direnv`]( in development and have to keep a template of `.envrc` file up-to-date for

So, how `confispex` helps with issues mentioned above?

Elixir 1.11 allows to run application code in `runtime.exs`,
so `confispex` uses a schema defined in your application code to cast values to Elixir terms. Errors should not be reported immediatelly, but only when you ask a report. If `confispex` can't cast value from store or default value to specified type, then `nil` is returned. Think about it as an advanced wrapper around `System.get_env/1`. Also, there is a mix task to generate a `.envrc` template from schema. 

## Examples

### Schema

defmodule MyApp.RuntimeConfigSchema do
  import Confispex.Schema
  @behaviour Confispex.Schema
  alias Confispex.Type

      doc: "Autoupdate timezones from IANA Time Zone Database",
      cast: Type.Boolean,
      default: "false",
      groups: [:base],
      context: [env: [:dev, :prod]]
    "LOG_LEVEL" => %{
         values: [
      default_lazy: fn
        %{env: :test} -> "warning"
        %{env: :dev} -> "debug"
        %{env: :prod} -> "debug"
      groups: [:base]

### Runtime config

import Config

# setup confispex
{:ok, _} = Application.ensure_all_started(:confispex)

Confispex.set_context(%{env: config_env(), target: config_target()})

# application config
config :logger,
  level: String.to_atom(Confispex.get("LOG_LEVEL"))

config :tzdata,
         do: :enabled,
         else: :disabled

## Documentation

Check [Getting started](./docs/ guide.