Enact.Validations (Enact v0.1.0)

Copy Markdown View Source

Changeset-pipeline combinators for action validate/2 callbacks.

These compose into ordinary Ecto.Changeset pipelines — they are never replacements for validate_* functions. The dividing line for where a rule lives: payload-intrinsic rules (format, bounds, inclusion) belong in the input module's own validations; operation- and actor-aware rules belong in the action's validate/2.

Summary

Functions

Cheap gates expensive: no-ops when the changeset is already invalid, otherwise applies fun. All DB-backed checks go through it, so garbage input never triggers queries

Scoped uniqueness for input changesets. Skips when the field has no change (an untouched value on PATCH is already persisted).

Functions

check(changeset, fun)

Cheap gates expensive: no-ops when the changeset is already invalid, otherwise applies fun. All DB-backed checks go through it, so garbage input never triggers queries:

def validate(changeset, ctx) do
  changeset
  |> validate_length(:note, max: 500)
  |> check(&unique(&1, :slug, query: Project, repo: ctx.repo, scope: [org_id: ctx.actor.org_id], except: ctx.subject))
end

unique(changeset, field, opts)

@spec unique(Ecto.Changeset.t(), atom(), keyword()) :: Ecto.Changeset.t()

Scoped uniqueness for input changesets. Skips when the field has no change (an untouched value on PATCH is already persisted).

Options:

  • :query (required) — the persistence queryable to check against
  • :repo (required) — usually ctx.repo
  • :scope — keyword of extra column/value pairs to filter by (tenancy scoping)
  • :except — exclude the current row on PATCH. A struct (uses its primary key), an id (integer / binary, compared to id), or a keyword of column/value pairs. Soft-deletes stay on :query (e.g. from(s in Schema, where: is_nil(s.deleted_at))).

Adds "has already been taken" on the field, tagged validation: :unique. Like Ecto's unsafe_validate_unique/4 this is best-effort pre-flight UX — keep the DB unique index and declared constraint as the source of truth; the runner promotes constraint-error changesets from execute to :invalid.