mud

package
v1.119.11 Latest Latest
Warning

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

Go to latest
Published: Dec 20, 2024 License: AGPL-3.0 Imports: 13 Imported by: 1

README

mud

mud is a package for lazily starting up a large set of services and chores. The mud package name comes from a common architectural pattern called "Big Ball of Mud".

You can think about mud as a very huge map of service instances: map[type]component. Where component includes the singleton instance and functions to initialize the singleton and run/close them.

Components can also depend on each other, and there are helper function to filter the required components (and/or initialize/start them).

Compared to other similar libraries, like https://github.com/uber-go/fx or https://github.com/uber-go/dig, mud is just a very flexible framework, it wouldn't like to restrict the usage. Therefore advanced workflows also can be implemented with filtering different graphs and using them.

Users of this library has more power (and more responsibility).

Getting started

You can create the instance registry with:

mud := mud.NewBall()

Register a new component:

Provide[your.Service](ball, func() your.Service {
    return your.NewService()
})

Now, your component is registered, but not yet initialized. You should select some of the services to Init / run them:

err := mud.ForEach(ball, mud.Initialize(context.TODO()))
if err != nil {
   panic(err)
}

Now your component is initialized:

fmt.Println(mud.Find(ball, mud.All)[0].Instance())

This one selected the first component (we registered only one), but you can also use different selectors. This one selects the components by type.

fmt.Println(mud.Find(ball, mud.Select[your.Service](ball))[0].Instance())

Or, of you are sure, it's there:

fmt.Println(mud.MustLookup[your.Service](ball))

Dependencies and dependency injection

Dependencies are automatically injected. Let's say you have two structs:

type Service struct {
}

func NewService() Service {
	return Service{}
}

type Endpoint struct {
	Service Service
}

func NewEndpoint(service Service) Endpoint {
	return Endpoint{
		Service: service,
	}
}

Now you can register both:

mud.Provide[your.Service](ball, your.NewService)
mud.Provide[your.Endpoint](ball, your.NewEndpoint)

When you initialize the Endpoint, Service will be injected (if the instance is available!!!):

err := mud.MustDo[your.Service](ball, mud.Initialize(context.TODO()))
if err != nil {
    panic(err)
}

err = mud.MustDo[your.Endpoint](ball, mud.Initialize(context.TODO()))
if err != nil {
    panic(err)
}

But instead of initializing manually, you can also just ask what you need, and initialize everything in the right order

err := mud.ForEachDependency(ball, mud.Select[your.Endpoint](ball), mud.Initialize(context.TODO()), mud.All)

Views

Views are useful when you already have sg. registered, but you would like to make it fully or partially available under different type:

mud.Provide[satellite.DB](ball, OpenSatelliteDB)
mud.View[satellite.DB, gracefulexit.DB](ball, satellite.DB.GracefulExit)

This registers a satellite.DB (first line) and a gracefulexit.DB (second line). And if gracefulexit.DB is needed for injection, it will call the function to get it.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddTagOf added in v1.117.3

func AddTagOf[TAG any](c *Component, tag TAG)

AddTagOf attaches a tag to the component registration.

func All

func All(_ *Component) bool

All is a ComponentSelector which matches all components.

func CloseAll

func CloseAll(ball *Ball, timeout time.Duration) error

CloseAll calls the close callback stage on all initialized components.

func DependsOn

func DependsOn[BASE any, DEPENDENCY any](ball *Ball)

DependsOn creates a dependency relation between two components. With the help of the dependency graph, they can be executed in the right order.

func Dereference

func Dereference[A any](a *A) A

Dereference is a simple transformation to make real value from a pointer. Useful with View. for example: `View[*DB, DB](ball, Dereference[DB])`.

func DisableImplementation added in v1.117.3

func DisableImplementation[BASE any](ball *Ball)

DisableImplementation removes the implementation from the list of dependencies.

func DisableImplementationOf added in v1.117.3

func DisableImplementationOf(c *Component)

DisableImplementationOf is like DisableImplementation, but using components instead of generics.

func Dot added in v1.103.2

func Dot(w io.Writer, components []*Component) (err error)

Dot generates graph report of the modules in dot format, but only the selected components are included.

func DotAll added in v1.103.2

func DotAll(w io.Writer, ball *Ball) (err error)

DotAll generates graph report of the modules in dot format.

func Execute

