moonbitapp/main.mbt
// What the program carries.
let defaults = @moonconf.Node::map([
("db", @moonconf.Node::map([
("host", @moonconf.Node::str("localhost", "defaults")),
("port", @moonconf.Node::num(5432, "defaults")),
], "defaults")),
], "defaults")
// What the deployment shipped, and what the machine says.
let file = @ini.parse(
"[db]\nhost = db.internal\npool = 8\n",
).tree(source="app.ini")
let env = @moonconf.Node::map([
("db", @moonconf.Node::map([
("port", @moonconf.Node::str("6432", "env")),
], "env")),
], "env")
let merged = @moonconf.merge([
@moonconf.Source::new(@moonconf.Env, "env", env),
@moonconf.Source::new(@moonconf.Default, "defaults", defaults),
@moonconf.Source::new(@moonconf.File, "app.ini", file),
])
println(merged.need_text("db.host")) // db.internal
println(merged.need_text("db.port")) // 6432
// A .env file is the same idea with fewer brackets.
let env_file = @dotenv.load("HOST=db.internal\nDSN=postgres://${HOST}/app\n")
println(env_file.get("DSN").unwrap()) // postgres://db.internal/app
// Text expansion stands on its own, for a value written as a shell writes it.
let vars = @expand.Vars::of_map(Map([("PORT", "8080")]))
println(@expand.expand("http://localhost:${PORT:-80}/", vars))
// A model turns the section into a record, with types and defaults.
let db = @moonmodel.Model::new("Db", [
@moonmodel.Field::new("host", @moonmodel.Type::Text, required=true),
@moonmodel.Field::new("port", @moonmodel.Type::Int, required=true),
@moonmodel.Field::new("pool", @moonmodel.Type::Int,
default=Json::number(4.0)),
])
println(@moonconf.bind(merged.need("db"), db).stringify())
outputnative
$ moon run src/examples/tour
host -> db.internal
port -> 6432
pool -> 8
bound -> {"host":"db.internal","port":6432,"pool":8}
dsn -> postgres://db.internal/app
url -> http://localhost:8080/