MishkaCmsBuilder.EditorSession.Keeper (Mishka CMS Builder v0.0.1-alpha.2)

Copy Markdown View Source

Writes the edits an editing session holds and no draft does — on every change, on the way out of the editor, and before a reopened session is moved off its draft.

Why the session, and not the LiveView

An edit lands in the session first: MishkaCmsBuilder.EditorSession stores it in ETS, where it outlives the LiveView that made it. It reaches a draft row a moment later, when the chrome writes it — and that moment used to belong to the LiveView. The write ran in a task linked to it, and what the write settled (the draft it made, the revision it carried) was recorded by the LiveView when the answer came back. A tab closed, a page left or a socket dropped inside that moment took the write down with the LiveView, and the next visit detached the session from its draft and threw the unwritten edit away with the tree it replaced. A drop followed by closing the tab at once was enough.

So a write here:

  • runs in a process of its own under MishkaCmsCore.Runtime.TaskSupervisor, linked to nobody, so the caller exiting does not stop it;
  • records what it wrote on the session itself — EditorSession.mark_saved/2 with the draft and the revision it serialized — so the session knows its draft even when no LiveView is left to hear the answer;
  • waits its turn: one write per session at a time, and each reads the session as stored when its turn comes. A write asked for on the way out therefore follows the one already running and writes only what that one did not carry, and an older document never lands over a newer one.

A session with nothing unwritten is left alone, so asking costs one ETS read.

Summary

Types

What a keep did: wrote, found nothing to write, or did not write.

Functions

Writes token's session with write when it holds edits no draft has, and marks it saved as of the revision written.

Types

outcome()

@type outcome() ::
  {:ok, struct(), MishkaCmsBuilder.EditorSession.t()}
  | {:clean, MishkaCmsBuilder.EditorSession.t()}
  | :running
  | :error
  | term()

What a keep did: wrote, found nothing to write, or did not write.

Functions

keep(token, write, timeout \\ :infinity)

@spec keep(
  String.t(),
  (MishkaCmsBuilder.EditorSession.t() -> {:ok, struct()} | term()),
  timeout()
) ::
  outcome()

Writes token's session with write when it holds edits no draft has, and marks it saved as of the revision written.

write receives the session as stored when this write's turn comes, and answers {:ok, draft} for a draft it wrote; anything else is a write that did not happen and is answered as it came.

Answers {:ok, draft, session} with the session as marked, {:clean, session} when there was nothing to write, :error when there is no such session, {:error, {:exit, reason}} when the write crashed, and :running when timeout passed first — the write then goes on without the caller, and still records what it wrote.