Svelixir.Config.Section (svelixir v0.11.0)

Copy Markdown

Shared behaviour for the sections of Svelixir.Config.Otp.

Every section is built the same way: reject keys the struct does not declare, build any nested blocks, fill the rest over the defaults, then run that module's Vex validations.

Rejecting undeclared keys is the point of this module. struct/2 ignores keys a struct does not define, so without this check a mistyped superviser: would land as a silently ignored default and the section would report the opposite of what was asked for — the exact drift the templates warn about and do not prevent.

Using

use Svelixir.Config.Section

brings in TypedStruct, Vex.Struct, the @booleans domain, the booleans/1 shorthand, and a generated new!/1. Options:

  • :enumstrue aliases and requires Svelixir.Config.Enums. It is a parameter rather than unconditional because require is only needed by the four sections holding an enum field, and everything defenum/2 generates is a macro — an unrequired Enums.cache_mode_keys() fails to expand.
  • :children — nested blocks, as [key: Module]. Each is built from the input under key before the parent struct is assembled.
  • :as — the name to use in error messages, for the one section whose config key does not match its module name.

A section needing more than build-and-validate overrides finalize/1, which new!/1 applies last. Only the root does: cross-section rules are the one thing a section cannot check for itself.

Summary

Functions

Injects the shape every config section shares. See the module doc for options.

Reports whether value is an atom.

Reports whether value is a binary.

Declares fields as booleans, one validates/2 call each.

Builds module's struct from input and children, then validates it.

Merges override over base, recursing into nested sections.

Returns input unchanged, raising unless every key is declared by module.

Renders a module as the dotted section path used in error messages.

Reports whether value is a positive integer.

Returns struct unchanged, raising unless it satisfies its own validations.

Functions

__using__(opts \\ [])

(macro)
@spec __using__(keyword()) :: Macro.t()

Injects the shape every config section shares. See the module doc for options.

atom?(value)

@spec atom?(term()) :: boolean()

Reports whether value is an atom.

binary?(value)

@spec binary?(term()) :: boolean()

Reports whether value is a binary.

Vex's by validator needs a remote capture: an anonymous function cannot be stored in the module attribute validates/2 accumulates into.

booleans(fields)

(macro)
@spec booleans([atom()]) :: Macro.t()

Declares fields as booleans, one validates/2 call each.

Fifteen of the config's twenty-nine fields are plain on/off flags, and inclusion: [true, false] is deliberate: Vex's presence validator treats false as blank, so it would reject a legitimately-disabled flag.

build!(module, input, children \\ [], as \\ nil)

@spec build!(module(), keyword() | map(), keyword(), String.t() | nil) :: struct()

Builds module's struct from input and children, then validates it.

Raises Svelixir.Config.InvalidError for an undeclared key or a failed validation.

deep_merge(base, override)

@spec deep_merge(keyword() | map(), keyword() | map()) :: map()

Merges override over base, recursing into nested sections.

A preset supplies values several levels down, so a shallow merge would be wrong in a way that is easy to miss: Web.new!(assets: [type: :both]) would replace the whole assets block and silently discard the preset's ui flags, yielding a :web project with LiveView switched off. Recursing keeps the override to the leaf the caller actually named.

Only nested sections merge. A scalar override always wins outright, so false and nil replace a preset value rather than being skipped as blank.

known!(module, input, as \\ nil)

@spec known!(module(), keyword() | map(), String.t() | nil) :: keyword() | map()

Returns input unchanged, raising unless every key is declared by module.

label(module, as \\ nil)

@spec label(module(), String.t() | nil) :: String.t()

Renders a module as the dotted section path used in error messages.

Svelixir.Config.Container.Sidecars becomes container.sidecars, matching how the templates name their keys. as overrides the result outright.

positive_integer?(value)

@spec positive_integer?(term()) :: boolean()

Reports whether value is a positive integer.

validate!(struct, as \\ nil)

@spec validate!(
  struct(),
  String.t() | nil
) :: struct()

Returns struct unchanged, raising unless it satisfies its own validations.