A spreadsheet in a map: the backend that needs no network.
It carries out the same ops the Google backend does, so a plan can be run and
read back in a test, and it refuses what Google refuses (writing to a sheet
that is not there, adding one that is), so a plan that forgets its
Sheetshow.Op.AddSheet fails here too.
iex> alias Sheetshow.{Memory, Op}
iex> plan = [
...> Op.AddSheet.new("Costs"),
...> Op.PutCells.new(Sheetshow.row(["Rent", 1000], sheet: "Costs"))
...> ]
iex> {:ok, memory} = Memory.run(plan, Memory.new())
iex> Memory.read!("Costs", memory) |> Sheetshow.to_rows()
[["Rent", 1000]]A memory holds content, not a grid: it has no row or column count, so the
space below the last cell is simply empty and a write can go anywhere. Each
sheet is a plain map, %{cells: %{{row, col} => cell}, col_widths: %{}, row_heights: %{}}, and the cells in it carry their sheet, so what read/2
returns is what the layout functions in Sheetshow take.
The functions here are the interpreter itself. Sheetshow.Workbook.memory/1
puts a memory behind the same Sheetshow.run/2 and reads the other backends
answer to, which is the way to use one in a test.
Summary
Functions
The column widths and row heights of a sheet, by index.
An empty spreadsheet, or one with the sheets named already there.
The cells inside a range, in reading order. Empty cells are not cells, so they are not there.
Same as read/2, raising on failure.
Carries out an op or a plan, in order, and gives back the spreadsheet it
leaves behind. Mirrors Sheetshow.run/2, so a plan runs here or at Google
with the same call shape.
Same as run/2, raising on failure.
The sheet titles, sorted. A memory keeps no sheet order of its own.
Types
@type sheet() :: %{ cells: %{ required({non_neg_integer(), non_neg_integer()}) => Sheetshow.Cell.t() }, col_widths: %{required(non_neg_integer()) => pos_integer()}, row_heights: %{required(non_neg_integer()) => pos_integer()} }
Functions
The column widths and row heights of a sheet, by index.
iex> alias Sheetshow.{Memory, Op}
iex> memory = Memory.run!(Op.SetDimensions.new("Costs", :cols, 0..1, 180), Memory.new(["Costs"]))
iex> Sheetshow.Memory.dimensions!("Costs", memory)
%{col_widths: %{0 => 180, 1 => 180}, row_heights: %{}}
An empty spreadsheet, or one with the sheets named already there.
iex> Sheetshow.Memory.new(["Costs", "log"]) |> Sheetshow.Memory.titles()
["Costs", "log"]
@spec read(Sheetshow.Range.t() | String.t(), t()) :: {:ok, [Sheetshow.Cell.t()]} | {:error, Sheetshow.Error.t()}
The cells inside a range, in reading order. Empty cells are not cells, so they are not there.
iex> alias Sheetshow.{Memory, Op}
iex> memory = Memory.run!(Op.PutCells.new(Sheetshow.row([1, 2]), "Costs"), Memory.new(["Costs"]))
iex> {:ok, cells} = Memory.read("Costs!A1:A1", memory)
iex> Enum.map(cells, & &1.value)
[1]
@spec read!(Sheetshow.Range.t() | String.t(), t()) :: [Sheetshow.Cell.t()]
Same as read/2, raising on failure.
@spec run(Sheetshow.Op.t() | [Sheetshow.Op.t()], t()) :: {:ok, t()} | {:error, Sheetshow.Error.t()}
Carries out an op or a plan, in order, and gives back the spreadsheet it
leaves behind. Mirrors Sheetshow.run/2, so a plan runs here or at Google
with the same call shape.
Nothing is half-done: the first op that fails ends the run and the memory you passed in is the one you still have.
iex> alias Sheetshow.{Memory, Op}
iex> {:error, error} = Memory.run(Op.PutCells.new(Sheetshow.row([1], sheet: "Costs")), Memory.new())
iex> error.reason
:unknown_sheet
@spec run!(Sheetshow.Op.t() | [Sheetshow.Op.t()], t()) :: t()
Same as run/2, raising on failure.
The sheet titles, sorted. A memory keeps no sheet order of its own.
iex> Sheetshow.Memory.new() |> Sheetshow.Memory.titles()
[]