Skip to content

Commit 29888fb

Browse files
committed
docs: document injection-site singleton, bare-list send, and web.shutdown
- dependency-injection.md: `deps { x: I as singleton }` marks a scope at the injection site; note it as the idiomatic way to share a stateful service. - web.md: `res.send` accepts a bare List<T>/T[] (serialized as a JSON array); the "keep state across requests" section shows the injection-site marker; and a new note on web.shutdown() / self-terminating servers.
1 parent f55f259 commit 29888fb

2 files changed

Lines changed: 54 additions & 9 deletions

File tree

docs/dependency-injection.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,21 @@ A binding is `transient` by default (constructed per dependent); mark it
206206
module App { bind Cache -> LruCache as singleton }
207207
```
208208

209+
You can also mark a scope **at the injection site**, right on the dependency —
210+
no module `bind` needed:
211+
212+
```x
213+
class OrderController implements WebRequestHandler {
214+
deps { store: OrderStore as singleton } // one shared OrderStore
215+
216+
}
217+
```
218+
219+
`x: I as singleton` binds `I` as a singleton for the whole program (an explicit
220+
module `bind … as singleton` still takes precedence). This is the idiomatic way
221+
to give a **stateful service** one shared instance: without it the service is
222+
transient and, being reconstructed per use, silently never accumulates state.
223+
209224
Singletons live for the whole process (and are never freed by design); transients
210225
are created where needed.
211226

docs/web.md

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,27 +130,44 @@ The response is *mutable* — fill it in, don't return it.
130130
| `res.sendText(code, body)` | reply `code` with a plain-text body |
131131

132132
`res.send` / `req.parse` work for any `event` or compound `type`; the compiler
133-
derives the codec automatically. A field may be a `List<T>` — it is stored
134-
growably in memory and serialized as a JSON array, so a service can accumulate
135-
posted items and hand the list straight back. A request that no controller
136-
matches gets a `404`.
133+
derives the codec automatically. The payload can also be a **bare `List<T>` or
134+
`T[]`** — it serializes to a JSON array, so a controller can return a
135+
repository's list directly with no wrapper DTO:
136+
137+
```x
138+
action handle(req: HttpRequest, res: HttpResponse) {
139+
res.send(users.findAll()) // -> [ {...}, {...} ] (List<User>)
140+
}
141+
```
142+
143+
A `List<T>` field on a DTO works the same way, and is stored growably in memory,
144+
so a service can accumulate posted items and hand the list straight back. A
145+
request that no controller matches gets a `404`.
137146

138147
### Keeping state across requests
139148

140-
A service that accumulates data across requests must be bound **`as singleton`**
141-
so every request shares one instance:
149+
A service that accumulates data across requests must be a **singleton** so every
150+
request shares one instance — either mark the dependency at the injection site
151+
or bind it in the module:
142152

143153
```x
144154
class ItemStore implements Store {
145155
state { items: List<Item> = empty List<Item> }
146156
consumer add(it: Item) { this.items.push(it) }
147157
...
148158
}
149-
module App { bind Store -> ItemStore as singleton } // shared, not per-resolve
159+
160+
class ItemController implements WebRequestHandler {
161+
deps { store: Store as singleton } // marker: one shared store
162+
...
163+
}
164+
// — or, equivalently, in the module —
165+
module App { bind Store -> ItemStore as singleton }
150166
```
151167

152-
Without `as singleton` the service is resolved fresh each time and its state
153-
never accumulates. See `examples/web/web_store_demo.xi` for the full pattern.
168+
Left transient (the default), the service is resolved fresh each request and its
169+
state never accumulates. See `examples/web/web_store_demo.xi` for the full
170+
pattern.
154171

155172
## The `WebTransport`
156173

@@ -186,6 +203,19 @@ $ xc app.xi && ./build/app
186203
web: serving on http://0.0.0.0:8080
187204
```
188205

206+
`web.serve` blocks until the server is stopped. **`web.shutdown()`** stops it —
207+
it unblocks `serve` so the call returns and the process exits. It is safe to call
208+
from another thread, so a background timer can bound the server's lifetime (the
209+
serve demos use this so a run never blocks):
210+
211+
```x
212+
async entry main(args: String[]) -> Integer {
213+
runWithDelay(30000) { web.shutdown() } // self-terminate after 30s
214+
web.serve(8080)
215+
return 0
216+
}
217+
```
218+
189219
## HTTPS
190220

191221
`web.serveTLS(port, certPath, keyPath)` runs the same handler stack over TLS.

0 commit comments

Comments
 (0)