What a fragment is about, and the ids it refers to (spec/14 §19.2, §19.8).
A fragment's kind is derived, never stored: walk down while the fragment sets exactly
one key, and the kind is the last node kind reached. A library body says its kind with a
singular wrapper — %{style: %{…}} — and a hand-written fragment says it by the schema's
own keys; both read the same way here. A fragment about more than one thing at its
shallowest branching point is :design.
An id is {kind, n} (§19.2). refs/1 finds every id a fragment carries, which is
how a bundle takes the closure of an entry, and relocate/2 rewrites them through a
table, which is how an import renumbers (§19.7). Both are deep walks that stop at a
struct, as every walk over a design does (D-92).
Examples
iex> Visualize.Chart.Fragment.kind(%{style: %{stroke_width: 5}})
:style
iex> Visualize.Chart.Fragment.kind(%{styles: %{series: %{stroke_width: 5}}})
:style
iex> Visualize.Chart.Fragment.kind(%{frames: %{main: %{axes: [%{scale: :x, side: :bottom}]}}})
:axis
iex> Visualize.Chart.Fragment.kind(%{frames: %{main: %{margin: %{top: 1}}}})
:margin
iex> Visualize.Chart.Fragment.kind(%{theme: :dark})
:theme
iex> Visualize.Chart.Fragment.kind(%{composite: %{uses: []}})
:composite
iex> Visualize.Chart.Fragment.kind(%{version: 2, theme: :dark, frames: %{main: %{}}})
nil
iex> Visualize.Chart.Fragment.refs(%{style: [{:style, 11}, %{fill: {:style, 3}}]})
[{:style, 3}, {:style, 11}]
Summary
Functions
The name of a design's frame; "main" when it has none or several.
The frame a node of a design is drawn in (spec/14 §2.1, §18.16): a mark's or a label's
own frame, else the design's one frame; with several and none said — the validator's
fault, and a design being built — main when there is one, else the first by name, so
a tool always has a frame to read.
The path of a design's frame, and its name (spec/14 §2.1, #383): a design holds its
frames by name, so the builder addresses frames.<name> where it used to address
frame. A design with one frame gives that one, whatever it is called; an empty or
many-framed fragment gives "main", the name a migrated design takes (§9). A name is
its string (§1.4, D-195).
The fragment to_json/1 wrote (#465): tagged terms back as %Visualize.Chart.Use{},
ids, variables and atoms, and the "$version" stamp read and dropped. An id whose kind
is not one a fragment can have is left as it was read, so no atom is made from a
document's kind. from_json(to_json(f)) is f.
The kind of a fragment (§19.8): the last node kind reached walking down while exactly one
key is set. nil when the fragment is many things — it branches at the top, or its one
key is a design scalar such as version — which is no kind of fragment: the thing that
is many kinds is a composite, whose sites are each one.
Every kind a fragment can be, and so every kind an id can carry: the schema's node kinds
but the design, and :composite (§19.6, §19.8). A design is the deployed tier, not a
kind of fragment.
from_json/2 with the version the document was stamped with: {:ok, fragment, version},
the current version for a document with no stamp (spec/14 §9.3, D-139). A reader that
migrates what it reads — a theme document — needs the version to migrate from, which
from_json/2 drops. The errors are from_json/2's.
Every id a fragment refers to, once each, sorted (§19.7). Sorted rather than in the order met, because the order a map is walked in is the runtime's and not a promise.
A fragment with every id rewritten through a table, and the ids the table did not carry, sorted (§19.7). An id that is not in the table is left as it is — it resolves as a mask (§19.2) — and is returned so an import can report it.
The kind a singular wrapper key names — :style for style: — or nil for any other
key, composite included (spec/14 §19.2).
A fragment of any kind as the JSON of §11 and §19.7 (#465): use sites as $use, ids as
$id, variables as $var, and the document stamped "$version" with the schema
version it was written at (spec/14 §9.1). It is not validated as a design — a fragment
may be partial — so a host that keeps its library in a database can write whatever
Store.put/2 hands it. {:error, {:missing_dependency, :jason}} without Jason, as
Visualize.Chart.to_json/1. A board, %{board: map}, is written in the board's own JSON
form with its own "$version" inside the board object, and read back through it
(spec/14 §22.1).
A singular-wrapper body as {kind, body} — %{style: %{…}} is {:style, %{…}} — or nil
for anything else, a composite included (spec/14 §19.2).
Types
@type id() :: {atom(), pos_integer()}
An id: the kind of thing, and a number its store never repeats (§19.2).
Functions
The name of a design's frame; "main" when it has none or several.
The frame a node of a design is drawn in (spec/14 §2.1, §18.16): a mark's or a label's
own frame, else the design's one frame; with several and none said — the validator's
fault, and a design being built — main when there is one, else the first by name, so
a tool always has a frame to read.
iex> design = %{frames: %{history: %{}, now: %{}}}
iex> Visualize.Chart.Fragment.frame_of(design, %{type: :line, frame: :now})
:now
iex> Visualize.Chart.Fragment.frame_of(%{frames: %{history: %{}, now: %{}}}, %{type: :line})
:history
iex> Visualize.Chart.Fragment.frame_of(%{frames: %{history: %{}}}, nil)
:history
The path of a design's frame, and its name (spec/14 §2.1, #383): a design holds its
frames by name, so the builder addresses frames.<name> where it used to address
frame. A design with one frame gives that one, whatever it is called; an empty or
many-framed fragment gives "main", the name a migrated design takes (§9). A name is
its string (§1.4, D-195).
iex> Visualize.Chart.Fragment.frame_path(%{frames: %{history: %{kind: :cartesian}}})
[:frames, :history]
iex> Visualize.Chart.Fragment.frame_path(%{})
[:frames, "main"]
@spec from_json(String.t(), keyword()) :: {:ok, map()} | {:error, {:json, String.t()} | {:newer_schema, pos_integer(), pos_integer()} | {:limits, [Visualize.Chart.Validator.error(), ...]} | {:tags, [Visualize.Chart.Validator.error(), ...]} | {:missing_dependency, :jason}}
The fragment to_json/1 wrote (#465): tagged terms back as %Visualize.Chart.Use{},
ids, variables and atoms, and the "$version" stamp read and dropped. An id whose kind
is not one a fragment can have is left as it was read, so no atom is made from a
document's kind. from_json(to_json(f)) is f.
Before a key is decoded (spec/14 §9.1, §9.2): a stamp, or a design fragment's own
"version", newer than this library reads is {:error, {:newer_schema, needs, this}},
and a document over the depth or items limit is {:error, {:limits, errors}}, with
limits: as Visualize.Chart.from_map/2 takes them. A document with no stamp — written
before the stamp existed — is read as the current version. {:error, {:json, message}}
for text that is not JSON. A document holding a tag the one table of spec/14 §11.2 does
not read, unknown or retired, is {:error, {:tags, errors}}, each at its path (D-182).
The kind of a fragment (§19.8): the last node kind reached walking down while exactly one
key is set. nil when the fragment is many things — it branches at the top, or its one
key is a design scalar such as version — which is no kind of fragment: the thing that
is many kinds is a composite, whose sites are each one.
@spec kinds() :: [atom(), ...]
Every kind a fragment can be, and so every kind an id can carry: the schema's node kinds
but the design, and :composite (§19.6, §19.8). A design is the deployed tier, not a
kind of fragment.
@spec read_json(String.t(), keyword()) :: {:ok, map(), pos_integer()} | {:error, {:json, String.t()} | {:newer_schema, pos_integer(), pos_integer()} | {:limits, [Visualize.Chart.Validator.error(), ...]} | {:tags, [Visualize.Chart.Validator.error(), ...]} | {:missing_dependency, :jason}}
from_json/2 with the version the document was stamped with: {:ok, fragment, version},
the current version for a document with no stamp (spec/14 §9.3, D-139). A reader that
migrates what it reads — a theme document — needs the version to migrate from, which
from_json/2 drops. The errors are from_json/2's.
iex> {:ok, json} = Visualize.Chart.Fragment.to_json(%{theme: %{axis: "#666666"}})
iex> Visualize.Chart.Fragment.read_json(json)
{:ok, %{theme: %{axis: "#666666"}}, 3}
Every id a fragment refers to, once each, sorted (§19.7). Sorted rather than in the order met, because the order a map is walked in is the runtime's and not a promise.
An id is a {kind, n} tuple whose kind is one of kinds/0 and whose n is a positive
integer. A tuple of any other shape — an anchor {:axis, :y}, a {:field, :t} — is a
value and is left alone.
A fragment with every id rewritten through a table, and the ids the table did not carry, sorted (§19.7). An id that is not in the table is left as it is — it resolves as a mask (§19.2) — and is returned so an import can report it.
iex> table = %{{:style, 11} => {:style, 1}}
iex> Visualize.Chart.Fragment.relocate(%{style: [{:style, 11}, {:style, 9}]}, table)
{%{style: [{:style, 1}, {:style, 9}]}, [{:style, 9}]}
The kind a singular wrapper key names — :style for style: — or nil for any other
key, composite included (spec/14 §19.2).
iex> Visualize.Chart.Fragment.singular(:scale)
:scale
iex> Visualize.Chart.Fragment.singular(:styles)
nil
A fragment of any kind as the JSON of §11 and §19.7 (#465): use sites as $use, ids as
$id, variables as $var, and the document stamped "$version" with the schema
version it was written at (spec/14 §9.1). It is not validated as a design — a fragment
may be partial — so a host that keeps its library in a database can write whatever
Store.put/2 hands it. {:error, {:missing_dependency, :jason}} without Jason, as
Visualize.Chart.to_json/1. A board, %{board: map}, is written in the board's own JSON
form with its own "$version" inside the board object, and read back through it
(spec/14 §22.1).
iex> {:ok, json} = Visualize.Chart.Fragment.to_json(%{style: %{stroke_width: 5}})
iex> Visualize.Chart.Fragment.from_json(json)
{:ok, %{style: %{stroke_width: 5}}}
A singular-wrapper body as {kind, body} — %{style: %{…}} is {:style, %{…}} — or nil
for anything else, a composite included (spec/14 §19.2).
iex> Visualize.Chart.Fragment.wrapped(%{style: %{stroke_width: 5}})
{:style, %{stroke_width: 5}}
iex> Visualize.Chart.Fragment.wrapped(%{styles: %{series: %{}}})
nil