Four options on field / sub_field / conditional_field that read other
parts of the input. Parsed once at compile time into __from_path__,
__on_path__, __domain_ops__.
auto
Fill in a field from an MFA call.
field :id, :string, auto: {Ecto.UUID, :generate}
field :slug, :string, auto: {MyApp.Slug, :from_title, [:title]}| Form | Calls |
|---|---|
{Mod, :fun} | Mod.fun() (no args) |
{Mod, :fun, arg} | Mod.fun(arg) |
- In the default
:addmode it runs whether or not the field was supplied, and replaces a value the caller sent. In:editmode (builder({:root, attrs, :edit})) it runs only when the field is missing ornil, so a supplied value is kept. - Compile-time verifier
VerifyAutoMFAwarns about an unknown MFA; the module still compiles.
from
Copy a value from a path elsewhere in the input.
field :user_id, :string, from: "headers::auth_user_id"
field :owner, :string, from: "root::username"The string is split on :: into a path. A leading root starts at the whole
input; any other path starts at the map the field sits in. The runtime reads
get_in(input, [:headers, :auth_user_id]) and assigns it as the field's value.
- When the source holds a value, it replaces the field's own value, even one the caller sent.
- When the source is missing or
nil, the field keeps its own value.
on
A pre-condition: the field is only allowed when another path is present.
Useful for ownership ("role_id only present when role is sent").
field :role_id, :string, on: "role"
field :owner_id, :string, on: "root::owner"The path follows the same rules as from. It is present when get_in/2 finds a
value that is not nil. The value itself is not compared. The gate only runs
when the field itself has a value; enforce: true makes the field required.
If the gate fails, the runtime emits a :dependent_keys error for the field.
To compare the value, use domain:
field :role_id, :string, domain: "!role=String[admin]"domain
Cross-field constraint expressions. Compile-evaluated into a structured op map.
field :status, :string, domain: "!auth_type=Atom[admin, moderator]"Reads: "when status has a value, auth_type must be :admin or :moderator".
The ! prefix also makes a missing auth_type an error; ? lets it be
missing. The values of Atom[...] / String[...] are separated by commas
(enum= in a derives: string separates them with ::). Works in
GuardedStruct.AshResource resources the same way as in standalone modules.
Pipeline order
Per do_pipeline/7 (lib/guarded_struct/runtime.ex):
normalize_keys— atom-convert keys (exceptdynamic_fieldvalues).authorized_fields— reject unknown top-level keys when section opted in.check_enforce_keys— required-fields check.apply_auto— run theautoMFAs (:addreplaces a supplied value,:editkeeps it).check_domain— cross-field constraints.check_on— conditional gates.apply_from— copy from other paths.- Sub-field recursion.
- Per-field validators.
run_main_validator.- Pass-1 derives over virtual fields.
- Pass-2 derives over the merged map.