ectoinject

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 15, 2023 License: MIT Imports: 11 Imported by: 0

README

ectoinject

Simple, easy, user friendly Dependency Injection for Go projects. ectoinject is designed to allow users to quickly implement dependency injection without requiring extensive boilerplate code or code generation while still remaining flexible. Simply create a container, register the dependencies, and start using the dependencies.

Features:

  • Easy dependency registration
  • Multiple dependency lifecycles
  • Complex type handling
  • Unit testable
  • Clear error messages

Table of Contents

Install

go get -u github.com/Gobusters/ectoinject

Requirements

go >= 1.18

Concepts

Lifecycles

Singleton: A singleton dependency is created once and saved for the lifetime of the application. Singletons are the most performant dependencies but may cause issues if your dependency is stateful.

Scoped: A scoped dependency is created once per context.Context. This is useful for situations where you need temporary statefulness between dependencies.

Transient: A transient dependency is created everytime the dependency is requested.

Captive Dependencies: A Captive dependency occurs when a dependency's parent has a longer lifecycle than the dependency itself. For example, if we have dependency foo that is a singleton and has dependency on transient dependency bar. When foo is created, an instance of bar will be created, but because foo is a singleton, bar will be captive till foo is deleted.

Usage

Examples

Check out the example projects in the examples folder

Basic

Simple implementation to get you started

package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
)

type Bar struct {
}

func (b *Bar) Hello() string {
	return "Hello"
}

type Foo struct {
	Bar Bar
}

func main() {
	// create default container
	container, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register Bar as a singleton
	err = ectoinject.RegisterSingleton[Bar, Bar](container)
	if err != nil {
		panic(err) // handle error
	}

	// register Foo as a singleton
	err = ectoinject.RegisterSingleton[Foo, Foo](container)
	if err != nil {
		panic(err) // handle error
	}

	// create a new context with the container
	ctx := context.Background()

	// get Foo from the container
	ctx, foo, err := ectoinject.GetContext[Foo](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(foo.Bar.Hello()) // prints "Hello"
}
Named Dependencies

Often its necessary to implement multiple concrete structs for a single interface. To differentiate between them, we can give them names

package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
)

type Person interface {
	Speak() string
}

type Peter struct {
}

func (p *Peter) Speak() string {
	return "We came, we saw, we kicked its ass!"
}

type Ray struct {
}

func (r *Ray) Speak() string {
	return "I tried to think of the most harmless thing. Something I loved from my childhood. Something that could never ever possibly destroy us. Mr. Stay Puft!"
}

type GhostBusters struct {
	Peter Person `inject:"peter"`
	Ray   Person `inject:"ray"`
}

func main() {
	// create default container
	container, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register Peter as a singleton
	err = ectoinject.RegisterSingleton[Person, Peter](container, "peter") // name needs to match the inject tag
	if err != nil {
		panic(err) // handle error
	}

	// register Peter as a singleton
	err = ectoinject.RegisterSingleton[Person, Ray](container, "ray") // name needs to match the inject tag
	if err != nil {
		panic(err) // handle error
	}

	// register GhostBusters as a singleton
	err = ectoinject.RegisterSingleton[GhostBusters, GhostBusters](container)
	if err != nil {
		panic(err) // handle error
	}

	// create a new context with the container
	ctx := context.Background()

	// get GhostBusters from the container
	ctx, gb, err := ectoinject.GetContext[GhostBusters](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.Peter.Speak()) // prints "We came, we saw, we kicked its ass!"
	println(gb.Ray.Speak())   // prints "I tried to think of the most harmless thing. Something I loved from my childhood. Something that could never ever possibly destroy us. Mr. Stay Puft!"
}
Scoped Dependencies

Below is an example showing how you can utilze scoped dependencies

package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
)

type GhostBuster interface {
	CaptureGhost()
	HasGhost() bool
}

type Ray struct {
	hasGhost bool
}

func (r *Ray) CaptureGhost() {
	r.hasGhost = true
}

func (r *Ray) HasGhost() bool {
	return r.hasGhost
}

func main() {
	// create default container
	container, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register Ray as Scoped
	err = ectoinject.RegisterScoped[GhostBuster, Ray](container)
	if err != nil {
		panic(err) // handle error
	}

	// create a new context with the container
	ctx := context.Background()

	// get Ray from the container. The context thats returned should be used for all subsequent calls that need the scoped Ray
	ctx, gb, err := ectoinject.GetContext[GhostBuster](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.HasGhost()) // false
	gb.CaptureGhost()
	println(gb.HasGhost()) // true

	// get Ray from the container again
	ctx, gb, err = ectoinject.GetContext[GhostBuster](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.HasGhost()) // true

	// get Ray from the container again but with a new context
	ctx = context.Background() // The new context will not have the scoped Ray. It will be a new instance of Ray
	ctx, gb, err = ectoinject.GetContext[GhostBuster](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.HasGhost()) // false
}
Constructors

