soma.cell

Mini blog API with tag filtering. Posts carry a list of tags;

GET /posts?tag=X filters in-memory using a pipe over stored records.

A Soma program from the web part of the verified corpus: 61 lines, cells Blog, BlogTest. It passes soma check with soma 3.2.4.

How it is built

The cell Blog declares 4 signals in its face: request(method: String, path: String, body: String) -> Map, create_post(data: Map) -> Map, list_all() -> List and by_tag(tag: String) -> List; keeps 1 memory slot: posts (Map<String, Map>, persistent and consistent). Handlers: create_post (line 17), list_all (line 25), by_tag (line 27) and request (line 45). Private helpers: _demo.

The file also holds 1 test cell (BlogTest) with 1 assertion, run by soma test.

Generated from soma describe on this file.

The source

soma.toml

[package]
name = "web-corpus"
version = "0.1.0"
entry = "todo_state.cell"

[verify]
cells = ["TodoService"]
deadlock_free = true
eventually = ["Done", "Cancelled"]

blog_tags.cell

// Mini blog API with tag filtering. Posts carry a list of tags;
// GET /posts?tag=X filters in-memory using a pipe over stored records.

cell Blog {
    face {
        signal request(method: String, path: String, body: String) -> Map
        signal create_post(data: Map) -> Map
        signal list_all() -> List
        signal by_tag(tag: String) -> List
        promise all_persistent
    }

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

    on create_post(data: Map) {
        let id = to_string(next_id())
        let tagstr = data.tags |> join(",")
        let post = map("id", id, "title", data.title, "body", data.body, "tags", tagstr)
        posts.set(id, post)
        return post
    }

    on list_all() { return posts.values }

    on by_tag(tag: String) {
        let needle = concat(",", concat(tag, ","))
        return posts.values |> filter(p => contains(concat(",", concat(p.tags, ",")), needle))
    }

    on _demo() {
        create_post(map("title", "Hi", "body", "first", "tags", list("soma", "intro")))
        create_post(map("title", "Two", "body", "second", "tags", list("soma")))
        create_post(map("title", "Three", "body", "third", "tags", list("rust")))
        // fingerprint: total/soma-count/rust-count/first-soma-title
        let parts = list(
            to_string(len(list_all())),
            to_string(len(by_tag("soma"))),
            to_string(len(by_tag("rust"))),
            to_string(len(by_tag("missing"))))
        return parts |> join("/")
    }

    on request(method: String, path: String, body: String) {
        let req = map("method", method, "path", path)
        match req {
            {method: "GET",  path: "/posts"}          -> list_all()
            {method: "POST", path: "/posts"}          -> create_post(from_json(body))
            {method: "GET",  path: "/posts/tag/" + t} -> by_tag(t)
            _                                         -> response(404, map("error", "no route"))
        }
    }
}

cell test BlogTest {
    rules {
        assert _demo() == "3/2/1/0"
    }
}

What the toolchain says

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

$ soma check blog_tags.cell
✓ All checks passed.
$ soma test blog_tags.cell
test BlogTest ...
  ✓ assert _demo() == "3/2/1/0"

1 tests: 1 passed, 0 failed
$ soma verify blog_tags.cell
Verifying blog_tags.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
note: the soma.toml beside this file declares [verify] properties; none applies here (no state machine)
VERIFY OK (vacuous — no state machine)

Builtins used

concat, contains, filter, from_json, join, len, list, map, next_id, response, to_string

Source file: /corpus/web/blog_tags.cell · examples/corpus/web/blog_tags.cell