After some development, I’m interested in your thoughts with my prototype API. Today it looks akin to:
# config/config.exs
config :runtime, values: [
SIGNING_SALT: :string,
DATABASE_URL: :uri,
DATABASE_POOL: {:integer, min: 1, max: 100}
]
config :runtime, sources: [
json: [file: "priv/config.json"],
exs: [file: "priv/config.exs"],
dotenv: [file: "priv/config.env"],
env: []
]
While I’d love to distance myself even further from the config/*.exs world, having the library know these things in advance at compile-time lets me make very strong guarantees—for example:
- generating compile-time warnings/errors on typo’d keys
- ie.
Runtime.Config.get(:DATABAS_URL)
- ie.
- meta-programming clauses with typespecs for specific keys
- ie. letting dialyzer know that
Runtime.Config.get(:DATABASE_URL)will always return a%URI{}struct and contrary usage should emit a warning
- ie. letting dialyzer know that
This API is Keyword based, so plays well with Config’s deep-merging strategy. This means theoretically you could still do stuff like:
# config/dev.exs
config :runtime, values: [
DATABASE_URL: [
type: :uri,
required: false,
default: {:value, "http://localhost:5432/my_app_dev"}
]
]
# config/prod.exs
config :runtime, values: [
DATABASE_URL: [type: :uri, required: true]
]
or:
# config/dev.exs
config :runtime, sources: [json: [file: "priv/config/dev.json"]]
# config/prod.exs
config :runtime, sources: [json: [file: "priv/config/prod.json"]]
However, the library would generally discourage such an approach, in favor of a more robust and consolidated “context” DSL. The above examples can be written as:
# config/config.exs
config :runtime, values: [
DATABASE_URL: [
type: :uri,
required: [in: :prod],
default: [in: [dev: {:value, "http://localhost:5432/my_app_dev"}]]
]
]
config :runtime, sources: [
json: [file: "priv/config/dev.json", in: :dev],
json: [file: "priv/config/prod.json", in: :prod]
]
The current context DSL further supports things like specifying that a value is required to have been provided in contexts:
at: :compile_time | :boot_time | :runtimein: [:list, :of, :mix, :envs],for: [:list, :of, :mix, :targets]
in any permutation. Similarly for default values, when sources are loaded, and if a source is required to exist at: a certain time in: a certain env for: a certain target.
Thoughts? As far as I can tell, this DSL pretty much allows all compile-time config that actually impacts how a project’s dependencies generate source code to be provided exclusively by traditional config/*.exs mechanisms, and have all other current runtime-config usecases to be described by two single entries for config :runtime. This lets all actual runtime values live outside the config/*.exs world entirely, removing the need for any config/runtime.exs stuff, while preserving compile-time goodies furnished by the library itself.






















