Schema versions and the migrations between them (spec/14 §9).
current/0 is the version this library writes. migrate/1 steps an older design
forward one version at a time through the migration registered for each step, so a
stored design never goes stale. Version 2 is the first step: a design holds its frames
by name (spec/14 §2.1, §4.1, #383), so a version 1 design's singular frame becomes
frames: %{main: …} and the size it may still carry is dropped, the render giving one
now (§4.2, #427). A stored version 1 design therefore keeps working through
Visualize.Chart.from_map/1, which migrates before it validates.
Summary
Functions
The design in the shape this library writes, within the current version: a :text
given as a list of parts — the form an earlier draft wrote — becomes the string with
interpolation (spec/14 §7.1), walking the map by the schema so a text is known to be
one. Every other value passes through as it is.
canonical/1 of a node of kind alone: a fragment of that kind (spec/14 §19.6).
The schema version this library writes: 3.
The type a flat style key of an earlier version had (spec/14 §9, D-172), nil for any
other key: fill, stroke, curve and effect are flat when their value is not a part,
and are read by these types then.
The type a key had in an earlier version, for a key of a kind node that the current
schema no longer has; nil for any other key.
The design at the current version.
The styles of a node of kind in their parts and nothing else rewritten (spec/14 §3.1,
§9, D-172): what the validator reads, so a design or a fragment written in the flat keys
validates as the parts it means, while a text given as parts keeps the paths its faults
are reported at.
The version gate of spec/14 §9.1 alone: {:error, {:newer_schema, version, current}}
for an integer above current/0, else :ok.
Functions
The design in the shape this library writes, within the current version: a :text
given as a list of parts — the form an earlier draft wrote — becomes the string with
interpolation (spec/14 §7.1), walking the map by the schema so a text is known to be
one. Every other value passes through as it is.
iex> Visualize.Chart.Migration.canonical(%{version: 2, labels: [%{anchor: :title, text: ["u: ", Visualize.Chart.var(:unit)]}]})
%{version: 2, labels: [%{anchor: :title, text: "u: \#{var(:unit)}"}]}
@spec canonical(map(), Visualize.Chart.Schema.kind()) :: map()
canonical/1 of a node of kind alone: a fragment of that kind (spec/14 §19.6).
iex> Visualize.Chart.Migration.canonical(%{fill: :red, fill_opacity: 0.5}, :style)
%{fill: %{type: :solid, color: {:color, :red}, opacity: 0.5}}
@spec current() :: pos_integer()
The schema version this library writes: 3.
@spec flat_type(atom()) :: Visualize.Chart.Schema.type() | nil
The type a flat style key of an earlier version had (spec/14 §9, D-172), nil for any
other key: fill, stroke, curve and effect are flat when their value is not a part,
and are read by these types then.
iex> Visualize.Chart.Migration.flat_type(:stroke_width)
:size
@spec legacy_type(atom(), atom()) :: Visualize.Chart.Schema.type() | nil
The type a key had in an earlier version, for a key of a kind node that the current
schema no longer has; nil for any other key.
The codec reads a document by the current schema, so a key only an earlier version had
would be read untyped and its values left as strings. This is what it reads such a key
by instead, so a document from that version is typed before migrate/1 reshapes it
(spec/14 §9, #479). Each entry belongs to the step that removed the key: version 1's
design-level frame is one :frame node, which the 1 → 2 step moves to frames.main.
iex> Visualize.Chart.Migration.legacy_type(:design, :frame)
{:node, :frame}
iex> Visualize.Chart.Migration.legacy_type(:design, :frames)
nil
@spec migrate(term()) :: {:ok, map()} | {:error, [Visualize.Chart.Validator.error(), ...]}
The design at the current version.
A design already there passes through unchanged; an older one is migrated step by
step; a version above current/0 is {:newer_schema, v, current} (spec/14 §9.1), and
one below 1, missing or not an integer is an error at [:version] too.
@spec parts(map(), Visualize.Chart.Schema.kind()) :: map()
The styles of a node of kind in their parts and nothing else rewritten (spec/14 §3.1,
§9, D-172): what the validator reads, so a design or a fragment written in the flat keys
validates as the parts it means, while a text given as parts keeps the paths its faults
are reported at.
iex> Visualize.Chart.Migration.parts(%{styles: %{s: %{stroke_width: 2}}}, :design)
%{styles: %{"s" => %{stroke: %{width: 2}}}}
@spec readable(term()) :: :ok | {:error, {:newer_schema, pos_integer(), pos_integer()}}
The version gate of spec/14 §9.1 alone: {:error, {:newer_schema, version, current}}
for an integer above current/0, else :ok.
It is for a reader that holds a version before it holds a design — a parsed JSON
document, a fragment document's "$version", a bundle's provenance — so a newer
document is refused before anything of it is read. Any other version is :ok here and
left to migrate/1 to judge.
iex> Visualize.Chart.Migration.readable(4)
{:error, {:newer_schema, 4, 3}}
iex> Visualize.Chart.Migration.readable(1)
:ok