Visualize.Chart.Builder.Bundle (Visualize v0.8.1)

Copy Markdown View Source

A collection of entries that can cross from one store into another (spec/14 §19.7).

Export takes the closure. Exporting an entry exports every entry it refers to, transitively, with their ids, so an import cannot dangle by construction. The bundle carries provenance — which store it came from, when, and the schema version it was exported at (spec/14 §9.1) — and each entry keeps its original id, so a repeated import of the same collection is detectable rather than a second copy of everything.

Importing one is the store's import/2 callback: a relocation through one old => new table, committed whole or not at all (D-103).

A bundle may carry a board (spec/15 §11, D-147): export_board/3 adds the board's map form, the closure of every component its cells reference and every stored theme it or its components name; import_board/3 checks all of it before any entry is put, then imports the entries through the store's own import/2 and rewrites the board's cell refs through the same table. A bundle carries no binding: bindings are the host's.

This module compiles only when Phoenix.Component is loaded (spec/10 §1.1, D-45, D-85).

Summary

Types

What a board's import gives: the board re-keyed, the id table, and the masks.

Where a bundle came from, when, and the schema version it was exported at, which an importer older than it refuses (spec/14 §9.1).

t()

A bundle: its provenance, its entries in dependency order, and — for one export_board/3 made — the board in its map form (spec/15 §2.1), else nil.

Functions

The closure of an id through a store: the entry, and every entry it refers to, transitively, each once, in dependency order — an entry after everything it refers to.

A board as a bundle (spec/15 §11.1): the board's map form, the closure of every component its cells reference, and the closure of every stored theme the board or its components name, dependencies first, with provenance as export/3 gives it.

The bundle of a JSON document to_json/1 wrote, with limits: as from_map/2 takes them. The document's "$version" and the provenance's schema are gated, and the whole document held to the limits, before a key is decoded; then each part is read by its own reader — an entry's fragment by the fragment's, gated and held to the tag table (D-182), its faults under [:entries, i, :fragment]; the board by the board's — and the map read as from_map/2 reads it. An id is read by Store.decode_id/1, so no atom is made from a document's kind (D-195). Text that is not a JSON object is {:error, [{[], {:json, message}}]}.

The bundle of a map (spec/14 §19.7), or every fault of it by path.

A board bundle into a store (spec/15 §11.2): {:ok, %{board: board, ids: table, outside: ids}}, the board with every cell's ref rewritten to its component's new id, or {:error, errors}, every fault in the board's path shape.

The bundle as one JSON document stamped "$version" (spec/14 §19.7, D-217): the provenance with exported_at in ISO 8601, each entry's id as Store.encode_id/1 writes it and its fragment as the document Fragment.to_json/1 writes, and the board in its own JSON form (spec/15 §2.2). A store named by a string or an atom is written as the name's string; any other term as its tagged term (spec/14 §11.2), and one the table has no form for is {:error, [{[:provenance, :store], {:type, :term}}]}. {:error, {:missing_dependency, :jason}} without Jason.

The bundle as plain data: provenance, entries as %{id, name, fragment} — an entry's kind is derived from its fragment (spec/14 §19.8), so the form leaves it out — and board, the board's map form or nil.

Types

imported()

@type imported() :: %{
  board: Visualize.Board.t(),
  ids: %{
    required(Visualize.Chart.Builder.Store.id()) =>
      Visualize.Chart.Builder.Store.id()
  },
  outside: [Visualize.Chart.Builder.Store.id()]
}

What a board's import gives: the board re-keyed, the id table, and the masks.

provenance()

@type provenance() :: %{
  store: term(),
  exported_at: DateTime.t(),
  schema: pos_integer()
}

Where a bundle came from, when, and the schema version it was exported at, which an importer older than it refuses (spec/14 §9.1).

t()

@type t() :: %Visualize.Chart.Builder.Bundle{
  board: map() | nil,
  entries: [Visualize.Chart.Builder.Store.entry()],
  provenance: provenance()
}

A bundle: its provenance, its entries in dependency order, and — for one export_board/3 made — the board in its map form (spec/15 §2.1), else nil.

Functions

export(store, ids, source)

The closure of an id through a store: the entry, and every entry it refers to, transitively, each once, in dependency order — an entry after everything it refers to.

source names the store in the provenance; it is the host's to choose and the store is never asked. An id the store does not hold is skipped: it was a mask before export and stays one after import (§19.2).

export_board(store, board, source)

@spec export_board(Visualize.Chart.Builder.Store.store(), Visualize.Board.t(), term()) ::
  t()

