Documentation ¶
Overview ¶
Package module provides a way to do dependency injection, with type-safe, without performance penalty. See [examples_test.go](./examples_test.go) for the basic usage.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoPrivoder = fmt.Errorf("can't find module")
ErrNoPrivoder means that it can't find a module within a given context. Usually it misses adding that module to a repo.
Functions ¶
This section is empty.
Types ¶
type Module ¶
type Module[T any] struct { // contains filtered or unexported fields }
Module provides a module to inject and retreive an instance with its type.
Example ¶
package main import ( "context" "fmt" "github.com/googollee/module" ) type DB interface { Target() string } type db struct { target string } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB keyPrefix string } var ( ModuleCache = module.New[*Cache]() ProvideCache = ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, keyPrefix: "cache", }, nil }) ) func main() { repo := module.NewRepo() // No order required when adding providers repo.Add(ProvideCache) repo.Add(ModuleDB.ProvideValue(&db{target: "local.db"})) ctx := context.Background() ctx, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } db := ModuleDB.Value(ctx) cache := ModuleCache.Value(ctx) fmt.Println("db target:", db.Target()) fmt.Println("cache fallback target:", cache.fallback.Target()) }
Output: db target: local.db cache fallback target: local.db
Example (CreateWithError) ¶
package main import ( "context" "fmt" "github.com/googollee/module" ) type DB interface { Target() string } type db struct { target string } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB keyPrefix string } var ModuleCache = module.New[*Cache]() func main() { repo := module.NewRepo() repo.Add(ModuleDB.ProvideValue(&db{target: "local.db"})) repo.Add(ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { _ = ModuleDB.Value(ctx) return nil, fmt.Errorf("new cache error") })) ctx := context.Background() _, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } }
Output: inject error: creating with module *module_test.Cache: new cache error
Example (CreateWithPanic) ¶
package main import ( "context" "fmt" "github.com/googollee/module" ) type DB interface { Target() string } type db struct { target string } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB keyPrefix string } var ModuleCache = module.New[*Cache]() func main() { repo := module.NewRepo() repo.Add(ModuleDB.ProvideValue(&db{target: "localhost.db"})) repo.Add(ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { _ = ModuleDB.Value(ctx) panic(fmt.Errorf("new cache error")) })) defer func() { err := recover() fmt.Println("panic:", err) }() ctx := context.Background() _, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } }
Output: panic: new cache error
Example (DuplicatingProviders) ¶
package main import ( "fmt" "regexp" "github.com/googollee/module" ) type DB interface { Target() string } type db struct { target string } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() func main() { defer func() { p := recover().(string) // Remove the file line info for testing. fmt.Println("panic:", regexp.MustCompile(`at .*`).ReplaceAllString(p, "at <removed file and line>")) }() repo := module.NewRepo() repo.Add(ModuleDB.ProvideValue(&db{target: "real.db"})) repo.Add(ModuleDB.ProvideValue(&db{target: "fake.db"})) }
Output: panic: already have a provider with type "module_test.DB", added at <removed file and line>
Example (LoadOtherValue) ¶
package main import ( "context" "fmt" "github.com/googollee/module" ) type DB interface { Target() string } type db struct { target string } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB keyPrefix string } var ( ModuleCache = module.New[*Cache]() ProvideCache = ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, keyPrefix: "cache", }, nil }) ) func main() { type Key string targetKey := Key("target") repo := module.NewRepo() repo.Add(ModuleDB.ProvideWithFunc(func(ctx context.Context) (DB, error) { // Load the target value from the context. target := ctx.Value(targetKey).(string) return &db{ target: target, }, nil })) repo.Add(ProvideCache) // Store the target value in the context. ctx := context.WithValue(context.Background(), targetKey, "target.db") ctx, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } db := ModuleDB.Value(ctx) cache := ModuleCache.Value(ctx) fmt.Println("db target:", db.Target()) fmt.Println("cache fallback target:", cache.fallback.Target()) }
Output: db target: target.db cache fallback target: target.db
Example (NewPrefixInSpan) ¶
package main import ( "context" "fmt" "github.com/googollee/module" ) type DB interface { Target() string } type db struct { target string } func (db *db) Target() string { return db.target } var ModuleDB = module.New[DB]() type Cache struct { fallback DB keyPrefix string } var ( ModuleCache = module.New[*Cache]() ProvideCache = ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, keyPrefix: "cache", }, nil }) ) func main() { repo := module.NewRepo() repo.Add(ModuleDB.ProvideValue(&db{target: "local.db"})) repo.Add(ProvideCache) ctx := context.Background() ctx, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } db := ModuleDB.Value(ctx) cache := ModuleCache.Value(ctx) fmt.Println("before span, db target:", db.Target()) fmt.Println("before span, cache prefix:", cache.keyPrefix) { // a new context in the span ctx := ModuleCache.With(ctx, &Cache{ fallback: db, keyPrefix: "span", }) db := ModuleDB.Value(ctx) cache := ModuleCache.Value(ctx) fmt.Println("in span, db target:", db.Target()) fmt.Println("in span, cache prefix:", cache.keyPrefix) } db = ModuleDB.Value(ctx) cache = ModuleCache.Value(ctx) fmt.Println("after span, db target:", db.Target()) fmt.Println("after span, cache fallback target:", cache.keyPrefix) }
Output: before span, db target: local.db before span, cache prefix: cache in span, db target: local.db in span, cache prefix: span after span, db target: local.db after span, cache fallback target: cache
Example (NotExistingProvider) ¶
package main import ( "context" "fmt" "github.com/googollee/module" ) type DB interface { Target() string } var ModuleDB = module.New[DB]() type Cache struct { fallback DB keyPrefix string } var ( ModuleCache = module.New[*Cache]() ProvideCache = ModuleCache.ProvideWithFunc(func(ctx context.Context) (*Cache, error) { db := ModuleDB.Value(ctx) return &Cache{ fallback: db, keyPrefix: "cache", }, nil }) ) func main() { ctx := context.Background() repo := module.NewRepo() repo.Add(ProvideCache) // repo.Add(ModuleDB.ProvideValue()) _, err := repo.InjectTo(ctx) if err != nil { fmt.Println("inject error:", err) return } }
Output: inject error: creating with module module_test.DB: can't find module
func (Module[T]) ProvideValue ¶
ProvideValue returns a provider which always provides given `value` as instances.
func (Module[T]) ProvideWithFunc ¶
ProvideWithFunc returns a provider which provides instances creating from `ctor` function.
type Provider ¶
type Provider interface {
// contains filtered or unexported methods
}
Provider is the interface to provide an Instance.
type Repo ¶
type Repo struct {
// contains filtered or unexported fields
}
Repo is a repository of modules, and to inject instances creating by modules into a context.
func (*Repo) InjectTo ¶
InjectTo injects instances created by modules into a context `ctx`. It returns a new context with all injections. If any module creates an instance with an error, `InjectTo` returns that error with the module name. Injecting instances only create once if necessary. Calling `InjectTo` mutlple times share instances between returning contexts. InjectTo ignores all new providers adding to the Repo after the first run. So adding all providers before calling `InjectTo`.