func Execute[A any](ctx context.Context, ball *Ball, factory interface{}, options ...any) (A, error)

Execute executes a function with injecting all the required dependencies with type based Dependency Injection.

func Execute0

func Execute0(ctx context.Context, ball *Ball, factory interface{}) error

Execute0 executes a function with injection all required parameters. Same as Execute but without return type.

func Factory added in v1.119.2

func Factory[A any](ball *Ball, factory interface{})

Factory is like Provide, but instead of storing an instance in the component, it stores a factory function. factory function is called each time when the instance is required. Useful for logger, which requires further adjustment when it's injected.

func ForEach

func ForEach(ball *Ball, callback func(component *Component) error, selectors ...ComponentSelector) error

ForEach executes a callback action on all the selected components.

func ForEachDependency

func ForEachDependency(ball *Ball, target ComponentSelector, callback func(component *Component) error, selectors ...ComponentSelector) error

ForEachDependency executes a callback action on all the components, matching the target selector and dependencies, but only if selectors parameter is matching them.

func ForEachDependencyReverse added in v1.115.1

func ForEachDependencyReverse(ball *Ball, target ComponentSelector, callback func(component *Component) error, selectors ...ComponentSelector) error

ForEachDependencyReverse executes a callback action on all the components in reverse order, matching the target selector and dependencies, but only if selectors parameter is matching them.

func GenerateComponentsGraph added in v1.103.2

func GenerateComponentsGraph(fileprefix string, components []*Component) error

GenerateComponentsGraph generates dot and svg file including the selected components.

func GetTag

func GetTag[A any, Tag any](ball *Ball) (Tag, bool)

GetTag returns with attached tag (if attached).

func GetTagOf added in v1.102.2

func GetTagOf[Tag any](c *Component) (Tag, bool)

GetTagOf returns with attached tag (if attached).

func Implementation added in v1.102.2

func Implementation[L ~[]T, Instance any, T any](ball *Ball)

Implementation registers a new []T component, which will be filled with any registered instances. Instances will be marked with "Optional{}" tag, and will be injected only, if they are initialized. It's the responsibility of the Init code to exclude / include them during initialization.

func Initialize

func Initialize(ctx context.Context) func(c *Component) error

Initialize components as ForEach callback.

func MustGenerateGraph added in v1.103.2

func MustGenerateGraph(ball *Ball, fileprefix string, selector ComponentSelector)

MustGenerateGraph generates dot and svg files from components selected by the selector.

func MustLookup

func MustLookup[T any](ball *Ball) T

MustLookup returns with the registered component instance (or panic).

func Provide

func Provide[A any](ball *Ball, factory interface{}, options ...any)

Provide registers a new instance to the dependency pool. Run/Close methods are auto-detected (stage is created if they exist).

func RegisterImplementation added in v1.103.2

func RegisterImplementation[L ~[]T, T any](ball *Ball)

RegisterImplementation registers the implementation interface, without adding concrete implementation.

func RegisterInterfaceImplementation added in v1.117.3

func RegisterInterfaceImplementation[BASE any, DEP any](ball *Ball)

RegisterInterfaceImplementation registers an interface with an implementation. Later the implementation can be replaced. Only one (or zero) implementation can be registered/used at the same time.

func RegisterManual

func RegisterManual[T any](
	ball *Ball,
	factory func(ctx context.Context) (T, error),
)

RegisterManual registers a component manually. Most of the time you need either Provide or View instead of this.

func RemoveTag added in v1.102.2

func RemoveTag[A any, Tag any](ball *Ball)

RemoveTag removes all the Tag type of tags from the component.

func RemoveTagOf added in v1.117.3

func RemoveTagOf[TAG any](c *Component)

RemoveTagOf removes all the Tag type of tags from the component.

func ReplaceDependency added in v1.117.3

func ReplaceDependency[BASE any, DEP any](ball *Ball)

ReplaceDependency replaces the dependency of a component. Can be used to switch to an alternative implementation.

func ReplaceDependencyOf added in v1.117.3

func ReplaceDependencyOf(from *Component, to *Component)

ReplaceDependencyOf is like ReplaceDependency but using components instead of generics.

func RunWithDependencies

func RunWithDependencies(ctx context.Context, ball *Ball, selector ComponentSelector) error

RunWithDependencies will init and run all components which are matched by the selector.

func Supply

func Supply[T any](ball *Ball, t T)

Supply registers and instance which is already initialized.

