Each module README now says where the module sits in the dependency graph, why it is its own module, and lists use cases for application developers.
83 lines
3.4 KiB
Markdown
83 lines
3.4 KiB
Markdown
# towel
|
|
|
|
Request-scoped actor, organization, collection and locale values carried through `context.Context`.
|
|
|
|
`import "git.golem15.com/golem15/summercms/modules/towel"`
|
|
|
|
**Name:** You carry it everywhere and it marks your spot; it carries the actor, organization and locale through each request.
|
|
|
|
## Overview
|
|
|
|
`towel` replaces the request-global state that WinterCMS reads through facades (the current locale, the acting user) with explicit values on the request context. Middleware stores a value once, and any code further down the call chain that receives the context reads it back without a global lookup. [surf](../surf/README.md) sets the locale from `Accept-Language` (or the signed-in user's preferred locale) and the organization for every request, [phrasebook](../phrasebook/README.md) reads the locale when it translates, and [cabana](../cabana/README.md) sets it while localizing admin schemas.
|
|
|
|
## Role in the framework
|
|
|
|
A dependency-free leaf for request-scoped values. `surf` sets them, and `phrasebook`, `cabana` and plugin code read them. It replaces WinterCMS facades for per-request state, so it has to be importable by any module without cycles.
|
|
|
|
Use cases:
|
|
|
|
- Read the acting user or organization in a service without passing them through every call.
|
|
- Translate in the request's locale from deep inside a plugin.
|
|
- Set a locale explicitly in a job or a test.
|
|
|
|
## Features
|
|
|
|
- Four independent string values, each with a setter and a getter: actor (`towel.WithActor`, `towel.Actor`), organization (`towel.WithOrganization`, `towel.Organization`), collection (`towel.WithCollection`, `towel.Collection`) and locale (`towel.WithLocale`, `towel.Locale`).
|
|
- Unexported context key types, so no other package can read or overwrite the values by accident.
|
|
- Getters return `(value, ok)`: a value that was set to an empty string is distinguishable from one that was never set.
|
|
- Nil-safe: a setter given a nil context starts from `context.Background()`, and a getter given a nil context reports `false`.
|
|
|
|
## Usage
|
|
|
|
```go
|
|
package blog
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/towel"
|
|
)
|
|
|
|
// withAcmeScope tags every request with the organization and collection it serves.
|
|
func withAcmeScope(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
ctx := towel.WithOrganization(r.Context(), "acme")
|
|
ctx = towel.WithCollection(ctx, "blog")
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
|
|
func listPosts(w http.ResponseWriter, r *http.Request) {
|
|
org, _ := towel.Organization(r.Context())
|
|
locale, ok := towel.Locale(r.Context())
|
|
if !ok {
|
|
locale = "en"
|
|
}
|
|
fmt.Fprintf(w, "posts for %s in %s\n", org, locale)
|
|
}
|
|
```
|
|
|
|
## API reference
|
|
|
|
| Identifier | Description |
|
|
|------------|-------------|
|
|
| `towel.WithActor` / `towel.Actor` | Store and read the acting user or client identifier. |
|
|
| `towel.WithOrganization` / `towel.Organization` | Store and read the organization (tenant) the request belongs to. |
|
|
| `towel.WithCollection` / `towel.Collection` | Store and read the collection the request operates on. |
|
|
| `towel.WithLocale` / `towel.Locale` | Store and read the locale used for translations and localized responses. |
|
|
|
|
## Dependencies
|
|
|
|
- SummerCMS modules: none.
|
|
- Third-party: none.
|
|
- Standard library: `context`.
|
|
|
|
## Testing
|
|
|
|
```sh
|
|
go test ./modules/towel/...
|
|
```
|
|
|
|
The tests exercise plain contexts and need no external services.
|