Packages

Advanced schema definition and validation library for Elixir

Current section

Files

Jump to
exdantic examples settings_loader.exs
Raw

examples/settings_loader.exs

# Comprehensive Exdantic.Settings example
# Run with: mix run examples/settings_loader.exs
Mix.Task.run("compile")
defmodule SettingsLoaderExample do
alias Exdantic.Settings
defmodule DatabaseConfig do
use Exdantic
schema do
field(:host, :string, required: true)
field(:pool_size, :integer, default: 5)
end
end
defmodule AppConfig do
use Exdantic, define_struct: true
schema "Settings schema for a sample service" do
field(:name, :string, default: "exdantic-service")
field(:port, :integer, default: 4000)
field(:enabled, :boolean, default: true)
field(:mode, :atom, default: :safe)
field(:database, DatabaseConfig, required: true)
field(:tags, {:array, :string}, default: [])
field(:database_url, :string, required: true, extra: %{"env" => "DATABASE_URL"})
end
end
def run do
IO.puts("=== Exdantic.Settings Comprehensive Example ===")
IO.puts("")
successful_load_demo()
input_precedence_demo()
empty_string_behavior_demo()
case_collision_demo()
invalid_json_demo()
from_system_env_demo()
end
defp successful_load_demo do
IO.puts("1) Successful load with prefixing, nested merge, and absolute override")
env = baseline_env()
{:ok, settings} =
Settings.load(AppConfig,
env: env,
env_prefix: "APP_",
env_nested_delimiter: "__",
allow_atoms: :existing,
ignore_empty: true
)
IO.inspect(settings, label: "Loaded settings")
IO.puts("Derived key APP_DATABASE_URL is ignored because DATABASE_URL override wins.")
IO.puts("")
end
defp input_precedence_demo do
IO.puts("2) Explicit input has higher precedence than env values")
env = baseline_env()
{:ok, settings} =
Settings.load(AppConfig,
env: env,
env_prefix: "APP_",
allow_atoms: :existing,
input: %{port: 4200, database: %{pool_size: 20}}
)
IO.inspect(settings, label: "Merged settings")
IO.puts("")
end
defp empty_string_behavior_demo do
IO.puts("3) ignore_empty controls whether empty strings are treated as absent")
env =
baseline_env()
|> Map.put("APP_NAME", "")
{:ok, ignored_empty} =
Settings.load(AppConfig,
env: env,
env_prefix: "APP_",
allow_atoms: :existing,
ignore_empty: true
)
{:ok, kept_empty} =
Settings.load(AppConfig,
env: env,
env_prefix: "APP_",
allow_atoms: :existing,
ignore_empty: false
)
IO.inspect(ignored_empty.name, label: "ignore_empty: true")
IO.inspect(kept_empty.name, label: "ignore_empty: false")
IO.puts("")
end
defp case_collision_demo do
IO.puts("4) Case-insensitive collisions are rejected")
env =
baseline_env()
|> Map.merge(%{"app_port" => "1111", "APP_PORT" => "2222"})
case Settings.load(AppConfig, env: env, env_prefix: "APP_", allow_atoms: :existing) do
{:ok, _} ->
IO.puts("Unexpected success")
{:error, errors} ->
IO.inspect(errors, label: "Collision errors")
end
IO.puts("")
end
defp invalid_json_demo do
IO.puts("5) Structured fields require JSON values")
env =
baseline_env()
|> Map.put("APP_TAGS", "not-json")
case Settings.load(AppConfig, env: env, env_prefix: "APP_", allow_atoms: :existing) do
{:ok, _} ->
IO.puts("Unexpected success")
{:error, errors} ->
IO.inspect(errors, label: "JSON decode errors")
end
IO.puts("")
end
defp from_system_env_demo do
IO.puts("6) from_system_env/2 usage")
env_vars = %{
"APP_DATABASE" => ~s({"host":"db.system","pool_size":7}),
"APP_PORT" => "4300",
"APP_MODE" => "safe",
"DATABASE_URL" => "ecto://system@localhost/system_db"
}
previous = Enum.into(env_vars, %{}, fn {k, _} -> {k, System.get_env(k)} end)
Enum.each(env_vars, fn {k, v} -> System.put_env(k, v) end)
try do
{:ok, settings} =
Settings.from_system_env(AppConfig,
env_prefix: "APP_",
allow_atoms: :existing
)
IO.inspect(settings, label: "System env settings")
after
Enum.each(previous, fn
{k, nil} -> System.delete_env(k)
{k, v} -> System.put_env(k, v)
end)
end
IO.puts("")
end
defp baseline_env do
%{
"APP_PORT" => "4100",
"APP_ENABLED" => "0",
"APP_MODE" => "safe",
"APP_DATABASE" => ~s({"host":"db.local","pool_size":5}),
"APP_DATABASE__POOL_SIZE" => "12",
"APP_TAGS" => ~s(["settings","example"]),
"APP_DATABASE_URL" => "ecto://derived@localhost/from_prefix",
"DATABASE_URL" => "ecto://override@localhost/main_db"
}
end
end
SettingsLoaderExample.run()