An alternative way to building a dependency is the use of the Constructor. On the dependency Struct, you may define a method with the name "Constructor" (this name can be changed with Configuration). You simply define your dependencies as arguments in the constructor method. The method should return the dependnecy instance as the first return value, you can optionally provide a error as the second return value. Note: currently ectoinject does not support named dependencies for the constructor method. A work around is Constructors with DIContainer dependency.

package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
)

type GhostTrap struct {
	hasGhost bool
	isSet    bool
}

func (gt *GhostTrap) Set() {
	gt.isSet = true
}

func (gt *GhostTrap) IsSet() bool {
	return gt.isSet
}

func (gt *GhostTrap) Trigger() {
	gt.hasGhost = true
}

func (gt *GhostTrap) HasGhost() bool {
	return gt.hasGhost
}

type GhostBuster interface {
	CaptureGhost()
	HasGhost() bool
}

type Ray struct {
	ghostTrap *GhostTrap
	isReady   bool
}

func (r *Ray) CaptureGhost() {
	if r.isReady && r.ghostTrap.IsSet() {
		r.ghostTrap.Trigger()
	}
}

func (r *Ray) HasGhost() bool {
	return r.ghostTrap.HasGhost()
}

// Ray requires a GhostTrap to be injected and returns an instance of the Ray struct
func (r *Ray) Constructor(gt *GhostTrap) GhostBuster {
	r.isReady = true

	r.ghostTrap = gt
	r.ghostTrap.Set()

	return r
}

type GhostBusters struct {
	Ray GhostBuster `inject:"ray"`
}

func main() {
	// create default container
	container, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register GhostTrap as Singleton
	err = ectoinject.RegisterTransient[GhostTrap, GhostTrap](container)
	if err != nil {
		panic(err) // handle error
	}

	// register Ray as Singleton
	err = ectoinject.RegisterSingleton[GhostBuster, Ray](container, "ray")
	if err != nil {
		panic(err) // handle error
	}

	// register GhostBusters as Singleton
	err = ectoinject.RegisterSingleton[GhostBusters, GhostBusters](container)
	if err != nil {
		panic(err) // handle error
	}

	// create a new context with the container
	ctx := context.Background()

	// get GhostBusters from the container
	ctx, gb, err := ectoinject.GetContext[GhostBusters](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.Ray.HasGhost()) // false

	gb.Ray.CaptureGhost()

	println(gb.Ray.HasGhost()) // true
}
Constructors with DIContainer dependency

You can also inject the DIContainer and context.Context into your constructor if you need

package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
)

type GhostTrap struct {
	hasGhost bool
	isSet    bool
}

func (gt *GhostTrap) Set() {
	gt.isSet = true
}

func (gt *GhostTrap) IsSet() bool {
	return gt.isSet
}

func (gt *GhostTrap) Trigger() {
	gt.hasGhost = true
}

func (gt *GhostTrap) HasGhost() bool {
	return gt.hasGhost
}

type GhostBuster interface {
	CaptureGhost()
	HasGhost() bool
}

type Ray struct {
	ghostTrap *GhostTrap
	isReady   bool
}

func (r *Ray) CaptureGhost() {
	if r.isReady && r.ghostTrap.IsSet() {
		r.ghostTrap.Trigger()
	}
}

func (r *Ray) HasGhost() bool {
	return r.ghostTrap.HasGhost()
}

// Ray requires a GhostTrap to be injected and returns an instance of the Ray struct
func (r *Ray) Constructor(ctx context.Context, di ectoinject.DIContainer) GhostBuster {
	ctx, gt, err := ectoinject.GetContext[GhostTrap](ctx)
	if err != nil {
		panic(err) // handle error
	}
	r.isReady = true

	r.ghostTrap = &gt
	r.ghostTrap.Set()

	return r
}

type GhostBusters struct {
	Ray GhostBuster `inject:"ray"`
}

