What a row means: a keyword list of {name, type}, and the casting either
way across it.
iex> schema = [item: :string, cost: :decimal, on: :date]
iex> Sheetshow.Schema.validate(schema)
:okA schema is a plain keyword list, not a struct, so it composes with ++ and
reads as data in a config file. The order is the column order.
| Type | Written from | Read back as |
|---|---|---|
:string | String.t() | String.t() |
:integer | integer() | integer() |
:float | number() | float() |
:boolean | true / false | true / false |
:date | Date.t() | Date.t() |
:datetime | NaiveDateTime.t() / DateTime.t() | NaiveDateTime.t() |
:time | Time.t() | Time.t() |
:decimal | a numeric String.t() | String.t() |
:json | any term JSON can encode | the decoded term |
:decimal is text on both sides, so a double never touches it and 1000.00
stays 1000.00. It is a string here because Sheetshow has no dependencies:
Decimal.to_string/1 on the way in and Decimal.new/1 on the way out is the
whole of it, and your app decides which library that is.
Writing is strict. encode/2 refuses a value of the wrong type or a key
the schema does not have: your code wrote it, so your code should hear about
it. Reading is lenient. cast/2 is given whatever a human left in the
cell, so it coerces what Sheets itself would have coerced (a number in a
:string column, a whole number in an :integer one), and where a value
will not cast at all it hands back nil and an error beside it rather than
failing the read.
Summary
Functions
The values of one row, in schema order, as a record, with an error beside each cell that would not cast.
Reads a cell the way a :boolean column does, which is also how a log reads
its deleted flag. Blank is false; TRUE, true and 1 are true.
The column names, in order, as they read in a header row.
A record as the cell values of one row, in schema order. A column the record
says nothing about is nil, which is an empty cell.
Every type a column can have.
Checks a schema: at least one column, names that are atoms and appear once,
types from types/0.
Types
Functions
@spec cast([term()], t()) :: {fields(), %{optional(name()) => Sheetshow.Error.t()}}
The values of one row, in schema order, as a record, with an error beside each cell that would not cast.
Nothing here fails: a cell Sheets or a person left in a state the column did
not expect comes back as nil in the record and an entry in the errors, so
one bad cell costs you that field rather than the row or the read.
iex> Sheetshow.Schema.cast(["Rent", "1000.00"], item: :string, cost: :decimal)
{%{item: "Rent", cost: "1000.00"}, %{}}
iex> {record, errors} = Sheetshow.Schema.cast(["abc"], cost: :integer)
iex> {record.cost, errors.cost.reason}
{nil, :cast}
Reads a cell the way a :boolean column does, which is also how a log reads
its deleted flag. Blank is false; TRUE, true and 1 are true.
iex> Sheetshow.Schema.cast_boolean("TRUE")
{:ok, true}
iex> Sheetshow.Schema.cast_boolean("")
{:ok, false}
iex> Sheetshow.Schema.cast_boolean("perhaps")
:error
The column names, in order, as they read in a header row.
iex> Sheetshow.Schema.columns(item: :string, cost: :decimal)
["item", "cost"]
@spec encode(fields(), t()) :: {:ok, [Sheetshow.Value.t()]} | {:error, Sheetshow.Error.t()}
A record as the cell values of one row, in schema order. A column the record
says nothing about is nil, which is an empty cell.
Strict: a value of the wrong type, or a key the schema has no column for, is an error.
iex> Sheetshow.Schema.encode(%{item: "Rent", cost: "1000.00"}, item: :string, cost: :decimal)
{:ok, ["Rent", "1000.00"]}
iex> {:error, %Sheetshow.Error{reason: :invalid_record}} =
...> Sheetshow.Schema.encode(%{cost: 1000.0}, cost: :decimal)
@spec types() :: [type()]
Every type a column can have.
iex> :decimal in Sheetshow.Schema.types()
true
@spec validate(term()) :: :ok | {:error, Sheetshow.Error.t()}
Checks a schema: at least one column, names that are atoms and appear once,
types from types/0.
iex> {:error, %Sheetshow.Error{reason: :invalid_schema}} =
...> Sheetshow.Schema.validate(item: :text)