# `MishkaCmsCore.Runtime.PageBuilder.Binding`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_builder/binding.ex#L5)

The v2 prototype's **Data tab**: repeating a node over a CMS collection.

An author selects a card, picks "Blog posts", and the card becomes one card per post. In the
template that is a `:for` comprehension — but the editor does not write raw source and then try to
read it back with a regex. The binding is held as a **structured descriptor** in the node's meta
and *generated* into `:for` on serialize, so it round-trips structurally: reopening the page shows
the picker set to "Blog posts", not an opaque amber chip.

This is the difference between the loop being editable and merely surviving. ADR-9 keeps
hand-written `eex_block` source byte-for-byte because the editor cannot understand it; a binding
it generated itself, it can.

## Sources

A source names a collection the CMS can actually read. They are declared here rather than guessed
from the template so the picker can only ever offer bindings that resolve, and so an extension can
add one through `builder:components`' sibling registry without the editor learning about blogs.

# `bound?`

```elixir
@spec bound?(MishkaCmsCore.Runtime.PageBuilder.Tree.Node.t()) :: boolean()
```

Whether a node repeats.

# `close`

```elixir
@spec close(MishkaCmsCore.Runtime.PageBuilder.Tree.Node.t()) :: String.t() | nil
```

The closing tag matching `open/1`.

# `delete`

```elixir
@spec delete(MishkaCmsCore.Runtime.PageBuilder.Tree.t(), String.t()) ::
  {:ok, MishkaCmsCore.Runtime.PageBuilder.Tree.t()} | {:error, term()}
```

Removes a node's binding, so it stops repeating.

# `get`

```elixir
@spec get(MishkaCmsCore.Runtime.PageBuilder.Tree.Node.t()) :: map() | nil
```

The binding recorded on a node, or `nil`.

# `open`

```elixir
@spec open(MishkaCmsCore.Runtime.PageBuilder.Tree.Node.t()) :: String.t() | nil
```

The `<%= for … do %>` opening a bound node serializes inside, or `nil` when it is not bound.

# `parse`

```elixir
@spec parse(String.t()) :: {:ok, map()} | :error
```

Reads a binding back out of a parsed `:eex_block` whose source this module generated.

Returns `{:ok, binding}` for a loop the editor recognises and `:error` for any other block — a
hand-written comprehension stays an opaque atomic chip, exactly as ADR-9 requires, because
pretending to understand it is how the old editor corrupted things.

# `preview_count`

```elixir
@spec preview_count() :: pos_integer()
```

How many preview rows the canvas renders for a bound node — the demo's three, with a badge — since
the editor has no real data to loop over.

# `put`

```elixir
@spec put(
  MishkaCmsCore.Runtime.PageBuilder.Tree.t(),
  String.t(),
  String.t(),
  keyword()
) ::
  {:ok, MishkaCmsCore.Runtime.PageBuilder.Tree.t()} | {:error, term()}
```

Binds `id` to a source. An unknown source key is refused rather than written, so the serializer
can never be handed a loop over something that does not exist.

# `source`

```elixir
@spec source(String.t()) :: map() | nil
```

One source by key, or `nil`.

# `sources`

```elixir
@spec sources() :: [map()]
```

The collections the Data tab can bind a node to.

---

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