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.
The schema version this library writes: 2.
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 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 current() :: pos_integer()
The schema version this library writes: 2.
@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 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(3)
{:error, {:newer_schema, 3, 2}}
iex> Visualize.Chart.Migration.readable(1)
:ok