# `Enact.Validations`
[🔗](https://github.com/svycal/enact/blob/v0.1.0/lib/enact/validations.ex#L1)

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`.

# `check`

```elixir
@spec check(Ecto.Changeset.t(), (Ecto.Changeset.t() -&gt; Ecto.Changeset.t())) ::
  Ecto.Changeset.t()
```

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`

```elixir
@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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
