Visualize.Chart.Fragment (Visualize v0.8.3)

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

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()) :: String.t() | atom()

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

frame_of(design, arg2)

@spec frame_of(term(), term()) :: String.t() | atom()

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

frame_path(design)

@spec frame_path(map()) :: [atom() | String.t()]

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

from_json(json, opts \\ [])

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

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.

read_json(json, opts \\ [])

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

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

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