func main() {
	// create default container
	container, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register GhostTrap as Singleton
	err = ectoinject.RegisterTransient[GhostTrap, GhostTrap](container)
	if err != nil {
		panic(err) // handle error
	}

	// register Ray as Singleton
	err = ectoinject.RegisterSingleton[GhostBuster, Ray](container, "ray")
	if err != nil {
		panic(err) // handle error
	}

	// register GhostBusters as Singleton
	err = ectoinject.RegisterSingleton[GhostBusters, GhostBusters](container)
	if err != nil {
		panic(err) // handle error
	}

	// create a new context with the container
	ctx := context.Background()

	// get GhostBusters from the container
	ctx, gb, err := ectoinject.GetContext[GhostBusters](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.Ray.HasGhost()) // false

	gb.Ray.CaptureGhost()

	println(gb.Ray.HasGhost()) // true
}
Instance Dependencies

You may encounter usecases where you wish to create a dependency instance that you share throughout your project. That can be done using the RegisterInstance func.

package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
)

type GhostBuster interface {
	WhoYaGonnaCall() string
}

type Peter struct {
	question string
}

func (r *Peter) WhoYaGonnaCall() string {
	return r.question
}

type Ray struct {
	Response string `inject:"catchphrase"`
}

func (r *Ray) WhoYaGonnaCall() string {
	return r.Response
}

type GhostBusters struct {
	Ray   GhostBuster `inject:"ray"`
	Peter GhostBuster `inject:"peter"`
}

func main() {
	// create default container
	container, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register Ray as Singleton
	err = ectoinject.RegisterSingleton[GhostBuster, Ray](container, "ray")
	if err != nil {
		panic(err) // handle error
	}

	// register the catchphrase as an instance with name "catchphrase"
	err = ectoinject.RegisterInstance[string](container, "Ghostbusters!", "catchphrase")
	if err != nil {
		panic(err) // handle error
	}

	peterInstance := &Peter{question: "Who ya gonna call?"} // create an instance of Peter
	// register peter as an instance with name "peter"
	err = ectoinject.RegisterInstance[GhostBuster](container, peterInstance, "peter")
	if err != nil {
		panic(err) // handle error
	}

	// register GhostBusters as Singleton
	err = ectoinject.RegisterSingleton[GhostBusters, GhostBusters](container)
	if err != nil {
		panic(err) // handle error
	}

	// create a new context with the container
	ctx := context.Background()

	// get GhostBusters from the container
	ctx, gb, err := ectoinject.GetContext[GhostBusters](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.Peter.WhoYaGonnaCall()) // prints "Who ya gonna call?"
	println(gb.Ray.WhoYaGonnaCall())   // prints "Ghostbusters!"
}
Custom Instance Getters
package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
	"github.com/Gobusters/ectoinject/lifecycles"
)

type GhostBuster interface {
	WhoYaGonnaCall() string
}

type Peter struct {
	question string
}

func (r *Peter) WhoYaGonnaCall() string {
	return r.question
}

type Ray struct {
	Response string `inject:"catchphrase"`
}

func (r *Ray) WhoYaGonnaCall() string {
	return r.Response
}

type GhostBusters struct {
	Ray   GhostBuster `inject:"ray"`
	Peter GhostBuster `inject:"peter"`
}

