di

package module
v0.0.5 Latest Latest
Warning

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

Go to latest
Published: Mar 14, 2023 License: MIT Imports: 5 Imported by: 0

README

Coditory - Go Dependency Injection

GitHub release Go Reference Go Report Card Build Status Coverage

🚧 This library as under heavy development until release of version 1.x.x 🚧

Dependency injection for Go projects, targeted for web applications that create DI context during the startup and operates on collections of dependencies.

  • Register dependencies by name and type
  • Register multiple dependencies per type
  • Conditional dependency registration
  • Simple dependency retrieval - no manual casting or additional callbacks
  • Simple setup - no generators
  • Detection of slow dependency creation (TODO)
  • Initialization and finalization mechanisms (TODO)

Getting started

Installation

Get the dependency with:

go get github.com/coditory/go-di

and import it in the project:

import "github.com/coditory/go-di"

The exported package is di, basic usage:

import "github.com/coditory/go-di"

func main() {
  ctxb := di.NewContextBuilder()
  // Add dependencies
  ctxb.Add(&foo)
  // Build the di context
  ctx := ctxb.Build()
  // Retrieve dependencies from the context
}

Full example

package main

import (
  "fmt"
  "github.com/coditory/go-di"
)

type Baz interface { Id() string }
type Foo struct { id string }
func (f *Foo) Id() string { return f.id }
type Bar struct { id string }
func (b *Bar) Id() string { return b.id }

func main() {
  foo := Foo{id: "foo1"}
  foo2 := Foo{id: "foo2"}
  bar := Bar{id: "baz"}

  ctxb := di.NewContextBuilder()
  ctxb.AddAs(new(Baz), &foo)
  ctxb.AddAs(new(Baz), &foo2)
  ctxb.AddAs(new(Baz), &bar)
  ctx := ctxb.Build()

  fmt.Printf("first implementation: %+v\n", di.Get[Baz](ctx))
  for i, baz := range di.GetAll[Baz](ctx) {
    fmt.Printf("%d: %+v\n", i, baz)
  }
}
// output:
// first implementation: &{id:foo1}
// 0: &{id:foo1}
// 1: &{id:foo2}
// 2: &{id:baz}

Usage

Dependencies by type

Add dependency reference and retrieve by the reference type:

ctxb := di.NewContextBuilder()
ctxb.Add(&foo)
ctxb.Add(&foo2)
ctx := ctxb.Build()
suite.Equal(&foo, di.Get[*Foo](ctx))
suite.Equal([]*Foo{&foo, &foo2}, di.GetAll[*Foo](ctx))

Add dependency (by value) and retrieve by the type:

ctxb := di.NewContextBuilder()
ctxb.Add(foo)
ctxb.Add(foo2)
ctx := ctxb.Build()
suite.Equal(foo, di.Get[Foo](ctx))
foos := di.GetAll[Foo](ctx)
suite.Equal(2, len(foos))
suite.Equal("foo", foos[0].Id())
suite.Equal("foo2", foos[1].Id())

Dependencies by interface

To retrieve a dependency by the interface it must be registered explicitly by the interface type:

ctxb := di.NewContextBuilder()
// ctx.Add(&foo) <- will not work!
ctxb.AddAs(new(Baz), &foo)
ctxb.AddAs(new(Baz), &foo2)
ctx := ctxb.Build()
suite.Equal(&foo, di.Get[Baz](ctx))
suite.Equal([]Baz{&foo, &foo2}, di.GetAll[Baz](ctx))

Single dependency can be registered multiple times:

ctxb := di.NewContextBuilder()
ctxb.Add(&foo)
ctxb.AddAs(new(Baz), &foo)
ctxb.Add(&foo2)
ctxb.AddAs(new(Baz), &foo2)
ctx := ctxb.Build()
suite.Equal(&foo, di.Get[Baz](ctx))
suite.Equal([]Baz{&foo, &foo2}, di.GetAll[Baz](ctx))
suite.Equal(&foo, di.Get[*Foo](ctx))
suite.Equal([]*Foo{&foo, &foo2}, di.GetAll[*Foo](ctx))

