soma.cell

Kanban board: Card records stored by id in a Map slot, moved between columns by mutating a field and writing back

Demonstrates records in a keyed store, a column roll-up, and counting by a record field.

A Soma program from the records part of the verified corpus: 55 lines, cells Kanban, KanbanTest. It passes soma check with soma 3.2.4.

How it is built

The cell Kanban declares 4 signals in its face: add(title: String, col: String) -> String, move(id: String, col: String) -> Map, column(col: String) -> List and counts() -> Map; keeps 1 memory slot: cards (Map<String, Map>, persistent and consistent). Handlers: add (line 17), move (line 23), column (line 31) and counts (line 35).

The file also holds 1 test cell (KanbanTest) with 6 assertions, run by soma test.

Generated from soma describe on this file.

The source

kanban.cell

// Kanban board: Card records stored by id in a Map slot, moved between
// columns by mutating a field and writing back. Demonstrates records in a
// keyed store, a column roll-up, and counting by a record field.

cell Kanban {
    face {
        signal add(title: String, col: String) -> String
        signal move(id: String, col: String) -> Map
        signal column(col: String) -> List
        signal counts() -> Map
    }

    memory {
        cards: Map<String, Map> [persistent, consistent]
    }

    on add(title: String, col: String) {
        let id = "card-{next_id()}"
        cards.set(id, Card { id: id, title: title, col: col })
        return id
    }

    on move(id: String, col: String) {
        let c = cards.get(id)
        if c == () { return map("error", "no card") }
        c.col = col
        cards.set(id, c)
        return map("id", id, "col", col)
    }

    on column(col: String) {
        return cards.values |> filter(c => c.col == col) |> pluck("title")
    }

    on counts() {
        let vals = cards.values
        return map(
            "todo", count(vals, c => c.col == "todo"),
            "doing", count(vals, c => c.col == "doing"),
            "done", count(vals, c => c.col == "done")
        )
    }
}

cell test KanbanTest {
    rules {
        assert starts_with(add("Spec", "todo"), "card-")
        assert add("Build", "doing") != ""
        assert move(add("Ship", "todo"), "done").col == "done"
        assert counts().done == 1
        assert len(column("todo")) == 1
        assert move("card-999", "done").error == "no card"
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check kanban.cell
✓ All checks passed.
$ soma test kanban.cell
test KanbanTest ...
  ✓ assert starts_with(add("Spec", "todo"), "card-")
  ✓ assert add("Build", "doing") != ""
  ✓ assert move(add("Ship", "todo"), "done").col == "done"
  ✓ assert counts().done == 1
  ✓ assert len(column("todo")) == 1
  ✓ assert move("card-999", "done").error == "no card"

6 tests: 6 passed, 0 failed
$ soma verify kanban.cell
Verifying kanban.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)

Builtins used

count, filter, len, map, next_id, pluck, starts_with

Source file: /corpus/records/kanban.cell · examples/corpus/records/kanban.cell