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

What a snippet reads, what it calls and what it assigns, read off the parsed code.

The sibling of `MishkaCmsCore.Runtime.PageBuilder.CodeFlags`, which answers a different question
the same way: parse it, walk it, and let the shape of the tree say what a thing is. An assign is
the node `{:@, meta, [{name, meta, context}]}` — so `@flash` inside a string, inside a comment, or
written out in a code sample is not one, and neither is `@moduledoc "…"`, whose context is the
text rather than an atom.

    iex> alias MishkaCmsCore.Runtime.PageBuilder.CodeReads
    iex> CodeReads.assigns(~S|[@size, "@colour is not one"]|) |> MapSet.to_list()
    ["size"]

For a template rather than a snippet, `MishkaCmsCore.Runtime.PageBuilder.Markup.expressions/1`
gives the expressions to hand here.

## What it does with code it cannot parse

Nothing found. An author is mid-sentence in a box they are typing into, and `CodeCheck` is what
tells them the code will not compile — a half-typed line is not this module's news to break.

`assigned/1` is the exception: its caller decides from the answer that an assign is NOT written,
so code it cannot read is `:unknown`.

# `assigned`

```elixir
@spec assigned(String.t() | nil) :: {:ok, MapSet.t(String.t())} | :unknown
```

Every assign a snippet may write, by name — or `:unknown` when it writes one whose name it works
out at runtime, or when it does not parse.

A write is:

  * `assign/2,3`, `assign_new/3`, `assign_async/3,4` and `update/3`, bare, piped or on any module
    but `Map`;
  * on `Map`, `put/3`, `put_new/3`, `put_new_lazy/3`, `replace/3`, `replace!/3`, `update/4`,
    `update!/3` and `merge/2,3`;
  * a map update, `%{map | key: value}`;
  * `put_in`, `update_in`, `get_and_update_in` and `pop_in` on a path through `assigns`, which is
    `:unknown`.

A key given as anything but an atom — a variable, a call, a keyword list or map that is not
written out — is `:unknown`. On `Map` and in a map update it is `:unknown` only when the map is
`assigns` or `socket.assigns`; any other map there is ordinary data. `assign(socket, assigns)`
writes nothing new: it hands the component what it was handed.

Unlike `assigns/1`, code that does not parse is `:unknown` rather than nothing.

    iex> MishkaCmsCore.Runtime.PageBuilder.CodeReads.assigned(
    ...>   "socket |> assign(:page, 1) |> assign(loading: true)"
    ...> )
    {:ok, MapSet.new(["loading", "page"])}

    iex> MishkaCmsCore.Runtime.PageBuilder.CodeReads.assigned("assign(socket, key, 1)")
    :unknown

# `assigns`

```elixir
@spec assigns(String.t() | nil) :: MapSet.t(String.t())
```

Every assign a snippet reads, by name and without the `@`.

    iex> MishkaCmsCore.Runtime.PageBuilder.CodeReads.assigns("@a <> @b") |> MapSet.to_list()
    ["a", "b"]

# `calls`

```elixir
@spec calls(String.t() | nil) :: [{atom(), [Macro.t()]}]
```

Every call a snippet makes by a bare name, as `{name, arguments}` with the arguments still quoted.

Bare-name and nothing else: `String.trim(x)` is a call on a module and is not one of these, which
is what makes this the right question to ask about a component's own helpers.

---

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