Documentation
¶
Index ¶
- Variables
- func WithRegistry(ctx context.Context, r *Registry) context.Context
- type Hooks
- type Option
- type Registry
- func (r *Registry) BeginScope(parent context.Context) (context.Context, func())
- func (r *Registry) Clear()
- func (r *Registry) Get(key string) any
- func (r *Registry) IsRegistered(key string) bool
- func (r *Registry) Keys() []string
- func (r *Registry) Register(key string, factory func(ctx context.Context) any)
- func (r *Registry) RegisterFunc(key string, factory func() any)
- func (r *Registry) RegisterScoped(key string, factory func(ctx context.Context) any)
- func (r *Registry) Resolve(ctx context.Context, key string) any
- func (r *Registry) ResolveOK(ctx context.Context, key string) (any, bool)
- func (r *Registry) Set(key string, value any)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Default = New()
Default is the global registry — the entry point for apps that don't need isolated registries (e.g. tests, multi-tenant setups).
Functions ¶
Types ¶
type Hooks ¶
type Hooks struct {
// OnResolve fires once per Resolve call with the key and the time the
// whole resolution took (chain walk + factory or cache lookup).
OnResolve func(key string, dur time.Duration)
// OnScopeBegin fires when BeginScope opens a scope.
OnScopeBegin func()
// OnScopeEnd fires when a scope is released (via endScope or ctx
// cancellation).
OnScopeEnd func()
}
Hooks are optional observability callbacks. A nil field is never called, so an empty Hooks value (the default) costs a single nil check per event and nothing else.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is a name-keyed factory/value store. It is a general-purpose primitive: not HTTP-specific, not a DI framework on its own.
func NewFrom ¶
NewFrom creates a registry that falls back to parent for keys it doesn't have registered itself. It shares parent's scope store, so scopes are consistent across the whole chain.
Example ¶
NewFrom builds a registry that falls back to parent for keys it doesn't have itself — handy for tests that only need to override one dependency.
package main
import (
"fmt"
"github.com/arpaad/grr"
)
func main() {
parent := grr.New()
parent.Set("db", "parent-db")
child := grr.NewFrom(parent)
fmt.Println(child.Get("db")) // falls back to parent
child.Set("db", "child-db")
fmt.Println(child.Get("db"), parent.Get("db")) // child overrides, parent untouched
}
Output: parent-db child-db parent-db
func RegistryFromCtx ¶
RegistryFromCtx recovers the registry attached to ctx via WithRegistry, falling back to Default if none was attached.
func (*Registry) BeginScope ¶
BeginScope starts a new scope tied to ctx and returns a derived context plus an endScope function that releases the scope's cached instances. endScope is safe to call multiple times. If ctx is cancelled before endScope is called explicitly, the scope is cleaned up automatically.
Scope storage is shared across a whole parent/child registry chain, so a scoped entry registered on a parent resolves correctly even when BeginScope was called on a child.
Example ¶
RegisterScoped ties an instance to a scope (see BeginScope): the same instance comes back for every Resolve call within that scope.
package main
import (
"context"
"fmt"
"github.com/arpaad/grr"
)
func main() {
r := grr.New()
calls := 0
r.RegisterScoped("conn", func(ctx context.Context) any {
calls++
return fmt.Sprintf("conn-%d", calls)
})
ctx, end := r.BeginScope(context.Background())
defer end()
a := r.Resolve(ctx, "conn")
b := r.Resolve(ctx, "conn")
fmt.Println(a, b, a == b)
}
Output: conn-1 conn-1 true
func (*Registry) Clear ¶
func (r *Registry) Clear()
Clear removes all entries from this registry. It does not touch the parent chain or any in-flight scopes — mainly useful for test teardown.
func (*Registry) Get ¶
Get is sugar for Resolve(context.Background(), key). Panics if key resolves to a scoped entry — there is no scope in a bare background context.
func (*Registry) IsRegistered ¶
IsRegistered reports whether key is registered in this registry or any of its parents.
Example ¶
package main
import (
"fmt"
"github.com/arpaad/grr"
)
func main() {
r := grr.New()
fmt.Println(r.IsRegistered("db"))
r.Set("db", "conn")
fmt.Println(r.IsRegistered("db"))
}
Output: false true
func (*Registry) Keys ¶
Keys returns the keys registered directly in this registry, not walking the parent chain. Order is unspecified. Mainly for introspection and startup validation (see gold.Validate).
func (*Registry) Register ¶
Register registers a factory under key. Whether it behaves as transient or singleton is entirely up to the factory's own logic.
func (*Registry) RegisterFunc ¶
RegisterFunc is sugar for Register with a context-less factory.
func (*Registry) RegisterScoped ¶
RegisterScoped registers a factory that produces one instance per active scope (see BeginScope). Resolving without an active scope panics.
func (*Registry) Resolve ¶
Resolve looks up key, walking the parent chain if necessary, and produces an instance via the registered factory. Panics if key isn't registered anywhere in the chain, or if key is scoped but ctx carries no active scope (see BeginScope).
Example (NestedScopedDependency) ¶
A scoped factory may resolve another scoped key from the same scope — useful when one dependency is built from another (e.g. a repo needs a transaction). The opposite — a cycle — panics instead of deadlocking.
package main
import (
"context"
"fmt"
"github.com/arpaad/grr"
)
func main() {
r := grr.New()
r.RegisterScoped("db", func(ctx context.Context) any { return "db-conn" })
r.RegisterScoped("repo", func(ctx context.Context) any {
db := r.Resolve(ctx, "db")
return fmt.Sprintf("repo(%s)", db)
})
ctx, end := r.BeginScope(context.Background())
defer end()
fmt.Println(r.Resolve(ctx, "repo"))
}
Output: repo(db-conn)
func (*Registry) ResolveOK ¶
ResolveOK is like Resolve but reports a missing key with ok == false instead of panicking — for genuinely conditional lookups (e.g. an optional, feature-flagged registration). It does NOT soften real misuse: resolving a scoped key with no active scope, a circular dependency, or resolving after a scope ended still panic, because those are bugs, not conditions to branch on.
func (*Registry) Set ¶
Set registers a fixed value under key — always returns the same value. Sugar for a singleton factory.
Example ¶
Set/Get is sugar for a fixed value — always the same instance.
package main
import (
"fmt"
"github.com/arpaad/grr"
)
func main() {
r := grr.New()
r.Set("greeting", "hello")
fmt.Println(r.Get("greeting"))
}
Output: hello
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package middleware provides net/http (and any router built on it, e.g.
|
Package middleware provides net/http (and any router built on it, e.g. |