func Tag

func Tag[A any, Tag any](ball *Ball, tag Tag)

Tag attaches a tag to the component registration.

func Tagged

func Tagged[Tag any]() func(c *Component) bool

Tagged is a selector, checking an annotation key/value.

func View

func View[From any, To any](ball *Ball, convert func(From) To)

View is lightweight component, which provides a type based on a existing instances.

Types

type Ball

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

Ball is the component registry.

Example
ball := NewBall()
Provide[string](ball, func() string {
	return "test"
})
Provide[string](ball, func() string {
	return "test"
})
components := Find(ball, All)
_ = components[0].Init(context.Background())
fmt.Println(components[0].Instance())
Output:

func NewBall

func NewBall() *Ball

NewBall creates a new component registry.

type Component

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

Component manages the lifecycle of a singleton Golang struct.

func Find

func Find(ball *Ball, selector ComponentSelector) (result []*Component)

Find selects components matching the selector.

func FindSelectedWithDependencies

func FindSelectedWithDependencies(ball *Ball, selector ComponentSelector) (result []*Component)

FindSelectedWithDependencies selects components matching the selector, together with all the dependencies.

func MustLookupComponent added in v1.102.2

func MustLookupComponent[T any](ball *Ball) *Component

MustLookupComponent gets the component (or panics if doesn't exist) based on a type.

func (*Component) Close

func (c *Component) Close(ctx context.Context) error

Close calls the Close stage function.

func (*Component) GetTarget

func (c *Component) GetTarget() reflect.Type

GetTarget returns with type, which is used as n identifier in mud.

func (*Component) ID

func (c *Component) ID() string

ID is the unque identifier of the component.

func (*Component) Init

func (c *Component) Init(ctx context.Context) error

Init initializes the internal singleton instance.

func (*Component) Instance

func (c *Component) Instance() any

Instance returns the singleton instance of the component. Can be null, if not yet initialized.

func (*Component) Name

func (c *Component) Name() string

Name returns with the human friendly name of the component.

func (*Component) Run

func (c *Component) Run(ctx context.Context, eg *errgroup.Group) error

Run executes the Run stage function.

func (*Component) String

func (c *Component) String() string

String returns with a string representation of the component.

type ComponentSelector

type ComponentSelector func(c *Component) bool

ComponentSelector can filter components.

func And

func And(selectors ...ComponentSelector) ComponentSelector

And selects components which matches all the selectors.

func ImplementationOf added in v1.102.2

func ImplementationOf[L ~[]T, T any](ball *Ball) ComponentSelector

ImplementationOf is a ForEach filter to get all the dependency of an implementation.

func Or

func Or(selectors ...ComponentSelector) ComponentSelector

Or selects components which matches any of the selectors.

func Select

func Select[A any](ball *Ball) ComponentSelector

Select is a component selector based on the specified type ([A]).

type CustomDotNode added in v1.119.2

type CustomDotNode interface {

	// CustomizeDotNode can replace / modify entries, which are key=value parameters of graphviz dot.
	CustomizeDotNode(tags []string) []string
}

CustomDotNode is an interface that can be implemented by a component to customize the SVG output for debugging..

type Injector added in v1.119.2

type Injector[T any] func(ball *Ball, t reflect.Type) T

Injector is a function which can be used to inject a specific type.

type Interface added in v1.119.2

type Interface struct {
}

Interface is a marker tag, to make it easier to list all possible extension points. Only used for debug / helper commands.

func (Interface) CustomizeDotNode added in v1.119.2

func (i Interface) CustomizeDotNode(tags []string) []string

CustomizeDotNode customize the graphical representation of the node in the graph, when rendered for debugging.

func (Interface) String added in v1.119.2

func (i Interface) String() string

type Nullable added in v1.117.3

type Nullable struct {
}

Nullable is a custom tag, which enables injecting null value, even if the component is not initialized.

type Optional

type Optional struct{}

Optional tag is used to mark components which may not required.

type Stage

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

Stage represents a function which should be called on the component at the right time (like start, stop, init).

type StageName

type StageName string

StageName is the unique identifier of the stages (~lifecycle events).

type Wrapper added in v1.115.1

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

Wrapper can be used during injection to decorate existing instances.

func NewWrapper added in v1.115.1

func NewWrapper[T any](f func(T) T) Wrapper

NewWrapper registers a new injection wrapper.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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