Spellbook v1.0.0 Spellbook
Introduction
Spellbook is an Elixir library providing dynamic hierarchical configurations loading for your application. It is based on the ideas implemented in the Javascript node-config module.
It lets you define a set of default parameters, and extend them for different deployment environments (development, staging, production, etc.) or custom needs (client id, hostname, etc.).
Configurations are stored in default or custom folders containing configuration files and can be overridden and extended by environment variables.
Custom configuration static and dynamic filenames and file formats can be added as needed.
Quick Start
Read the configuration files from the standard <CWD>/config
folder
config = Spellbook.load_config_folder()
Using Spellbook.load_config_folder/0
by default will use the following filename templates (in the listed order and if they exist) with the {SOMETHING}
template variables substituted:
<CWD>/config/default.{EXT}
<CWD>/config/default-{INSTANCE}.{EXT}
<CWD>/config/{ENV}.{EXT}
<CWD>/config/{ENV}-{INSTANCE}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}-{INSTANCE}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}-{ENV}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
<CWD>/config/{FULL_HOSTNAME}.{EXT}
<CWD>/config/{FULL_HOSTNAME}-{INSTANCE}.{EXT}
<CWD>/config/{FULL_HOSTNAME}-{ENV}.{EXT}
<CWD>/config/{FULL_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
<CWD>/config/local.{EXT}
<CWD>/config/local-{INSTANCE}.{EXT}
<CWD>/config/local-{ENV}.{EXT}
<CWD>/config/local-{ENV}-{INSTANCE}.{EXT}
<CWD>/config/custom-env-variables.{EXT}
Spellbook will use the default environment ({ENV}
= dev
) and the full hostname of the machine the code gets executed on ({FULL_HOSTNAME}
= my-machine.spellbook.domain
). As the other template variables are not defined, the filenames using them are ignored.
The resulting filenames searched/merged will be:
<CWD>/config/default.json
<CWD>/config/default.yaml
<CWD>/config/dev.json
<CWD>/config/dev.yaml
<CWD>/config/my-machine.spellbook.domain.json
<CWD>/config/my-machine.spellbook.domain.yaml
<CWD>/config/my-machine.spellbook.domain-dev.json
<CWD>/config/my-machine.spellbook.domain-dev.yaml
<CWD>/config/local.json
<CWD>/config/local.yaml
<CWD>/config/local-dev.json
<CWD>/config/local-dev.yaml
<CWD>/config/custom-env-variables.json
<CWD>/config/custom-env-variables.yaml
By default Spellbook supports JSON and YAML file formats.
Read brand’s configuration from a specific folder with custom settings for a specific client
config = Spellbook.default_config()
|> Spellbook.add_filename_format("clients/#{brand}.#{ext}")
|> Spellbook.load_config(
folder: "./test/support/brand",
config_filename: "brand-conf",
vars: [instance: "job-processor", brand: "elixir", env: "prod", short_hostname: "worker"]
)
Here we specify a specific folder were to look for the configuration files (with the folder
option), a custom configuration file name (with the config_filename
option). The vars
configuration field is used to define the variable values used in the filename templates.
The Spellbook.default_config/0
function (and the Spellbook.load_config/0
one as well) configures the Spellbook to search for the following file templates:
./test/support/brand/{CONFIG_FILENAME}.{EXT}
./test/support/brand/{CONFIG_FILENAME}-{INSTANCE}.{EXT}
./test/support/brand/{CONFIG_FILENAME}-{ENV}.{EXT}
./test/support/brand/{CONFIG_FILENAME}-{SHORT_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
./test/support/brand/{CONFIG_FILENAME}-{FULL_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
./test/support/brand/clients/{BRAND}.{EXT}
./test/support/brand/custom-env-variables.{EXT}
In this case the searched/merged files will be:
./test/support/brand/brand-conf.json
./test/support/brand/brand-conf.yaml
./test/support/brand/brand-conf-job-processor.json
./test/support/brand/brand-conf-job-processor.yaml
./test/support/brand/brand-conf-prod.json
./test/support/brand/brand-conf-prod.yaml
./test/support/brand/brand-conf-worker-prod-job-processor.json
./test/support/brand/brand-conf-worker-prod-job-processor.yaml
./test/support/brand/brand-conf-worker1.spellbook.domain-prod-job-processor.json
./test/support/brand/brand-conf-worker1.spellbook.domain-prod-job-processor.yaml
./test/support/brand/clients/elixir.json
./test/support/brand/clients/elixir.yaml
./test/support/brand/custom-env-variables.json
./test/support/brand/custom-env-variables.yaml
Get a value out of a Spellbook configuration
A configuration is just a Map.
iex> config = Spellbook.load_config_folder()
%{ "some" => %{ "value" => %{ "from" => %{ "config" => "a value" }}}}
iex> is_map(config) == true
true
You can access the configuration values using the standard language features
iex> value = config["some"]["value"]["from"]["config"]
"a value"
or using the Spellbook.get
method that supports dot notation to access elements deep down the configuration structure:
iex> value = Spellbook.get(config, "some.value.from.config")
"a value"
Use environment variables in configuration files
Some situations rely heavily on environment variables to configure secrets and settings best left out of a codebase. Spellbook lets you use map the environment variable names into your configuration structure using a custom-env-variables.{EXT}
file:
{
"database": {
"username": "DB_USERNAME",
"password": "DB_PASSWORD"
}
}
If the DB_USERNAME
and DB_PASSWORD
environment variable exist, they would override the values for database.username
and database.password
in the configuration.
Custom environment variables have precedence and override all configuration files, including local.json
.
Summary
Functions
Performs a deep merge of two maps
Sets up the default configuration for reading a generic configuration set of files
Sets up the default configuration for reading a generic configuration set of files
Sets up the default configuration for reading application configuration from a folder
Sets up the default configuration for reading application configuration from a folder
Retrieves a configuration value
Loads the configuration files from the provided Spellbook
Creates a Spellbook with the default config folder filenames list and loads them into a configuration map
Creates a Spellbook with the default config folder filenames list and loads them into a configuration map
Creates a Spellbook with the default config filenames list and loads them into a configuration map
Registers an config file format extension and its parser
Sets Spellbook options. Option names are atoms
Sets a variable to be used during filenames list generation using a 2 elements tuple
Sets a variable to be used during filenames list generation
Sets some variable to be used during filenames list generation
Performs a deep substitution of variables used as map values
Macros
Adds a filename format to the list of templates to be used to generate the list of files to be searched when the configuration is loaded
Functions
Performs a deep merge of two maps.
Examples
iex> Spellbook.deep_merge(%{"a" => %{"b" => "1", "c" => [1,2,3]}}, %{"a" => %{"b" => "X"}})
%{"a" => %{"b" => "X", "c" => [1, 2, 3]}}
Sets up the default configuration for reading a generic configuration set of files.
Accepts a list
Specs
default_config(spellbook :: Spellbook, params :: Map.t) :: Spellbook
Sets up the default configuration for reading a generic configuration set of files.
The valid params
are:
vars
: Keyword or Keyword list of variables to be used in the filenames list generation.options
: map with Spellbook options
The default filename formats are:
<FOLDER>/{CONFIG_FILENAME}.{EXT}
<FOLDER>/{CONFIG_FILENAME}-{INSTANCE}.{EXT}
<FOLDER>/{CONFIG_FILENAME}-{ENV}.{EXT}
<FOLDER>/{CONFIG_FILENAME}-{SHORT_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
<FOLDER>/{CONFIG_FILENAME}-{FULL_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
<FOLDER>/custom-env-variables.{EXT}
Specs
default_config_folder(params :: []) :: Spellbook
default_config_folder(params :: Map.t) :: Spellbook
Sets up the default configuration for reading application configuration from a folder.
Specs
default_config_folder(spellbook :: Spellbook, params :: Map.t) :: Spellbook
Sets up the default configuration for reading application configuration from a folder.
The valid params
are:
vars
: Keyword or Keyword list of variables to be used in the filenames list generation.options
: map with Spellbook options
The default filename formats are:
<CWD>/config/default.{EXT}
<CWD>/config/default-{INSTANCE}.{EXT}
<CWD>/config/{ENV}.{EXT}
<CWD>/config/{ENV}-{INSTANCE}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}-{INSTANCE}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}-{ENV}.{EXT}
<CWD>/config/{SHORT_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
<CWD>/config/{FULL_HOSTNAME}.{EXT}
<CWD>/config/{FULL_HOSTNAME}-{INSTANCE}.{EXT}
<CWD>/config/{FULL_HOSTNAME}-{ENV}.{EXT}
<CWD>/config/{FULL_HOSTNAME}-{ENV}-{INSTANCE}.{EXT}
<CWD>/config/local.{EXT}
<CWD>/config/local-{INSTANCE}.{EXT}
<CWD>/config/local-{ENV}.{EXT}
<CWD>/config/local-{ENV}-{INSTANCE}.{EXT}
<CWD>/config/custom-env-variables.{EXT}
Retrieves a configuration value.
This function supports dot notation, so you can retrieve values from deeply nested keys, like “database.config.password”.
Examples
iex> Spellbook.get(%{"a" => %{"b" => "1", "c" => [1,2,3]}}, "a.b")
"1"
Specs
Loads the configuration files from the provided Spellbook.
Creates a Spellbook with the default config folder filenames list and loads them into a configuration map
Creates a Spellbook with the default config folder filenames list and loads them into a configuration map
Specs
load_default_config(params :: list) :: Map.t
Creates a Spellbook with the default config filenames list and loads them into a configuration map.
Specs
register_extensions(spellbook :: Spellbook, extensions :: Map.t) :: Spellbook
Registers an config file format extension and its parser.
extensions = %{
"csv" => Crazy.Parser.CSV
}
Spellbook.register_extensions(spellbook, extensions)
Specs
set_options(spellbook :: Spellbook, options :: nil | list | Map.t) :: Spellbook
Sets Spellbook options. Option names are atoms.
Valid options are:
:folder
: folder where to find the configuration. Defaults to#{Path.join(System.cwd(), "config")}
.:config_filename
: name of the configuration file, default to"config"
.:ignore_invalid_filename_formats
: defauts totrue
. Set it tofalse
if you want to raise an exception if a file in the generated filenames list is not found.
Specs
set_var(spellbook :: Spellbook, {name :: String.t, value :: any}) :: Spellbook
Sets a variable to be used during filenames list generation using a 2 elements tuple.
Specs
set_var(spellbook :: Spellbook, name :: String.t, value :: any) :: Spellbook
Sets a variable to be used during filenames list generation.
Specs
set_vars(spellbook :: Spellbook, values :: [{name :: String.t, value :: any}]) :: Spellbook
Sets some variable to be used during filenames list generation.
Macros
Specs
Adds a filename format to the list of templates to be used to generate the list of files to be searched when the configuration is loaded.
Filename formats can contain template variables specified using Elixir string interpolation format (#{VARIABLE}
):
"special-#{env}.#{ext}"
"config-#{username}-#{role}.json"
"UPPERCASE-#{String.upcase(env)}.yaml"
Files are loaded in the order you specify the filename formats.
config = Spellbook.default_config()
|> Spellbook.add_filename_format("clients/#{brand}.#{ext}")
|> Spellbook.add_filename_format(["clients/special/#{brand}-#{version}.#{ext}", "clients/external-#{brand}.#{ext}"])