DeferredConfig v0.1.0 DeferredConfig

Seamlessly add runtime config to your library, with the “system tuples” or the {m,f,a} patterns.

Seamlessly?

In your application startup, add the following line:

defmodule Mine.Application do
  def start(_type, _args) do
    DeferredConfig.populate(:mine)  # <-- this one
    ...
  end
end

Where :mine is the name of your OTP app.

Now you and users of your app or lib can configure as follows, and it’ll work — regardless of if they’re running it from iex, or a release with env vars set:

config :mine, 

  # string from env var, or `nil` if missing.
  port1: {:system, "PORT"},

  # string from env var |> integer; `nil` if missing.
  port2: {:system, "PORT", {String, :to_integer}},

  # string from env var, or "4000" as default.
  port3: {:system, "PORT", "4000"},

  # converts env var to integer, or 4000 as default.
  port4: {:system, "PORT", 4000, {String, :to_integer}}

Accessing config does not change.

Since you can use arbitrary transformation functions, you can do advanced transformations if you need to:

# lib/mine/ip.ex
defmodule Mine.Ip do
  @doc ":inet_res uses `{0,0,0,0}` for ipv4 addrs"
  def str2ip(str) do
    case :inet_parse:address(str) do
      {:ok, ip = {_, _, _, _}} -> ip
      {:error, _}              -> nil
    end
  end
end

# config.exs 
config :my_app,
  port: {:system, "MY_IP", {127,0,0,1}, {Mine.Ip, :str2ip}

See README.md for explanation of rationale. TL;DR: REPLACE_OS_VARS is string-only and release-only, and {:system, ...} support among libraries is spotty and easy to get wrong in ways that bite your users come release time. This library tries to make it easier to do the right thing with 1 LOC. Other libraries add special config files and/or special config accessors, which is more complex than necessary.

Summary

Functions

Default recognize/transform pairs used in populating deferred config. Currently r/t pairs for :system tuples and :apply mfa tuples

Return transformed copy of recognized system tuples: gets from env, optionally converts it, with optional default if env returned nothing

Recognize mfa tuple, like {:apply, {File, :read!, ["name"]}}. Returns true on recognition, false otherwise

Recognizer for system tuples of forms:

  • {:system, "VAR"}
  • {:system, "VAR", default_value}
  • {:system, "VAR", {String, :to_integer}}
  • {:system, "VAR", default_value, {String, :to_integer}} Returns true when it matches one, false otherwise

Given a config kvlist, and an enumerable of {&recognize/1, &transform/1} functions, returns a kvlist with the values transformed via replacing walk

Return evaluated {:apply, {mod, fun, args}} tuple

Functions

apply_transformed_cfg!(kvlist, app)

Application.put_env/3 for config kvlist

default_transforms()

Default recognize/transform pairs used in populating deferred config. Currently r/t pairs for :system tuples and :apply mfa tuples.

get_system_tuple(t)

Return transformed copy of recognized system tuples: gets from env, optionally converts it, with optional default if env returned nothing.

populate(app, transforms \\ [{&DeferredConfig.recognize_system_tuple/1, &DeferredConfig.get_system_tuple/1}, {&DeferredConfig.recognize_mfa_tuple/1, &DeferredConfig.transform_mfa_tuple/1}])

Populate deferred values in an app’s config. Best run during Application.start/2.

By default attempts to populate the common {:system, "VAR"} tuple form for getting values from System.get_env/1, and the more general {:apply, {Mod, fun, [args]}} form as well.

System tuples support optional defaults and conversion functions, see Peerage.DeferredConfig.get_system_tuple/1.

Can be extended by passing in a different enumerable of {&recognizer/1, &transformer/1} functions.

recognize_mfa_tuple(arg1)

Recognize mfa tuple, like {:apply, {File, :read!, ["name"]}}. Returns true on recognition, false otherwise.

recognize_system_tuple(arg1)

Recognizer for system tuples of forms:

  • {:system, "VAR"}
  • {:system, "VAR", default_value}
  • {:system, "VAR", {String, :to_integer}}
  • {:system, "VAR", default_value, {String, :to_integer}} Returns true when it matches one, false otherwise.
transform_cfg(cfg, rts \\ [{&DeferredConfig.recognize_system_tuple/1, &DeferredConfig.get_system_tuple/1}, {&DeferredConfig.recognize_mfa_tuple/1, &DeferredConfig.transform_mfa_tuple/1}])

Given a config kvlist, and an enumerable of {&recognize/1, &transform/1} functions, returns a kvlist with the values transformed via replacing walk.

transform_mfa_tuple(arg)

Return evaluated {:apply, {mod, fun, args}} tuple.