func main() {
	// create default container
	container, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register Ray as Singleton
	err = ectoinject.RegisterSingleton[GhostBuster, Ray](container, "ray")
	if err != nil {
		panic(err) // handle error
	}

	// register the catchphrase as an instance with name "catchphrase"
	err = ectoinject.RegisterInstance[string](container, "Ghostbusters!", "catchphrase")
	if err != nil {
		panic(err) // handle error
	}

	getPeter := func(ctx context.Context) (any, error) {
		return &Peter{question: "Who ya gonna call?"}, nil
	}

	// register peter as an instance with name "peter"
	err = ectoinject.RegisterInstanceFunc[GhostBuster](container, lifecycles.Singleton, getPeter, "peter")
	if err != nil {
		panic(err) // handle error
	}

	// register GhostBusters as Singleton
	err = ectoinject.RegisterSingleton[GhostBusters, GhostBusters](container)
	if err != nil {
		panic(err) // handle error
	}

	// create a new context with the container
	ctx := context.Background()

	// get GhostBusters from the container
	ctx, gb, err := ectoinject.GetContext[GhostBusters](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(gb.Peter.WhoYaGonnaCall()) // prints "Who ya gonna call?"
	println(gb.Ray.WhoYaGonnaCall())   // prints "Ghostbusters!"
}

Configuration

ectoinject is intended to be flexible enough to handle your unique needs and requirements. When creating your container, you can provide a ectocontainer.DIContainerConfig. This allows you to change the behavior of the container as it gets dependencies.

	config := ectocontainer.DIContainerConfig{
		ID:                       "my-custom-container",
		AllowCaptiveDependencies: true,
		AllowMissingDependencies: true,
		RequireInjectTag:         false,
		AllowUnsafeDependencies:  false,
		RequireConstructor:       false,
		ConstructorFuncName:      "MyConstructorFunc",
		InjectTagName:            "MyInjectTag",
		LoggerConfig: &ectocontainer.DIContainerLoggerConfig{
			Prefix:      "ectoinject",
			LogLevel:    loglevel.INFO,
			EnableColor: true,
			Enabled:     true,
			LogFunc: func(ctx context.Context, level, msg string) {
				fmt.Printf("%s: %s\n", level, msg)
			},
		},
	}

	container, err := NewDIContainer(config)
AllowCaptiveDependencies

If enabled, AllowCaptiveDependencies will only log a message if a captive dependency is detected. If false, an error will be returned.

AllowMissingDependencies

If enabled, AllowMissingDependencies will ignore dependencies that have not been registered. If false, an error will be returned if a dependency is not found.

RequireInjectTag

If enabled, RequireInjectTag struct fields without the inject tag will be ignored. If false, the container will attempt to inject a dependency for all struct fields.

AllowUnsafeDependencies

If enabled, the container will attempt to inject a dependency for non-exported struct fields. If false, the container will ignore non-exported struct fields. Below is an example where Foo has a dependency on an unexported dependency bar. If AllowUnsafeDependencies is enabled, the container will attempt to inject bar despite it not being exported

type Foo struct {
	bar Bar // unexported field
}
RequireConstructor

If enabled, the container will only inject dependencies via the constructor function.

ConstructorFuncName

Defines the name of the constructor function the container will look to use. Defaults to "Constructor"

InjectTagName

Defines the name of the inject tag on the struct. Defaults to "inject"

Inject Tag

The inject tag allows the you specify a named dependency to be injected into your struct. The tag name used can be changed using the container configuration InjectTagName. You can tell the container to ignore the field by giving it a name of "-".

type Foo struct {
	Bar       Bar `inject:"bar"` // the container will look for dependency with name "bar"
	MyPrivate Dep `inject:"-"`   // the container will ignore this depenency
}

Logging

ectoinject does log messages to stdout in some instances. These logs are intended to help you identify potential issues in the dependency tree. You can change the behavior of these logs using the LoggerConfig field on the container configuration

Prefix

This is a string added to the front of each log message. It defaults to "ectoinject" and is intended to help you identify where the log message originated.

LogLevel

This affects the verbosity of the logs. It defaults to loglevel.INFO but can be changed to loglevel.WARN.

EnableColors

If enabled, the logs will be colored to indicate their log level. This is intended to help draw attention to specific messages.

Enabled

If false, no logs will be output

Custom Logging

Using the LogFunc you can override the logger behavior. This allows you set a custom logging function. It can be useful for situations where you have a standardized logger or log format. The example below demonstrates implementing a logrus logger

	myLogrusLogger := func(ctx context.Context, level, msg string) {
		if level == loglevel.INFO {
			logrus.WithContext(ctx).Info(msg)
		}
		if level == loglevel.WARN {
			logrus.WithContext(ctx).Warn(msg)
		}
	}
	config := ectoinject.DefaultContainerConfig
	config.LoggerConfig.LogFunc = myLogrusLogger
	// create default container
	container, err := ectoinject.NewDIContainer(config)

Multiple Containers

Most usecases will only require a single default container, but ectoinject does support multi container usecases. You can toggle between containers by using ctx, err = ectoinject.SetActiveContainer(ctx, "my container id"). If you don't set an active container, the default is used. You can change the default container by using err = ectoinject.SetDefaultContainer("my container id")

package main

import (
	"context"

	"github.com/Gobusters/ectoinject"
	"github.com/Gobusters/ectoinject/ectocontainer"
)

type MyInterface interface {
	Output() string
}

type Bar struct {
}

func (b *Bar) Output() string {
	return "Hello"
}

type Foo struct {
}

func (b *Foo) Output() string {
	return "World"
}

func main() {
	// create default container
	defaultContainer, err := ectoinject.NewDIDefaultContainer()
	if err != nil {
		panic(err) // handle error
	}

	// register Bar as a singleton in default container
	err = ectoinject.RegisterSingleton[MyInterface, Bar](defaultContainer)
	if err != nil {
		panic(err) // handle error
	}

	config := ectocontainer.DIContainerConfig{
		ID:                       "my custom container",
		AllowCaptiveDependencies: true,
		AllowMissingDependencies: true,
		RequireInjectTag:         false,
		AllowUnsafeDependencies:  false,
	}
	// create a new custom container with the config
	customContainer, err := ectoinject.NewDIContainer(config)

	// register Foo as a singleton in the custom container
	err = ectoinject.RegisterSingleton[MyInterface, Foo](customContainer)
	if err != nil {
		panic(err) // handle error
	}

	// create ctx. If no container is passed, default container is used
	ctx := context.Background()

	// get MyInterface from the container
	ctx, bar, err := ectoinject.GetContext[MyInterface](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(bar.Output()) // prints "Hello"

	// set the custom container in the context
	ctx, err = ectoinject.SetActiveContainer(ctx, config.ID)
	if err != nil {
		panic(err) // handle error
	}

	// get MyInterface from the container
	ctx, foo, err := ectoinject.GetContext[MyInterface](ctx)
	if err != nil {
		panic(err) // handle error
	}

	println(foo.Output()) // prints "World"
}

Unit Testing

While its not recommended to directly access the dependency container within code you intend to unit, it is possible to mock the container for unit testing.

Code under test:

package unittesting

import (
	"context"

	"github.com/Gobusters/ectoinject"
)

type Foo interface {
	Bar() string
}

func MyFunc(ctx context.Context) string {
	// code directly access the container
	_, foo, err := ectoinject.GetContext[Foo](ctx)
	if err != nil {
		panic(err) // handle error
	}

	return foo.Bar()
}

unit test with container mock

package unittesting

import (
	"context"
	"testing"

	"github.com/Gobusters/ectoinject"
	"github.com/Gobusters/ectoinject/dependency"
)

type ContainerMock struct {
	FooMock Foo
	ID      string
}

func (m *ContainerMock) Get(ctx context.Context, name string) (context.Context, any, error) {
	return ctx, m.FooMock, nil
}

func (m *ContainerMock) GetConstructorFuncName() string {
	return ""
}

func (m *ContainerMock) AddDependency(dep dependency.Dependency) {

}

func (m *ContainerMock) GetContainerID() string {
	return m.ID
}

type FooMock struct {
}

func (m *FooMock) Bar() string {
	return "Hello World"
}

func TestWithMockContainer(t *testing.T) {
	containerID := "my mock container"
	fooMock := &FooMock{}
	containerMock := &ContainerMock{
		FooMock: fooMock,
		ID:      containerID,
	}

	err := ectoinject.RegisterContainer(containerMock)
	if err != nil {
		t.Fatalf("error registering container: %s", err)
	}

	ctx := context.Background()
	ctx, err = ectoinject.SetActiveContainer(ctx, containerID)
	if err != nil {
		t.Fatalf("error setting active container: %s", err)
	}

	result := MyFunc(ctx)
	if result != "Hello World" {
		t.Fatalf("expected result to be 'Hello World', got '%s'", result)
	}
}

Tips and Tricks

Stick to singletons

Singletons will provide you the best overall performance. They are created once per container and saved for the lifetime of the application.

Avoid Captive Dependencies

While captive dependencies do not break ectoinject they can make your code behave in ways you might not expect.

Use interfaces

ectoinject fully supports interfaces and interfaces will help make your code more flexible and testable.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaulLoggerConfig = ectocontainer.DIContainerLoggerConfig{
	Prefix:      "ectoinject",
	LogLevel:    loglevel.INFO,
	EnableColor: true,
	Enabled:     true,
	LogFunc:     nil,
}
View Source
var DefaultContainerConfig = ectocontainer.DIContainerConfig{
	ID:                       store.GetDefaultContainerID(),
	AllowCaptiveDependencies: true,
	AllowMissingDependencies: true,
	RequireInjectTag:         false,
	AllowUnsafeDependencies:  false,
	ConstructorFuncName:      "Constructor",
	InjectTagName:            "inject",
	LoggerConfig:             &DefaulLoggerConfig,
}

Functions

func Get

func Get[T any]() (T, error)

Get gets a dependency from the default container. Returns the dependency and an error. T: The type of the dependency

func GetActiveContainer

func GetActiveContainer(ctx context.Context) (ectocontainer.DIContainer, error)

GetActiveContainer gets the active container from the context ctx: The context to get the active container from

func GetContainer

func GetContainer(id string) ectocontainer.DIContainer

GetContainer gets the container with the provided id

func GetContext

func GetContext[T any](ctx context.Context) (context.Context, T, error)

GetContext gets a dependency from the container. Returns a context with scoped dependencies caching, the dependency, and an error. The context is used to provide scoped caching of dependencies. T: The type of the dependency ctx: The context to use. To use a non-default container, use SetActiveContainer

func GetDefaultContainer

func GetDefaultContainer() ectocontainer.DIContainer

GetDefaultContainer gets the default container

func GetDependency

func GetDependency[T any](ctx context.Context, container ectocontainer.DIContainer, name string) (context.Context, T, error)

GetDependency gets a dependency from the container by name T: The type of the dependency ctx: The context to use. To use a non-default container, use SetActiveContainer container: The container to get the dependency from name: The name of the dependency. Unnamed dependencies will use `{module}.{type}` as the name

func GetFromContainer

func GetFromContainer[T any](containerID string) (T, error)

GetFromContainer gets a dependency from the container. Returns the dependency and an error. T: The type of the dependency containerID: The id of the container to get the dependency from

func GetNamedDependency

func GetNamedDependency[T any](ctx context.Context, name string) (context.Context, T, error)

GetNamedDependency gets a dependency from the container by name T: The type of the dependency ctx: The context to use. To use a non-default container, use SetActiveContainer name: The name of the dependency. Unnamed dependencies will use `{module}.{type}` as the name

func NewDIContainer

NewDIContainer creates a new container with the provided configuration config: The configuration to use for the container

func NewDIDefaultContainer

func NewDIDefaultContainer() (ectocontainer.DIContainer, error)

NewDIDefaultContainer creates a new container with the default configuration. The default configuration is: ID: "default" AllowCaptiveDependencies: true AllowMissingDependencies: true RequireInjectTag: false AllowUnsafeDependencies: false ConstructorFuncName: "Constructor" InjectTagName: "inject"

func RegisterContainer

func RegisterContainer(container ectocontainer.DIContainer) error

RegisterContainer adds the container to the container lookup

func RegisterDependency

func RegisterDependency[TType any, TValue any](container ectocontainer.DIContainer, lifecycle string, names ...string) error

RegisterDependency registers a dependency in the container TType: The type of the dependency TValue: The implementation of the dependency container: The container to register the dependency in lifecycle: The lifecycle of the dependency

func RegisterInstance

func RegisterInstance[TType any](container ectocontainer.DIContainer, instance any, names ...string) error

RegisterInstance registers an instance in the container. Instances are treated as singletons TType: The type of the dependency container: The container to register the dependency in instance: The instance to register names: (optional) The names of the dependency

func RegisterInstanceFunc

func RegisterInstanceFunc[TType any](container ectocontainer.DIContainer, lifecycle string, getInstanceFunc func(context.Context) (any, error), names ...string) error

RegisterInstanceFunc registers a custom instance function in the container TType: The type of the dependency container: The container to register the dependency in lifecycle: The lifecycle of the dependency. Must be one of transient, scoped, or singleton getInstanceFunc: a function that returns the instance names: (optional) The names of the dependency

func RegisterScoped

func RegisterScoped[TType any, TValue any](container ectocontainer.DIContainer, names ...string) error

RegisterScoped registers a scoped dependency in the container. Scoped dependencies are cached for the lifetime of the scope TType: The type of the dependency TValue: The implementation of the dependency container: The container to register the dependency in names: (optional) The names of the dependency

func RegisterSingleton

func RegisterSingleton[TType any, TValue any](container ectocontainer.DIContainer, names ...string) error

RegisterSingleton registers a singleton dependency in the container. Singleton dependencies are cached for the lifetime of the application TType: The type of the dependency TValue: The implementation of the dependency container: The container to register the dependency in names: (optional) The names of the dependency

func RegisterTransient

func RegisterTransient[TType any, TValue any](container ectocontainer.DIContainer, names ...string) error

RegisterTransient registers a transient dependency in the container. Transient dependencies are not cached TType: The type of the dependency TValue: The implementation of the dependency container: The container to register the dependency in names: (optional) The names of the dependency

func SetActiveContainer

func SetActiveContainer(ctx context.Context, id string) (context.Context, error)

SetActiveContainer sets the active container in the context ctx: The context to set the active container in id: The id of the container to set as active

func SetDefaultContainer

func SetDefaultContainer(containerID string) error

SetDefaultContainer sets the default container to use

Types

This section is empty.

Jump to

Keyboard shortcuts

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