A board as a bundle (spec/15 §11.1): the board's map form, the closure of every component its cells reference, and the closure of every stored theme the board or its components name, dependencies first, with provenance as export/3 gives it.

A component names a theme by its flattened design's theme; a variable there is read through the cell's cascade (spec/15 §5), which is how a board names one. A built-in name is not carried, nor one the store does not hold. A cell whose component the store does not hold is exported as it is, and import_board/3 reports it. No binding is carried: a binding names one host's feeds.

from_json(json, opts \\ [])

@spec from_json(String.t(), keyword()) ::
  {:ok, t()}
  | {:error, [Visualize.Board.error(), ...] | {:missing_dependency, :jason}}

The bundle of a JSON document to_json/1 wrote, with limits: as from_map/2 takes them. The document's "$version" and the provenance's schema are gated, and the whole document held to the limits, before a key is decoded; then each part is read by its own reader — an entry's fragment by the fragment's, gated and held to the tag table (D-182), its faults under [:entries, i, :fragment]; the board by the board's — and the map read as from_map/2 reads it. An id is read by Store.decode_id/1, so no atom is made from a document's kind (D-195). Text that is not a JSON object is {:error, [{[], {:json, message}}]}.

from_json(to_json(b)) is {:ok, b} with each fragment as Fragment.from_json/2 reads it and a store named by an atom as its string.

iex> Visualize.Chart.Builder.Bundle.from_json(~s({"$version": 9, "entries": []}))
{:error, [{["$version"], {:newer_schema, 9, 3}}]}

from_map(map, opts \\ [])

@spec from_map(term(), keyword()) ::
  {:ok, t()} | {:error, [Visualize.Board.error(), ...]}

The bundle of a map (spec/14 §19.7), or every fault of it by path.

The provenance's schema is gated first ({:newer_schema, needs, this} at [:provenance, :schema]), then the map is held to the depth and items limits — limits: as Visualize.Board.from_map/2 takes them — then every key is read: the provenance's store (any JSON-representable term), exported_at (a UTC DateTime) and schema; each entry's id (an id of a kind a fragment has, carried once), name and fragment; the board by Visualize.Board.from_map/2, kept in its map form, its faults under [:board]. An entry's kind is derived from its fragment. A fragment is not validated as a design: import_board/3 checks what a board's import needs. from_map(to_map(b)) is {:ok, b}.

iex> Visualize.Chart.Builder.Bundle.from_map(%{provenance: %{schema: 9}, entries: []})
{:error, [{[:provenance, :schema], {:newer_schema, 9, 3}}]}

import_board(store, bundle)

@spec import_board(Visualize.Chart.Builder.Store.store(), t()) ::
  {:ok, imported()} | {:error, [Visualize.Board.error(), ...]}

As import_board/3 with [] options.

import_board(store, bundle, opts)

@spec import_board(Visualize.Chart.Builder.Store.store(), t(), keyword()) ::
  {:ok, imported()} | {:error, [Visualize.Board.error(), ...]}

A board bundle into a store (spec/15 §11.2): {:ok, %{board: board, ids: table, outside: ids}}, the board with every cell's ref rewritten to its component's new id, or {:error, errors}, every fault in the board's path shape.

Every check comes before any entry is put, so a refused bundle leaves the store as it was: the provenance's schema (spec/14 §9.1); the board read by Visualize.Board.from_map/2 under limits:; the entries' count and each entry's depth and items; the board validated with the bundle's entries as its store, a cell whose component the bundle does not carry being {:not_carried, id} at its ref; and each carried theme's name, which the store may not already give a different theme ({:theme_taken, name}). Then the entries go through the store's own import/2, which issues every new id, and a store's refusal is {:store, reason} at []. The board is returned, never put: where a host keeps its boards is its own.

Options

to_json(bundle)

@spec to_json(t()) ::
  {:ok, String.t()}
  | {:error, [Visualize.Board.error(), ...] | {:missing_dependency, :jason}}

The bundle as one JSON document stamped "$version" (spec/14 §19.7, D-217): the provenance with exported_at in ISO 8601, each entry's id as Store.encode_id/1 writes it and its fragment as the document Fragment.to_json/1 writes, and the board in its own JSON form (spec/15 §2.2). A store named by a string or an atom is written as the name's string; any other term as its tagged term (spec/14 §11.2), and one the table has no form for is {:error, [{[:provenance, :store], {:type, :term}}]}. {:error, {:missing_dependency, :jason}} without Jason.

to_map(bundle)

@spec to_map(t()) :: map()

The bundle as plain data: provenance, entries as %{id, name, fragment} — an entry's kind is derived from its fragment (spec/14 §19.8), so the form leaves it out — and board, the board's map form or nil.