Sheetshow.Backend behaviour (Sheetshow v0.1.0)

Copy Markdown View Source

What a backend has to do, and what it can promise.

Every impure function in Sheetshow takes a Sheetshow.Workbook and asks the module named on it to do the work. Three come with the library (Google, the in-memory one and xlsx), and they are not equals: one evaluates formulas and resolves an append server-side, another does neither. capabilities/1 is how that difference is said out loud rather than discovered. Reach for a backend through Sheetshow.Workbook.google/2, Sheetshow.Workbook.memory/1 or Sheetshow.Workbook.xlsx/2; the modules behind them are the library's own business.

iex> Sheetshow.Workbook.memory() |> Sheetshow.Backend.supports?(:evaluates_formulas)
false

The capabilities:

capabilitywhat it promises
:atomic_batcha plan is applied whole or not at all
:conditional_writea write can be refused if the spreadsheet has changed since you read it
:dimensionscolumn widths and row heights are kept
:evaluates_formulasa formula written here is worked out, so it can be read back as a value
:server_side_appendan append resolves against the last row at the moment it is applied, so two writers cannot land on each other
:stylesa cell's style is kept

:server_side_append is the one Sheetshow.Log rests on, and :conditional_write is what gives Sheetshow.Table the guarantee Google cannot; What Sheetshow Can Promise says how.

Summary

Callbacks

Trades credentials for a token. Only a backend that has credentials has this.

What this backend promises for this workbook. Every capability in known/0 has an answer.

Makes the workbook ready to use, and learns its sheets.

Asks the spreadsheet which sheets it has.

The cells in a range. Every range a backend is given names its sheet.

The values in a range, as rows.

The values in several ranges, in the order they were asked for.

Carries out a plan.

Functions

What the workbook's backend promises.

:ok when the backend promises the capability, and an error naming it when it does not. What a caller uses to refuse before writing rather than after.

Every capability a backend answers for.

Whether the workbook's backend promises one thing.

Types

capabilities()

@type capabilities() :: %{required(capability()) => boolean()}

capability()

@type capability() ::
  :atomic_batch
  | :conditional_write
  | :dimensions
  | :evaluates_formulas
  | :server_side_append
  | :styles

Callbacks

authenticate(t)

(optional)
@callback authenticate(Sheetshow.Workbook.t()) ::
  {:ok, Sheetshow.Workbook.t()} | {:error, Sheetshow.Error.t()}

Trades credentials for a token. Only a backend that has credentials has this.

capabilities(t)

@callback capabilities(Sheetshow.Workbook.t()) :: capabilities()

What this backend promises for this workbook. Every capability in known/0 has an answer.

Per workbook rather than per backend, because a backend's promises can depend on where it is pointed: the same xlsx codec over a local file cannot refuse a write that would clobber someone, and over a store that speaks If-Match it can.

connect(t)

@callback connect(Sheetshow.Workbook.t()) ::
  {:ok, Sheetshow.Workbook.t()} | {:error, Sheetshow.Error.t()}

Makes the workbook ready to use, and learns its sheets.

fetch_sheets(t)

@callback fetch_sheets(Sheetshow.Workbook.t()) ::
  {:ok, Sheetshow.Workbook.t()} | {:error, Sheetshow.Error.t()}

Asks the spreadsheet which sheets it has.

read_cells(t, t)

@callback read_cells(Sheetshow.Range.t(), Sheetshow.Workbook.t()) ::
  {:ok, [Sheetshow.Cell.t()]} | {:error, Sheetshow.Error.t()}

The cells in a range. Every range a backend is given names its sheet.

read_rows(t, t)

@callback read_rows(Sheetshow.Range.t(), Sheetshow.Workbook.t()) ::
  {:ok, [[term()]]} | {:error, Sheetshow.Error.t()}

The values in a range, as rows.

read_rows_batch(list, t)

@callback read_rows_batch([Sheetshow.Range.t()], Sheetshow.Workbook.t()) ::
  {:ok, [[[term()]]]} | {:error, Sheetshow.Error.t()}

The values in several ranges, in the order they were asked for.

run(plan, t)

@callback run(Sheetshow.Op.plan(), Sheetshow.Workbook.t()) ::
  {:ok, Sheetshow.Workbook.t()} | {:error, Sheetshow.Error.t()}

Carries out a plan.

Functions

capabilities(workbook)

@spec capabilities(Sheetshow.Workbook.t()) :: capabilities()

What the workbook's backend promises.

iex> Sheetshow.Workbook.memory() |> Sheetshow.Backend.capabilities() |> Map.fetch!(:atomic_batch)
true

ensure(workbook, capability)

@spec ensure(Sheetshow.Workbook.t(), capability()) ::
  :ok | {:error, Sheetshow.Error.t()}

:ok when the backend promises the capability, and an error naming it when it does not. What a caller uses to refuse before writing rather than after.

iex> {:error, error} = Sheetshow.Workbook.memory() |> Sheetshow.Backend.ensure(:evaluates_formulas)
iex> error.reason
:unsupported

known()

@spec known() :: [capability()]

Every capability a backend answers for.

iex> Sheetshow.Backend.known() |> Enum.member?(:server_side_append)
true

supports?(workbook, capability)

@spec supports?(Sheetshow.Workbook.t(), capability()) :: boolean()

Whether the workbook's backend promises one thing.

iex> Sheetshow.Workbook.memory() |> Sheetshow.Backend.supports?(:server_side_append)
true