Named dependencies

To differentiate dependencies of the same type you can name them. There can be only one dependency per name.

ctxb := di.NewContextBuilder()
ctxb.AddAs(new(Baz), &foo)
ctxb.AddNamedAs("special-foo", new(Baz), &foo2)
// ctxb.AddNamedAs("special-foo", new(Baz), &foo3) <- panics
ctx := ctxb.Build()
suite.Equal(&foo, di.Get[Baz](ctx))
suite.Equal(&foo2, di.GetNamed[Baz](ctx, "special-foo"))
suite.Equal([]Baz{&foo, &foo2}, di.GetAll[Baz](ctx))

Lazy dependencies

Lazy dependencies are created when retrieved:

type Boo struct {
  foo *Foo
  bar *Bar
}
ctxb := di.NewContextBuilder()
ctxb.Provide(func() *Foo {
  return &foo
})
ctxb.Provide(func() *Bar {
  return &bar
})
ctxb.Provide(func(foo *Foo, bar *Bar) *Boo {
  return &Boo{foo: foo, bar: bar}
})
ctx := ctxb.Build()
boo := di.Get[*Boo](ctx)
suite.Equal(&foo, boo.foo)
suite.Equal(&bar, boo.bar)

If a dependency requires a long list of other dependencies then inject *Context:

ctxb.Provide(func(ctx *di.Context) *Boo {
  return &Boo{foo: di.Get[*Foo](ctx), bar: di.Get[*Bar](ctx)}
})

There are the same variations for ctxb.Add* and ctxb.Provide* functions:

ctxb.Provide(createFoo)
ctxb.ProvideAs(new(Baz), createFoo)
ctxb.ProvideNamed("special-foo", createFoo)
ctxb.ProvideNamedAs("special-foo", new(Baz), createFoo)

When dependency creator is a separate (non-inlined) function, then creation happens only once:

creations := 0
createFoo := func() *Foo {
  creations++
  return &foo
}
ctxb := di.NewContextBuilder()
ctxb.Provide(createFoo)
ctxb.ProvideAs(new(Baz), createFoo)
ctx := ctxb.Build()
di.Get[Baz](ctx)
di.Get[*Foo](ctx)
suite.Equal(1, creations)

Documentation

Index

Constants

View Source
const (
	ErrTypeDependencyCreation = iota
	ErrTypeDuplicatedName
	ErrTypeDuplicatedRegistration
	ErrTypeMissingDependency
	ErrTypeInvalidType
	ErrTypeInvalidConstructor
	ErrTypeCyclicDependency
	ErrTypeDependencyInitialization
	ErrTypeDependencyShutdown
	ErrTypeLifecycle
)

Variables

View Source
var ErrSkippedDependency = errors.New("skipped dependency")

Functions

func Get

func Get[T any](ctx *Context) T

func GetAll

func GetAll[T any](ctx *Context) []T

func GetNamed

func GetNamed[T any](ctx *Context, name string) T

Types

type Context

type Context struct {
	// contains filtered or unexported fields
}

func (*Context) GetAllByType

func (ctx *Context) GetAllByType(atype any) []any

func (*Context) GetAllByTypeOrErr

func (ctx *Context) GetAllByTypeOrErr(atype any) ([]any, *Error)

func (*Context) GetByType

func (ctx *Context) GetByType(atype any) any

func (*Context) GetByTypeOrErr

func (ctx *Context) GetByTypeOrErr(atype any) (any, *Error)

func (*Context) GetNamed

func (ctx *Context) GetNamed(name string) any

func (*Context) GetNamedOrErr

func (ctx *Context) GetNamedOrErr(name string) (any, *Error)

func (*Context) Initialize added in v0.0.5

func (ctx *Context) Initialize()

func (*Context) InitializeOrErr added in v0.0.5

