Services.Globals (fnord v0.9.37)

View Source

Drop-in-ish replacement for Application env that shadows values down a process tree. Think: dynamic scope via process ancestry.

  • put_env/3 sets an override in the current tree (installing the caller as a root if needed).
  • get_env/3 first checks the current tree's overrides, then falls back to Application.get_env/3.
  • delete_env/2 removes the tree-local override.
  • get_all_env/1 lists all overrides in the current tree, overlaying them on top of Application.get_all_env/1 if the caller is the root.
  • put_all_env/2 bulk-inserts multiple overrides for one or more apps in the current tree (installing the caller as a root if needed).
  • install_root/0 explicitly installs the caller as a shadowing root (rarely needed; put_env/3 auto-installs).
  • current_root/0 returns the current shadowing root PID (or nil). Useful for debugging.
  • explain/0 prints the current process tree and its overrides (for debugging).

Summary

Functions

Returns a specification to start this module under a supervisor.

Return the current shadowing root PID (or nil). Useful for debugging.

Delete a tree-local override (no-op if none). Returns :ok.

Idempotently create a named, public ETS table owned by the Globals server. Use this instead of check-then-:ets.new in caller code: the bare pattern races under concurrency (both callers see :undefined, the loser crashes), and a table created by an arbitrary process dies with that process - under per-test instances, that means whichever test got there first. Creation is serialized through the Globals GenServer, and Globals lives for the whole VM, so the table is race-free and permanent.

Get all tree-local overrides for the given app, overlaying them on top of Application.get_all_env/1 if the caller is the root.

Get a value with tree-local shadowing, else falls back to Application.get_env/3.

Get a tree-local override only, with no fallback to Application.get_env/3. Returns default when the caller is not under a root or no override is set. Use this for keys that have no meaningful Application-env counterpart (e.g. Services.Instance registrations, whose keys are not atoms and whose values are pids scoped to a single tree's lifetime).

Install the caller as a shadowing root explicitly (rarely needed; put_env/3 auto-installs).

All tree-local overrides for the given app as raw {key, value} tuples, with no Application-env overlay and no atom-key restriction. This is the introspection surface for non-atom keys such as Services.Instance registrations.

Bulk put multiple overrides for the given app in the current tree (installing the caller as a root if needed).

Put a tree-local override. Creates a root for the current process if none exists.

Types

app()

@type app() :: atom()

key()

@type key() :: term()

value()

@type value() :: term()

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

current_root()

@spec current_root() :: pid() | nil

Return the current shadowing root PID (or nil). Useful for debugging.

delete_env(app, key)

@spec delete_env(atom(), term()) :: :ok

Delete a tree-local override (no-op if none). Returns :ok.

ensure_shared_table(name, opts)

@spec ensure_shared_table(atom(), list()) :: :ok

Idempotently create a named, public ETS table owned by the Globals server. Use this instead of check-then-:ets.new in caller code: the bare pattern races under concurrency (both callers see :undefined, the loser crashes), and a table created by an arbitrary process dies with that process - under per-test instances, that means whichever test got there first. Creation is serialized through the Globals GenServer, and Globals lives for the whole VM, so the table is race-free and permanent.

explain()

get_all_env(app)

@spec get_all_env(atom()) :: keyword()

Get all tree-local overrides for the given app, overlaying them on top of Application.get_all_env/1 if the caller is the root.

Only atom-keyed overrides are included, mirroring Application env semantics. Non-atom keys (e.g. Services.Instance registrations) are visible via overrides/1 instead - including them here would crash Keyword.put/3.

get_env(app, key, default \\ nil)

@spec get_env(atom(), term(), term()) :: term()

Get a value with tree-local shadowing, else falls back to Application.get_env/3.

get_override(app, key, default \\ nil)

@spec get_override(atom(), term(), term()) :: term()

Get a tree-local override only, with no fallback to Application.get_env/3. Returns default when the caller is not under a root or no override is set. Use this for keys that have no meaningful Application-env counterpart (e.g. Services.Instance registrations, whose keys are not atoms and whose values are pids scoped to a single tree's lifetime).

install_root()

@spec install_root() :: :ok

Install the caller as a shadowing root explicitly (rarely needed; put_env/3 auto-installs).

overrides(app)

@spec overrides(atom()) :: [{term(), term()}]

All tree-local overrides for the given app as raw {key, value} tuples, with no Application-env overlay and no atom-key restriction. This is the introspection surface for non-atom keys such as Services.Instance registrations.

put_all_env(app, kvs)

@spec put_all_env(app(), [{key(), value()}]) :: :ok

Bulk put multiple overrides for the given app in the current tree (installing the caller as a root if needed).

put_env(app, key, value)

@spec put_env(atom(), term(), term()) :: :ok

Put a tree-local override. Creates a root for the current process if none exists.

start_link(opts \\ [])