Visualize.Chart.Fragment (Visualize v0.2.35)

Copy Markdown View Source

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

Types

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

The fragment to_json/1 wrote (#465): tagged terms back as %Visualize.Chart.Use{}, ids, variables and atoms. 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. {:error, {:json, message}} for text that is not JSON. 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.

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. 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 singular-wrapper body as {kind, body} — %{style: %{…}} is {:style, %{…}} — or nil for anything else, a composite included (spec/14 §19.2).

Types

id()

@type id() :: {atom(), pos_integer()}

An id: the kind of thing, and a number its store never repeats (§19.2).

Functions

frame_name(arg1)

@spec frame_name(term()) :: atom()

The name of a design's frame; :main when it has none or several.

frame_path(design)

@spec frame_path(map()) :: [atom()]

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

iex> Visualize.Chart.Fragment.frame_path(%{frames: %{history: %{kind: :cartesian}}})
[:frames, :history]

iex> Visualize.Chart.Fragment.frame_path(%{})
[:frames, :main]

from_json(json)

@spec from_json(String.t()) ::
  {:ok, map()} | {:error, {:json, String.t()} | {:missing_dependency, :jason}}

The fragment to_json/1 wrote (#465): tagged terms back as %Visualize.Chart.Use{}, ids, variables and atoms. 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. {:error, {:json, message}} for text that is not JSON. from_json(to_json(f)) is f.

kind(fragment)

@spec kind(map()) :: atom() | nil

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.

kinds()

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

refs(fragment)

@spec refs(term()) :: [id()]

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.

relocate(fragment, table)

@spec relocate(term(), %{required(id()) => id()}) :: {term(), [id()]}

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

singular(key)

@spec singular(term()) :: atom() | nil

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

to_json(fragment)

@spec to_json(map()) :: {:ok, String.t()} | {:error, {:missing_dependency, :jason}}

A fragment of any kind as the JSON of §11 and §19.7 (#465): use sites as $use, ids as $id, variables as $var. 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.

iex> {:ok, json} = Visualize.Chart.Fragment.to_json(%{style: %{stroke_width: 5}})
iex> Visualize.Chart.Fragment.from_json(json)
{:ok, %{style: %{stroke_width: 5}}}

wrapped(fragment)

@spec wrapped(map()) :: {atom(), map()} | nil

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