The per-argument routing classifier for the Ecto.Changeset pipeline stages the plugin mutates
(Mutare.Ecto.Changeset.stages/0 — every validator, constraint, and Repo-time hook it can
drop) — the changeset counterpart of the query classifier Mutare.Ecto.Host.Routing. Nothing
here is hosted; the classifier only holds back from core's families the positions where a
core swap would be a crash, not a mutant, so a run that lists the plugin next to :all
reports no crasher in a changeset stage. The test of each pin is Ecto's own handling of the
swapped value:
- A written field atom in the field position (
validate_length(cs, :name, …),unique_constraint(cs, :email)) names a column — structural, likefield/2's name in a query — and routes:raw. Core's atom family would swap it for:mutare, an unknown field Ecto raises on. Anything else in that slot — a field list (validate_required(cs, [:name, :email])), a variable, aprepare_changesfunction — routes:expression, so a list stays reachable for core's list families. - The option keys of
validate_numberroute per-pair{:keyword, …}, which leaves every key raw while the values stay:expression. Ecto rejects unsupported options (ArgumentError: unknown option), so a swapped key raises — every written key is raw, no key set is consulted. The bound literal keeps core's off-by-one mutants, andMutare.Ecto.ValidationBoundaryswaps strict and non-strict keys. validate_length's keys are not held back: Ecto readscount:/is:/min:/max:and ignores anything else, so core'smin:→mutare:is a live mutant — that one bound gone, the call otherwise intact — a more specific mutation than the whole-call:validation_drop. Only a written mode atom in itscount:value (:graphemes/:codepoints/:bytes) is held back, by a keyed refinement ([:expression, count: :raw])::mutareis no mode, and Ecto's mode dispatch has no clause for it on a string field (aCaseClauseError). A computed value stays:expression, so core can mutate how the mode is selected. The options of every other stage (a constraint'sname:, amessage:alone) stay:expression— the plugin defines no option restrictions for unlisted stages.
The field position is the second argument in both spellings: core hands a pipe stage over as
the direct call it is sugar for, so cs |> validate_length(:name, …) is classified as
validate_length(cs, :name, …), with the changeset at position 0 (routed :expression, so
the upstream pipeline stays mutable).
Summary
Functions
Mutare.CallRouting.route_arguments/1 for a :routing-registered Ecto.Changeset stage:
the per-argument treatments/2 classification, wrapped as ArgumentRoutes.
Per-argument treatment for a changeset stage name with arguments args (the piped
changeset included, at position 0): every position :expression, except a written field atom
in the field position (:raw), validate_number's trailing keyword list ({:keyword, …},
every key raw), and validate_length's when its count: value is a written atom
([:expression, count: :raw]) — the moduledoc.
Functions
@spec route_arguments(Mutare.CallRouting.Call.t()) :: Mutare.CallRouting.ArgumentRoutes.t()
Mutare.CallRouting.route_arguments/1 for a :routing-registered Ecto.Changeset stage:
the per-argument treatments/2 classification, wrapped as ArgumentRoutes.
@spec treatments(atom(), [Macro.t()]) :: [Mutare.CallRouting.treatment()]
Per-argument treatment for a changeset stage name with arguments args (the piped
changeset included, at position 0): every position :expression, except a written field atom
in the field position (:raw), validate_number's trailing keyword list ({:keyword, …},
every key raw), and validate_length's when its count: value is a written atom
([:expression, count: :raw]) — the moduledoc.