Macaw.Component.Slot (macaw_component v0.1.5)

Copy Markdown View Source

A slot: a typed input, %{requires: %{role => requirement}, doc: "…"} (spec/01 §1.3).

Each role is a field the component reads from the slot's source, with what it requires of that field. check/3 checks a binding of the slot to a source's schema before any data flows, so a component author and every host check bindings the same way.

Summary

Functions

Checks a binding of a slot (as new/1 builds it) to a source of schema, fields mapping each role to its field, or for a many role a non-empty list of fields. :ok or every fault at once, each at [role]. A window role left unbound reads the schema's window field.

A slot built from its declaration, every requirement's defaults filled in and type always a list: {:ok, slot}, or {:error, faults} from the slot.

A requirement's keys, in order.

The resolved binding of a slot (as new/1 builds it) to a source of schema, for fields that check/3 passes: each bound role mapped to its field, a string, or for a many role its list of fields. A window role left unbound holds the schema's window field, by check/3's rule; an optional role left unbound is absent. This is the map a host puts in the context's bindings (spec/02 §2), so the core and every adopter resolve a binding the same way. Fields check/3 refuses raise ArgumentError.

Types

requirement()

@type requirement() :: %{
  type: [atom(), ...],
  window: boolean(),
  identity: boolean(),
  quantity: atom() | nil,
  many: boolean(),
  optional: boolean(),
  doc: String.t() | nil
}

t()

@type t() :: %{
  requires: %{required(String.t()) => requirement()},
  doc: String.t() | nil
}

Functions

check(map, schema, fields)

@spec check(t(), Macaw.Data.Schema.t(), map()) ::
  :ok | {:error, [Macaw.Component.fault(), ...]}

Checks a binding of a slot (as new/1 builds it) to a source of schema, fields mapping each role to its field, or for a many role a non-empty list of fields. :ok or every fault at once, each at [role]. A window role left unbound reads the schema's window field.

iex> {:ok, slot} = Macaw.Component.Slot.new(%{requires: %{"x" => %{type: :time, window: true}, "y" => %{type: :number}}})
iex> schema = Macaw.Data.Schema.new!(%{t: %{type: :time}, v: %{type: :number}})
iex> Macaw.Component.Slot.check(slot, schema, %{"y" => "v"})
:ok
iex> Macaw.Component.Slot.check(slot, schema, %{})
{:error, [{["y"], :unbound}]}

new(slot)

@spec new(term()) :: {:ok, t()} | {:error, [Macaw.Component.fault(), ...]}

A slot built from its declaration, every requirement's defaults filled in and type always a list: {:ok, slot}, or {:error, faults} from the slot.

requirement_keys()

@spec requirement_keys() :: [atom(), ...]

A requirement's keys, in order.

resolve(slot, schema, fields)

@spec resolve(t(), Macaw.Data.Schema.t(), map()) :: %{
  required(String.t()) => String.t() | [String.t(), ...]
}

The resolved binding of a slot (as new/1 builds it) to a source of schema, for fields that check/3 passes: each bound role mapped to its field, a string, or for a many role its list of fields. A window role left unbound holds the schema's window field, by check/3's rule; an optional role left unbound is absent. This is the map a host puts in the context's bindings (spec/02 §2), so the core and every adopter resolve a binding the same way. Fields check/3 refuses raise ArgumentError.

iex> {:ok, slot} = Macaw.Component.Slot.new(%{requires: %{"x" => %{type: :time, window: true}, "y" => %{type: :number, many: true}, "label" => %{type: :text, optional: true}}})
iex> schema = Macaw.Data.Schema.new!(%{t: %{type: :time}, v: %{type: :number}, w: %{type: :number}})
iex> Macaw.Component.Slot.resolve(slot, schema, %{"y" => [:v, :w]})
%{"x" => "t", "y" => ["v", "w"]}