Fude.Spec (Fude v0.1.0)

Copy Markdown View Source

A chart as plain data, to save and load back.

spec = Fude.Spec.dump(chart)
#=> %{"fude" => 1, "kind" => "bar", "x" => "region", "y" => %{"field" => "revenue", "aggregate" => "sum"}}

{:ok, chart} = Fude.Spec.load(spec)

A spec is a map with string keys and JSON values, so it goes into a :map column, a JSON file or a message as it is, and load(dump(chart)) is the chart again. It carries its version ("fude" => 1): load/1 takes the specs of every earlier version and gives today's chart.

A spec does not hold the chart's data: it names it ("data" => "sales"), and the chart gets it when it draws. dump(chart, data: true) puts the data in ("datasets"), for a file that stands alone.

The shape

A chart of one layer has its kind, channels and options at the top:

%{
  "fude" => 1,
  "kind" => "line",
  "title" => "Visits",
  "x" => %{"field" => "day", "time" => "week"},
  "y" => "count",
  "color" => "source",
  "curve" => "monotone"
}

A chart of several layers has them in "layers", each with its kind, channels and options:

%{"fude" => 1, "layers" => [%{"kind" => "area", ...}, %{"kind" => "rule", "y" => 1000}]}

A layer, and an annotation, may have an "id", text that names it among the others: a chart shared with Fude.Issho gives each one, so that two people who change two layers at once both have their change.

A channel is a field's name, or a map of the keys of Fude.Channel ("format" => "currency:EUR" for {:currency, "EUR"}); a constant is %{"value" => 1000}.

Errors

load/1 checks the whole spec and gives every error, each with where it is and what to do:

{:error, [%{path: ["y", "aggregate"], message: ~s|must be one of "count", "sum", ...; did you mean "sum"?|}]}

Summary

Types

An error of a spec: where (a path of keys and indexes), and what is wrong.

t()

A spec: a map with string keys and JSON values.

Functions

The chart as a spec. With data: true, the spec holds the chart's data too, under "datasets", as Fude.Data normalizes it. With layers: true, a chart of one layer has it in "layers" too, as a chart of several does.

The errors as lines of text, one for each: y.aggregate: must be ....

The chart of a spec, or every error of it. A spec of an earlier version is first migrated to this one.

The chart of a spec; raises an ArgumentError with every error.

The version of the specs that dump/1 gives.

Types

error()

@type error() :: %{path: [String.t() | non_neg_integer()], message: String.t()}

An error of a spec: where (a path of keys and indexes), and what is wrong.

t()

@type t() :: %{required(String.t()) => term()}

A spec: a map with string keys and JSON values.

Functions

dump(chart, opts \\ [])

@spec dump(Fude.Chart.t(), keyword()) :: t()

The chart as a spec. With data: true, the spec holds the chart's data too, under "datasets", as Fude.Data normalizes it. With layers: true, a chart of one layer has it in "layers" too, as a chart of several does.

format_errors(errors)

@spec format_errors([error()]) :: String.t()

The errors as lines of text, one for each: y.aggregate: must be ....

load(spec)

@spec load(t()) :: {:ok, Fude.Chart.t()} | {:error, [error()]}

The chart of a spec, or every error of it. A spec of an earlier version is first migrated to this one.

load!(spec)

@spec load!(t()) :: Fude.Chart.t()

The chart of a spec; raises an ArgumentError with every error.

version()

@spec version() :: pos_integer()

The version of the specs that dump/1 gives.