Which feed fills each of a board's slots: the connector's value, never the board's (spec/15 §4.2, D-141).
A board says what is shown and where; it names no feed. A connector — metresis,
Exio, any host that knows its feeds — supplies a binding from a cell's slot, {cell, slot}, to a feed: any term that names a feed or a pool entry, as the pool of
spec/14 §7.2 names a source. A board travels between hosts and a binding does not,
because the feeds it names are one host's.
check/3 holds a binding to the board's required sources (Visualize.Board.required_sources/3)
and the feeds' schemas before any data flows: every unbound slot, every contract a
bound schema cannot meet and every binding to nothing, by path. pools/2 turns a
binding and a host's sources into one pool per cell, the pool Visualize.Chart.apply/2
binds a component from.
Examples
iex> alias Visualize.Board.Binding
iex> binding = Binding.new(%{{:latency, :primary} => :api_latency})
iex> Binding.fetch(binding, :latency, :primary)
{:ok, :api_latency}
iex> required = %{
...> {:latency, :primary} => %{fields: [:t, :v], types: %{}, requires: %{v: [:unit]}},
...> {:errors, :primary} => %{fields: [:t, :n], types: %{}, requires: %{}}
...> }
iex> schema = Visualize.Data.Schema.new!(%{v: %{type: :number, label: "Latency"}})
iex> Binding.check(binding, required, %{api_latency: schema})
{:error,
[
{[:cells, "errors", :sources, "primary"], {:unbound, :binding, "primary"}},
{[:cells, "latency", :sources, "primary", :requires, "v"],
{:missing_attribute, "primary", "v", :unit}}
]}
Summary
Types
Anything that names a feed or a pool entry.
A cell's slot: the cell's name and the slot's own (spec/15 §4.1), held as their strings (D-195); a host may give either as an atom.
What one slot of a board needs, as Visualize.Board.required_sources/3 gives it.
The feeds' schemas: a map from feed to schema, or a feed's describe function.
Functions
:ok, or every fault of the binding against a board's required sources and the feeds'
schemas (spec/15 §4.3), each at [:cells, cell, :sources, slot] or under it, ordered
by path
:ok, or every field a bound slot's component reads through a scale whose known type
the scale's kind cannot take (spec/15 §4.3, spec/14 §7.4, D-153):
{:type_mismatch, slot, field, expected, given} at [:cells, cell | path], the read's
path in the component, ordered by path.
The feed a cell's slot is bound to, or :error.
The empty binding.
A binding of a map, or a list, of {{cell, slot}, feed} pairs. Raises ArgumentError
for a key that is not a pair of names, atoms or strings.
One pool per cell (spec/15 §4.4): %{cell => %{slot => source}} from the binding and the
host's sources by feed. A slot whose feed has no source is left out, so application
reports it unbound.
The binding with one cell's slot bound to a feed.
Types
@type feed() :: term()
Anything that names a feed or a pool entry.
A cell's slot: the cell's name and the slot's own (spec/15 §4.1), held as their strings (D-195); a host may give either as an atom.
@type need() :: %{ :fields => [atom()], :types => %{required(atom()) => atom()}, :requires => %{required(atom()) => [atom()]}, optional(:reads) => [{list(), atom(), Visualize.Chart.need()}] }
What one slot of a board needs, as Visualize.Board.required_sources/3 gives it.
@type schemas() :: %{required(feed()) => Visualize.Data.Schema.t()} | (feed() -> {:ok, Visualize.Data.Schema.t()} | {:error, term()})
The feeds' schemas: a map from feed to schema, or a feed's describe function.
Functions
@spec check(t(), %{required(key()) => need()}, schemas()) :: :ok | {:error, [{list(), term()}, ...]}
:ok, or every fault of the binding against a board's required sources and the feeds'
schemas (spec/15 §4.3), each at [:cells, cell, :sources, slot] or under it, ordered
by path:
- a required slot the binding does not bind is
{:unbound, :binding, slot}; - for a bound slot whose contract is not empty, a feed with no schema is
{:undescribed, feed}and a describe answering{:error, reason}is{:describe, feed, reason}; each attribute the schema does not give is{:missing_attribute, slot, field, key}under:requires, and each declared type it contradicts{:schema_type, field, declared, described}under:types— the reasons and words ofVisualize.Chart.apply/2(spec/14 §7.4); - for a bound slot whose
readsis not empty, each field its schema types as its scale cannot take is{:type_mismatch, slot, field, expected, given}at[:cells, cell | path], the read's path in the component (D-153); a schema wanted only for this is no fault where it cannot be had; - a binding of a slot nothing requires is
:not_required.
A slot whose contract and reads are empty is not looked up, and a feed is described at
most once.
@spec check_types(t(), %{required(key()) => need()}, %{ required(feed()) => Visualize.Chart.field_types() }) :: :ok | {:error, [{list(), term()}, ...]}
:ok, or every field a bound slot's component reads through a scale whose known type
the scale's kind cannot take (spec/15 §4.3, spec/14 §7.4, D-153):
{:type_mismatch, slot, field, expected, given} at [:cells, cell | path], the read's
path in the component, ordered by path.
types maps a feed to what is known of its fields: a Visualize.Data.Schema, or a map
from field to type, as Visualize.Data.Table.column_types/1 reads it from a sample's
rows. An unbound slot, and a feed the map does not hold, are not checked — that is
check/3's to report.
iex> alias Visualize.Board.Binding
iex> binding = Binding.new(%{{:wave, :points} => :clock})
iex> required = %{
...> {:wave, :points} => %{fields: [:t, :v], types: %{}, requires: %{},
...> reads: [{[:marks, 0, :channels, :x], :t, :number}]}
...> }
iex> Binding.check_types(binding, required, %{clock: %{t: :time, v: :number}})
{:error,
[{[:cells, "wave", :marks, 0, :channels, :x], {:type_mismatch, "points", "t", :number, :time}}]}
The feed a cell's slot is bound to, or :error.
@spec new() :: t()
The empty binding.
A binding of a map, or a list, of {{cell, slot}, feed} pairs. Raises ArgumentError
for a key that is not a pair of names, atoms or strings.
@spec pools(t(), %{required(feed()) => term()}) :: %{ required(String.t()) => %{required(String.t()) => term()} }
One pool per cell (spec/15 §4.4): %{cell => %{slot => source}} from the binding and the
host's sources by feed. A slot whose feed has no source is left out, so application
reports it unbound.
iex> binding = Visualize.Board.Binding.new(%{{:a, :primary} => :cpu, {:b, :primary} => :mem})
iex> Visualize.Board.Binding.pools(binding, %{cpu: [%{t: 1, v: 2}]})
%{"a" => %{"primary" => [%{t: 1, v: 2}]}}
The binding with one cell's slot bound to a feed.