typedctx

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2024 License: Apache-2.0 Imports: 3 Imported by: 1

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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Box

type Box[V any] struct {
	// contains filtered or unexported fields
}

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

func Boxed[V any](defaultValue V) *BoxedKey[V]

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

func (k *BoxedKey[V]) BoxBuilder(id handler.Key) handler.Builder

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

func (*BoxedKey[V]) MustValue

func (k *BoxedKey[V]) MustValue(ctx context.Context) V

func (*BoxedKey[V]) Value

func (k *BoxedKey[V]) Value(ctx context.Context) V

func (*BoxedKey[V]) WithBox

func (k *BoxedKey[V]) WithBox(ctx context.Context) context.Context

func (*BoxedKey[V]) WithValue

func (k *BoxedKey[V]) WithValue(ctx context.Context, val V) context.Context

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

func (*DefaultingKey[V]) WithValue

func (k *DefaultingKey[V]) WithValue(ctx context.Context, val V) context.Context

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

func NewKey

func NewKey[V any]() *Key[V]

NewKey creates a new Key

func (*Key[V]) MustValue

func (k *Key[V]) MustValue(ctx context.Context) V

func (*Key[V]) Value

func (k *Key[V]) Value(ctx context.Context) (V, bool)

func (*Key[V]) WithValue

func (k *Key[V]) WithValue(ctx context.Context, val V) context.Context

type MustValueContext

type MustValueContext[V any] interface {
	MustValue(ctx context.Context) V
}

type SettableContext

type SettableContext[V any] interface {
	WithValue(ctx context.Context, val V) context.Context
}

type ValueContext

type ValueContext[V any] interface {
	Value(ctx context.Context) (V, bool)
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL