Visualize.Board.Binding (Visualize v0.8.0)

Copy Markdown View Source

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.

t()

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

feed()

@type feed() :: term()

Anything that names a feed or a pool entry.

key()

@type key() :: {atom() | String.t(), atom() | String.t()}

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.

need()

@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.

schemas()

@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.

t()

@type t() :: %Visualize.Board.Binding{slots: %{required(key()) => feed()}}

Functions

check(binding, required, schemas)

@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 of Visualize.Chart.apply/2 (spec/14 §7.4);
  • for a bound slot whose reads is 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.

check_types(binding, required, types)

@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}}]}

fetch(binding, cell, slot)

@spec fetch(t(), atom(), atom()) :: {:ok, feed()} | :error

The feed a cell's slot is bound to, or :error.

new()

@spec new() :: t()

The empty binding.

new(pairs)

@spec new(map() | [{key(), feed()}]) :: t()

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.

pools(binding, sources)

@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}]}}

put(binding, cell, slot, feed)

@spec put(t(), atom(), atom(), feed()) :: t()

The binding with one cell's slot bound to a feed.