func (ctx *Context) InitializeOrErr() *Error

func (*Context) Shutdown added in v0.0.5

func (ctx *Context) Shutdown(context stdcontext.Context)

func (*Context) ShutdownOrErr added in v0.0.5

func (ctx *Context) ShutdownOrErr(context stdcontext.Context) *Error

type ContextBuilder

type ContextBuilder struct {
	// contains filtered or unexported fields
}

func NewContextBuilder

func NewContextBuilder() *ContextBuilder

func (*ContextBuilder) Add

func (ctxb *ContextBuilder) Add(ctor any)

func (*ContextBuilder) AddAs

func (ctxb *ContextBuilder) AddAs(atype any, ctor any)

func (*ContextBuilder) AddAsOrErr

func (ctxb *ContextBuilder) AddAsOrErr(atype any, ctor any) *Error

func (*ContextBuilder) AddNamed

func (ctxb *ContextBuilder) AddNamed(name string, ctor any)

func (*ContextBuilder) AddNamedAs

func (ctxb *ContextBuilder) AddNamedAs(name string, atype any, ctor any)

func (*ContextBuilder) AddNamedAsOrErr

func (ctxb *ContextBuilder) AddNamedAsOrErr(name string, atype any, ctor any) *Error

func (*ContextBuilder) AddNamedOrErr

func (ctxb *ContextBuilder) AddNamedOrErr(name string, ctor any) *Error

func (*ContextBuilder) AddOrErr

func (ctxb *ContextBuilder) AddOrErr(ctor any) *Error

func (*ContextBuilder) Build

func (ctxb *ContextBuilder) Build() *Context

func (*ContextBuilder) Provide added in v0.0.3

func (ctxb *ContextBuilder) Provide(ctor any)

func (*ContextBuilder) ProvideAs added in v0.0.3

func (ctxb *ContextBuilder) ProvideAs(atype any, ctor any)

func (*ContextBuilder) ProvideAsOrErr added in v0.0.3

func (ctxb *ContextBuilder) ProvideAsOrErr(atype any, ctor any) *Error

func (*ContextBuilder) ProvideNamed added in v0.0.3

func (ctxb *ContextBuilder) ProvideNamed(name string, ctor any)

func (*ContextBuilder) ProvideNamedAs added in v0.0.3

func (ctxb *ContextBuilder) ProvideNamedAs(name string, atype any, ctor any)

func (*ContextBuilder) ProvideNamedAsOrErr added in v0.0.3

func (ctxb *ContextBuilder) ProvideNamedAsOrErr(name string, atype any, ctor any) *Error

func (*ContextBuilder) ProvideNamedOrErr added in v0.0.3

func (ctxb *ContextBuilder) ProvideNamedOrErr(name string, ctor any) *Error

func (*ContextBuilder) ProvideOrErr added in v0.0.3

func (ctxb *ContextBuilder) ProvideOrErr(ctor any) *Error

type Error added in v0.0.3

type Error struct {
	// contains filtered or unexported fields
}

func GetAllOrErr

func GetAllOrErr[T any](ctx *Context) ([]T, *Error)

func GetNamedOrErr

func GetNamedOrErr[T any](ctx *Context, name string) (T, *Error)

func GetOrErr

func GetOrErr[T any](ctx *Context) (T, *Error)

func (*Error) ErrType added in v0.0.3

func (e *Error) ErrType() int

func (*Error) Error added in v0.0.3

func (e *Error) Error() string

func (*Error) IsErrType added in v0.0.3

func (e *Error) IsErrType(errType int) bool

func (*Error) RootCause added in v0.0.3

func (e *Error) RootCause() error

func (*Error) Unwrap added in v0.0.3

func (e *Error) Unwrap() error

type Initializable added in v0.0.5

type Initializable interface {
	Initialize()
}

type Shutdownable added in v0.0.5

type Shutdownable interface {
	Shutdown(context stdcontext.Context)
}

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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