# `MishkaCmsCore.Runtime.PageForm.Rules`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_form/_rules.ex#L5)

One form's declared rules, executed as a schemaless `Ecto.Changeset`.

## What a rule is

A rule is one of the HTML constraint attributes already on the field node, lifted at compile time
by `MishkaCmsCore.Runtime.PageBuilder.PageForms` into the field's `:rules` list. There is no second
place to declare one, so the browser and the server enforce the same sentence:

    :required                 required
    {:min_length, n}          minlength="n"
    {:max_length, n}          maxlength="n"
    {:min, n}                 min="n"
    {:max, n}                 max="n"
    {:pattern, source}        pattern="source"
    {:format, :email}         data-format="email"
    {:format, :url}           data-format="url"
    {:confirms, "password"}   data-confirms="password"

A rule this module does not know is ignored rather than raised on, because a template compiled by
a newer editor must still run on an older node.

`pattern` is read the way HTML reads it: the whole value has to match, not some piece of it, so
`pattern="[0-9]{5}"` refuses `abcd12345xyz` on both sides of the wire.

## The shapes callers depend on

`changeset/2` answers an `Ecto.Changeset` over `{%{}, types}`, so every error is
`{message, opts}` — the only shape a kit's `translate_error/1` matches — with Ecto's own
`opts[:count]` and `opts[:number]` where Gettext's plural rules can read them.

The changeset keeps the WHOLE params map, including the `_unused_<field>` markers the client
sends, which is what lets `Phoenix.Component.used_input?/1` work over a form built from it.

`apply_changes/2` answers a plain map of the cast values, keyed by atom, for whoever receives the
submission.

## Nothing here raises on what a visitor typed

A visitor can post a list where a string is expected, a word where a number is expected, or a date
that is not one. Each of those is a validation error and never an exception, so a keystroke can
never take a published page down. Where a stock Ecto validator would raise on such a value —
`validate_number/3` on a word, `validate_format/4` on an integer — the value is read first and the
validator is only reached with what it accepts.

See `MishkaCmsCore.Runtime.PageForm` for the runtime that builds the form and handles the events.

# `apply_changes`

```elixir
@spec apply_changes(map(), map()) :: map()
```

The cast values of one submission, as a map keyed by the fields the form declares.

# `changeset`

```elixir
@spec changeset(map(), map()) :: Ecto.Changeset.t()
```

The changeset for one form spec against one params map.

`values` is the form's OWN scoped params — `params["contact"]`, not the whole event payload. Keys
the spec does not declare are dropped by `cast/4` rather than reported.

---

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