# `Macaw.Data.Derived.Engine`
[🔗](https://github.com/dcoai/macaw_data/blob/v0.5.3/lib/macaw/data/derived/engine.ex#L1)

Where derived sources run (spec/02 §8): a **value** its owner holds and calls from its
own callbacks, as a `Macaw.Data.Source.Push` is held. One owner, and no second process
between the owner and its sources' rows. On a Macaw page the owner is `macaw_compose`'s
runtime.

`new/2` defines the sources (every input resolved, cycles refused by name, ranks
assigned). `push/4` shows the engine its sources' latest deliveries: a source that
changed marks the derived sources that read it, and the first mark makes a **pulse** due
`debounce:` milliseconds later. `run/2` runs a due pulse: the marked sources in rank
order, each at most once, every call bounded (`Macaw.Data.Derived.Sandbox.run/3`). A
source that fails keeps its last good output, and the pulse goes no further along it.
The engine never reads a clock: `now` is the owner's monotonic milliseconds.

    iex> alias Macaw.Data.{Delivery, Derived, Schema}
    iex> alias Macaw.Data.Derived.Engine
    iex> count = Derived.new!(id: :count, from: [hosts: nil],
    ...>   schema: %{n: %{type: :number}}, elixir: "fn %{hosts: rows} -> [%{n: Enum.count(rows)}] end")
    iex> {:ok, engine} = Engine.new(%{["count"] => count}, sources: [:hosts], debounce: 0)
    iex> hosts = Schema.new!(%{host: %{type: :category}})
    iex> engine = Engine.push(engine, %{hosts: Delivery.new([%{host: "a"}, %{host: "b"}])}, %{hosts: hosts}, 0)
    iex> engine = Engine.run(engine, 0)
    iex> Delivery.rows(Engine.outputs(engine)[["count"]])
    [%{"n" => 2}]
    iex> Engine.status(engine)
    %{["count"] => :live}

Ported from visualize's `Visualize.VirtualSource.Engine`, without its variables (a
variable is an input, spec/02 §2.3) and with sources in place of feeds.

# `error`

```elixir
@type error() :: {path(), list(), term()}
```

A definition fault: the source's path, where in it, and why (spec/02 §8.1).

# `fault`

```elixir
@type fault() :: %{kind: atom(), detail: term()}
```

A source's fault (spec/02 §8.3): its kind and its detail, never a value.

# `path`

```elixir
@type path() :: [String.t(), ...]
```

# `ref`

```elixir
@type ref() :: {:derived, path()} | {:source, String.t()}
```

# `scope`

```elixir
@type scope() :: [atom() | String.t()]
```

A scope's path, `[]` the outermost (spec/02 §3).

# `t`

```elixir
@type t() :: %Macaw.Data.Derived.Engine{
  arities: term(),
  cache: term(),
  checked: term(),
  compiled: term(),
  debounce: term(),
  derived: term(),
  due: term(),
  faults: term(),
  functions: term(),
  inputs: term(),
  marked: term(),
  operations: term(),
  order: term(),
  outputs: term(),
  ranks: term(),
  readers: term(),
  run_opts: term(),
  runs: term(),
  seen: term(),
  source_schemas: term(),
  sources: term()
}
```

# `due`

```elixir
@spec due(t()) :: integer() | nil
```

The monotonic millisecond the next pulse is due, or `nil` (spec/02 §8.2).

# `faults`

```elixir
@spec faults(t()) :: %{required(path()) =&gt; fault()}
```

Every failing derived source's fault by path, `%{kind: kind, detail: detail}`.

# `new`

```elixir
@spec new(%{required(list()) =&gt; Macaw.Data.Derived.t()}) ::
  {:ok, t()} | {:error, [error(), ...]}
```

As `new/2` with no options.

# `new`

```elixir
@spec new(%{required(list()) =&gt; Macaw.Data.Derived.t()}, keyword()) ::
  {:ok, t()} | {:error, [error(), ...]}
```

The engine over derived sources by path, defined (spec/02 §8.1): `{:ok, engine}`, or
`{:error, errors}`: every input that resolves to nothing, every host function the
registry lacks, then every cycle by name. Options (spec/02 §8.4): `sources:` (the names
of the sources the derived sources may read, `[]`), `functions:` (the registry, `%{}`,
default closed), `packages:` (the packages of operations a stored function may reach
besides `macaw_data`'s own, `[]`, default closed, spec/04 §6.1), `debounce:`
(milliseconds, `250`), and `max_reductions:`, `timeout:` and `max_heap:` (each call's
bounds, tighten-only).

# `outputs`

```elixir
@spec outputs(t()) :: %{required(path()) =&gt; Macaw.Data.Delivery.t()}
```

Every derived source's last good output by path, a `%Macaw.Data.Delivery{}` naming its
output schema's version.

# `push`

```elixir
@spec push(t(), map(), map(), integer()) :: t()
```

The sources' latest deliveries and their schemas, each by name, shown to the engine at
the owner's monotonic millisecond `now` (spec/02 §8.2): every derived source that reads
a source whose delivery is not the one last seen is marked, and the first mark makes the
next pulse due `debounce` later.

# `resolve`

```elixir
@spec resolve(t(), scope(), term()) :: ref() | :error
```

What a name read from a scope names (spec/02 §8.4): `{:derived, path}` for a derived
source, resolved from the inside out, else `{:source, name}` for a source the engine may
read, else `:error`.

# `run`

```elixir
@spec run(t(), integer()) :: t()
```

The due pulse run at `now` (spec/02 §8.2): the marked sources in rank order, each at
most once; a changed answer marks its readers, which run later in the same pulse. The
engine is returned as it is when no pulse is due.

# `runs`

```elixir
@spec runs(t()) :: %{required(path()) =&gt; non_neg_integer()}
```

How many times each derived source has run, by path.

# `schemas`

```elixir
@spec schemas(t()) :: %{required(path()) =&gt; Macaw.Data.Schema.t()}
```

Every derived source's output schema, by path.

# `source`

```elixir
@spec source(t(), list()) :: Macaw.Data.Derived.Output.t()
```

As `source/3` with no options.

# `source`

```elixir
@spec source(t(), list(), keyword()) :: Macaw.Data.Derived.Output.t()
```

The derived source at `path` as a `Macaw.Data.Source` (spec/02 §8.5): a
`%Macaw.Data.Derived.Output{}` over its last good output and its output schema.
`viewer_independent:` is the owner's to declare (`false`): the engine sees its inputs'
deliveries, not their descriptions.

# `sources_read`

```elixir
@spec sources_read(t()) :: [String.t()]
```

The sources some derived source reads, sorted.

# `status`

```elixir
@spec status(t()) :: %{required(path()) =&gt; :loading | :live | {:down, term()}}
```

Every derived source's state by path (spec/02 §8.3): `:loading` before its first
answer, `:live` after, and `{:down, {:derived, id, kind}}` while it fails.

---

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