Documentation ¶
Overview ¶
Package typedctx provides generic helpers for storing / retrieving values from a `context.Context`.
Breaking a controller down into small pieces with `Handler`s means that each piece either needs to re-calculate results from other stages or fetch the previously computed result from `context`.
var CtxExpensiveObject = typedctx.NewKey[ExpensiveComputation]() func (h *ComputeHandler) Handle(ctx context.Context) { ctx = CtxExpensiveObject.WithValue(ctx, myComputedExpensiveObject) } func (h *UseHandler) Handle(ctx context.Context) { precomputedExpensiveObject = CtxExpensiveObject.MustValue(ctx) // do something with object }
`Handlers` are typically chained in a way that preserves the context between handlers, but not always.
For example:
var CtxExpensiveObject = typedctx.NewKey[ExpensiveComputation]() func (h *ComputeHandler) Handle(ctx context.Context) { ctx = CtxExpensiveObject.WithValue(ctx, myComputedExpensiveObject) } func (h *DecorateHandler) Handle(ctx context.Context) { ComputeHandler{}.Handle(ctx) // this fails, because the ctx passed into the wrapped handler isn't passed back out CtxExpensiveObject.MustValue(ctx) }
To deal with these cases, `typedctx` provides a `Boxed` context type that instead stores a pointer to the object, with additional helpers for making a "space" for the pointer to be filled in later.
var CtxExpensiveObject = typedctx.Boxed[ExpensiveComputation](nil) func (h *ComputeHandler) Handle(ctx context.Context) { ctx = CtxExpensiveObject.WithValue(ctx, myComputedExpensiveObject) } func (h *DecorateHandler) Handle(ctx context.Context) { // adds an empty pointer ctx = CtxExpensiveObject.WithBox(ctx) // fills in the pointer - note that the implementation of ComputeHandler didn't change ComputeHandler{}.Handle(ctx) // now this succeeds, and returns the unboxed value CtxExpensiveObject.MustValue(ctx) }
Index ¶
- type Box
- type BoxedKey
- func (k *BoxedKey[V]) BoxBuilder(id handler.Key) handler.Builder
- func (k *BoxedKey[V]) MustValue(ctx context.Context) V
- func (k *BoxedKey[V]) Value(ctx context.Context) V
- func (k *BoxedKey[V]) WithBox(ctx context.Context) context.Context
- func (k *BoxedKey[V]) WithValue(ctx context.Context, val V) context.Context
- type DefaultingKey
- type Key
- type MustValueContext
- type SettableContext
- type ValueContext
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BoxedKey ¶
type BoxedKey[V any] struct { // contains filtered or unexported fields }
BoxedKey is a type that is used as a key in a context.Context that points to a handle containing the desired value. This allows a handler higher up in the chain to carve out a spot to be filled in by other handlers. It can also be used to hold non-comparable objects by wrapping them with a pointer.
func Boxed ¶
Boxed creates a new BoxedKey with a default value
Example ¶
type ExpensiveComputation struct { result string } CtxExpensiveObject := Boxed[*ExpensiveComputation](nil) // the compute handler performs some computation that we wish to re-use computeHandler := handler.NewHandlerFromFunc(func(ctx context.Context) { myComputedExpensiveObject := ExpensiveComputation{result: "computed"} CtxExpensiveObject.WithValue(ctx, &myComputedExpensiveObject) }, "compute") decorateHandler := handler.NewHandlerFromFunc(func(ctx context.Context) { // adds an empty box ctx = CtxExpensiveObject.WithBox(ctx) // fills in the box with the value computeHandler.Handle(ctx) // returns the unboxed value fmt.Println(CtxExpensiveObject.MustValue(ctx).result) }, "decorated") ctx := context.Background() decorateHandler.Handle(ctx)
Output: computed
func (*BoxedKey[V]) BoxBuilder ¶
BoxBuilder returns a handler.Builder that calls Boxed before calling the next handler in the chain. This is a shortcut for using Boxed context values in handler chains, i.e. by
type DefaultingKey ¶
type DefaultingKey[V comparable] struct { // contains filtered or unexported fields }
DefaultingKey is a type that is used as a key in a context.Context for a specific type of value, but returns the default value for V if unset.
func WithDefault ¶
func WithDefault[V comparable](defaultValue V) *DefaultingKey[V]
WithDefault creates a new DefaultingKey with the given default value
Example ¶
type ExpensiveComputation struct { result string } CtxExpensiveObject := WithDefault[*ExpensiveComputation](&ExpensiveComputation{result: "pending"}) useHandler := handler.NewHandlerFromFunc(func(ctx context.Context) { // fetch the computed value after the computation fmt.Println(CtxExpensiveObject.MustValue(ctx).result) }, "use") // the compute handler performs some computation that we wish to re-use computeHandler := handler.NewHandlerFromFunc(func(ctx context.Context) { // fetch the default value before the computation fmt.Println(CtxExpensiveObject.MustValue(ctx).result) myComputedExpensiveObject := ExpensiveComputation{result: "computed"} ctx = CtxExpensiveObject.WithValue(ctx, &myComputedExpensiveObject) useHandler.Handle(ctx) }, "compute") ctx := context.Background() computeHandler.Handle(ctx)
Output: pending computed
func (*DefaultingKey[V]) MustValue ¶
func (k *DefaultingKey[V]) MustValue(ctx context.Context) V
func (*DefaultingKey[V]) Value ¶
func (k *DefaultingKey[V]) Value(ctx context.Context) V
type Key ¶
type Key[V any] struct{}
Key is a type that is used as a key in a context.Context for a specific type of value V. It mimics the context.Context interface
Example ¶
type ExpensiveComputation struct { result string } CtxExpensiveObject := NewKey[*ExpensiveComputation]() useHandler := handler.NewHandlerFromFunc(func(ctx context.Context) { // fetch the computed value after the computation fmt.Println(CtxExpensiveObject.MustValue(ctx).result) }, "use") // the compute handler performs some computation that we wish to re-use computeHandler := handler.NewHandlerFromFunc(func(ctx context.Context) { myComputedExpensiveObject := ExpensiveComputation{result: "computed"} ctx = CtxExpensiveObject.WithValue(ctx, &myComputedExpensiveObject) useHandler.Handle(ctx) }, "compute") ctx := context.Background() computeHandler.Handle(ctx)
Output: computed