joetjen.net
EN DE
Web framework

The demo app

Webglue grows together with a companion application: a small ticket system that uses each library as it arrives.

In development · v0.4

Layout

PartHolds
libs/ticketsThe domain, with no HTTP in it
apps/webRoutes, handlers, middleware and the supervision tree
prx webglue watchA WebSocket client written in Praxis, to watch pushes arrive

The domain

A ticket is a map, and each transition returns #(:ok ticket) or #(:error reason). A ticket moves from open to assigned, between assigned and on hold, and finally to closed.

libs/tickets/src/webglue/demo/ticket.prax
(defn assign [ticket who] :spec [map any -> tuple]
  (if (open? ticket)
      #(:ok (map/merge ticket {:status :assigned :assignee who}))
      #(:error :closed)))

The HTTP side

The pieces are the ones on Core concepts: the route table, the application function, the supervision tree, validation on raising a ticket, a socket with presence, and a push on every change. A refused transition becomes a 409, a missing login a 401, an unknown ticket a 404.

A session with it

$ prx run --no-halt webglue_demo            # WEBGLUE_ADAPTER=wrangler for Wrangler

$ curl -d 'subject=Printer is on fire' localhost:4000/tickets
#1 [open] Printer is on fire

$ curl -X POST localhost:4000/tickets/1/assign
log in first                                # 401

$ curl -X POST -H 'X-Demo-User: jan' localhost:4000/tickets/1/assign
#1 [assigned] Printer is on fire -> jan

$ curl -X POST -H 'X-Demo-User: jan' localhost:4000/tickets/1/close
$ curl -X POST -H 'X-Demo-User: jan' localhost:4000/tickets/1/assign
refused: closed                             # 409

$ websocat ws://localhost:4000/socket/tickets   # watch the pushes live
$ prx routes webglue.demo.web                   # print the route table

X-Demo-User is a convenience that skips the cookie session so curl needs no cookie jar; the real sign-in is POST /session with a user field.

What it does not do yet

  • No database — the store is in memory, behind the data-layer seam, and stays swappable.
  • Identity is a name; there is no authorization.
  • Plain text and form bodies; no content negotiation.
  • No browser UI — pushes are watched with a WebSocket client.