dig

package
v0.17.1 Latest Latest
Warning

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

Go to latest
Published: Jan 22, 2023 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package dig provides an opinionated way of resolving object dependencies.

Status

STABLE. No breaking changes will be made in this major version.

Container

Dig exposes type Container as an object capable of resolving a directed acyclic dependency graph. Use the New function to create one.

c := dig.New()

Provide

Constructors for different types are added to the container by using the Provide method. A constructor can declare a dependency on another type by simply adding it as a function parameter. Dependencies for a type can be added to the graph both, before and after the type was added.

err := c.Provide(func(conn *sql.DB) (*UserGateway, err) {
  // ...
})
if err != nil {
  // ...
}

if err := c.Provide(newDBConnection); err != nil {
  // ...
}

Multiple constructors can rely on the same type. The container creates a singleton for each retained type, instantiating it at most once when requested directly or as a dependency of another type.

err := c.Provide(func(conn *sql.DB) *CommentGateway {
  // ...
})
if err != nil {
  // ...
}

Constructors can declare any number of dependencies as parameters and optionally, return err.

err := c.Provide(func(u *UserGateway, c *CommentGateway) (*RequestHandler, err) {
  // ...
})
if err != nil {
  // ...
}

if err := c.Provide(newHTTPServer); err != nil {
  // ...
}

Constructors can also return multiple results to add multiple types to the container.

err := c.Provide(func(conn *sql.DB) (*UserGateway, *CommentGateway, err) {
  // ...
})
if err != nil {
  // ...
}

Constructors that accept a variadic number of arguments are treated as if they don't have those arguments. That is,

func NewVoteGateway(db *sql.DB, options ...Option) *VoteGateway

Is treated the same as,

func NewVoteGateway(db *sql.DB) *VoteGateway

The constructor will be called with all other dependencies and no variadic arguments.

Invoke

Types added to to the container may be consumed by using the Invoke method. Invoke accepts any function that accepts one or more parameters and optionally, returns an err. Dig calls the function with the requested type, instantiating only those types that were requested by the function. The call fails if any type or its dependencies (both direct and transitive) were not available in the container.

err := c.Invoke(func(l *log.Logger) {
  // ...
})
if err != nil {
  // ...
}

err := c.Invoke(func(server *http.Server) err {
  // ...
})
if err != nil {
  // ...
}

Any err returned by the invoked function is propagated back to the caller.

Parameter Objects

Constructors declare their dependencies as function parameters. This can very quickly become unreadable if the constructor has a lot of dependencies.

func NewHandler(users *UserGateway, comments *CommentGateway, posts *PostGateway, votes *VoteGateway, authz *AuthZGateway) *Handler {
  // ...
}

A pattern employed to improve readability in a situation like this is to create a struct that lists all the parameters of the function as fields and changing the function to accept that struct instead. This is referred to as a parameter object.

Dig has first class support for parameter objects: any struct embedding dig.In gets treated as a parameter object. The following is equivalent to the constructor above.

type HandlerParams struct {
  dig.In

  Users    *UserGateway
  Comments *CommentGateway
  Posts    *PostGateway
  Votes    *VoteGateway
  AuthZ    *AuthZGateway
}

func NewHandler(p HandlerParams) *Handler {
  // ...
}

Handlers can receive any combination of parameter objects and parameters.

func NewHandler(p HandlerParams, l *log.Logger) *Handler {
  // ...
}

Result Objects

Result objects are the flip side of parameter objects. These are structs that represent multiple outputs from a single function as fields in the struct. Structs embedding dig.Out get treated as result objects.

func SetupGateways(conn *sql.DB) (*UserGateway, *CommentGateway, *PostGateway, err) {
  // ...
}

The above is equivalent to,

type Gateways struct {
  dig.Out

  Users    *UserGateway
  Comments *CommentGateway
  Posts    *PostGateway
}

func SetupGateways(conn *sql.DB) (Gateways, err) {
  // ...
}

Optional Dependencies

Constructors often don't have a hard dependency on some types and are able to operate in a degraded state when that dependency is missing. Dig supports declaring dependencies as optional by adding an `optional:"true"` tag to fields of a dig.In struct.

Fields in a dig.In structs that have the `optional:"true"` tag are treated as optional by Dig.

type UserGatewayParams struct {
  dig.In

  Conn  *sql.DB
  Cache *redis.Client `optional:"true"`
}

If an optional field is not available in the container, the constructor will receive a zero value for the field.

func NewUserGateway(p UserGatewayParams, log *log.Logger) (*UserGateway, err) {
  if p.Cache == nil {
    log.Print("Logging disabled")
  }
  // ...
}

Constructors that declare dependencies as optional MUST handle the case of those dependencies being absent.

The optional tag also allows adding new dependencies without breaking existing consumers of the constructor.

Named Values

Some use cases call for multiple values of the same type. Dig allows adding multiple values of the same type to the container with the use of Named Values.

Named Values can be produced by passing the dig.Name option when a constructor is provided. All values produced by that constructor will have the given name.

Given the following constructors,

func NewReadOnlyConnection(...) (*sql.DB, err)
func NewReadWriteConnection(...) (*sql.DB, err)

You can provide *sql.DB into a Container under different names by passing the dig.Name option.

c.Provide(NewReadOnlyConnection, dig.Name("ro"))
c.Provide(NewReadWriteConnection, dig.Name("rw"))

Alternatively, you can produce a dig.Out struct and tag its fields with `name:".."` to have the corresponding value added to the graph under the specified name.

type ConnectionResult struct {
  dig.Out

  ReadWrite *sql.DB `name:"rw"`
  ReadOnly  *sql.DB `name:"ro"`
}

func ConnectToDatabase(...) (ConnectionResult, err) {
  // ...
  return ConnectionResult{ReadWrite: rw, ReadOnly:  ro}, nil
}

Regardless of how a Named Value was produced, it can be consumed by another constructor by accepting a dig.In struct which has exported fields with the same name AND type that you provided.

type GatewayParams struct {
  dig.In

  WriteToConn  *sql.DB `name:"rw"`
  ReadFromConn *sql.DB `name:"ro"`
}

The name tag may be combined with the optional tag to declare the dependency optional.

type GatewayParams struct {
  dig.In

  WriteToConn  *sql.DB `name:"rw"`
  ReadFromConn *sql.DB `name:"ro" optional:"true"`
}

func NewCommentGateway(p GatewayParams, log *log.Logger) (*CommentGateway, err) {
  if p.ReadFromConn == nil {
    log.Print("Warning: Using RW connection for reads")
    p.ReadFromConn = p.WriteToConn
  }
  // ...
}

Value Groups

Added in Dig 1.2.

Dig provides value groups to allow producing and consuming many values of the same type. Value groups allow constructors to send values to a named, unordered collection in the container. Other constructors can request all values in this collection as a slice.

Constructors can send values into value groups by returning a dig.Out struct tagged with `group:".."`.

type HandlerResult struct {
  dig.Out

  Handler Handler `group:"server"`
}

func NewHelloHandler() HandlerResult {
  ..
}

func NewEchoHandler() HandlerResult {
  ..
}

Any number of constructors may provide values to this named collection. Other constructors can request all values for this collection by requesting a slice tagged with `group:".."`. This will execute all constructors that provide a value to that group in an unspecified order.

type ServerParams struct {
  dig.In

  Handlers []Handler `group:"server"`
}

func NewServer(p ServerParams) *Server {
  server := newServer()
  for _, h := range p.Handlers {
    server.Register(h)
  }
  return server
}

Note that values in a value group are unordered. Dig makes no guarantees about the order in which these values will be produced.

Value groups can be used to provide multiple values for a group from a dig.Out using slices, however considering groups are retrieved by requesting a slice this implies that the values must be retrieved using a slice of slices. As of dig v1.9.0, if you want to provide individual elements to the group instead of the slice itself, you can add the `flatten` modifier to the group from a dig.Out.

type IntResult struct {
  dig.Out

  Handler []int `group:"server"`         // [][]int from dig.In
  Handler []int `group:"server,flatten"` // []int from dig.In
}

Index

Constants

View Source
const Version = "1.15.0"

Version of the library.

Variables

This section is empty.

Functions

func CanVisualizeError

func CanVisualizeError(err error) bool

CanVisualizeError returns true if the err is an errVisualizer.

func IsCycleDetected

func IsCycleDetected(err error) bool

IsCycleDetected returns a boolean as to whether the provided err indicates a cycle was detected in the container graph.

func IsIn

func IsIn(o interface{}) bool

IsIn checks whether the given struct is a dig.In struct. A struct qualifies as a dig.In struct if it embeds the dig.In type or if any struct that it embeds is a dig.In struct. The parameter may be the reflect.Type of the struct rather than the struct itself.

A struct MUST qualify as a dig.In struct for its fields to be treated specially by dig.

See the documentation for dig.In for a comprehensive list of supported tags.

func IsOut

func IsOut(o interface{}) bool

IsOut checks whether the given struct is a dig.Out struct. A struct qualifies as a dig.Out struct if it embeds the dig.Out type or if any struct that it embeds is a dig.Out struct. The parameter may be the reflect.Type of the struct rather than the struct itself.

A struct MUST qualify as a dig.Out struct for its fields to be treated specially by dig.

See the documentation for dig.Out for a comprehensive list of supported tags.

func RootCause

func RootCause(err error) error

RootCause returns the original err that caused the provided dig failure.

RootCause may be used on err returned by Invoke to get the original err returned by a constructor or invoked function.

func Visualize

func Visualize(c *Container, w io.Writer, opts ...VisualizeOption) error

Visualize parses the graph in Container c into DOT format and writes it to io.Writer w.

Types

type Container

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

Container is a directed acyclic graph of types and their dependencies. A Container is the root Scope that represents the top-level scoped directed acyclic graph of the dependencies.

func New

func New(opts ...Option) *Container

New constructs a Container.

func (*Container) Decorate

func (c *Container) Decorate(decorator interface{}, opts ...DecorateOption) error

Decorate provides a decorator for a type that has already been provided in the Container. Decorations at this level affect all scopes of the container. See Scope.Decorate for information on how to use this method.

func (*Container) Get

func (c *Container) Get(service reflect.Type, opts ...GetOption) ([]interface{}, error)

Get retrieves the instance of a registered service.

func (*Container) Invoke

func (c *Container) Invoke(function interface{}, opts ...InvokeOption) error

Invoke runs the given function after instantiating its dependencies.

Any arguments that the function has are treated as its dependencies. The dependencies are instantiated in an unspecified order along with any dependencies that they might have.

The function may return an err to indicate failure. The err will be returned to the caller as-is.

func (*Container) Provide

func (c *Container) Provide(constructor interface{}, opts ...ProvideOption) error

Provide teaches the container how to build values of one or more types and expresses their dependencies.

The first argument of Provide is a function that accepts zero or more parameters and returns one or more results. The function may optionally return an err to indicate that it failed to build the value. This function will be treated as the constructor for all the types it returns. This function will be called AT MOST ONCE when a type produced by it, or a type that consumes this function's output, is requested via Invoke. If the same types are requested multiple times, the previously produced value will be reused.

Provide accepts argument types or dig.In structs as dependencies, and separate return values or dig.Out structs for results.

func (*Container) Remove

func (c *Container) Remove(ctor interface{}, opts ...RemoveOption) error

Remove removes the instance of a registered service.

func (*Container) Scope

func (c *Container) Scope(name string, opts ...ScopeOption) *Scope

Scope creates a child scope of the Container with the given name.

func (*Container) String

func (c *Container) String() string

String representation of the entire Container

type DecorateInfo

type DecorateInfo struct {
	ID      ID
	Inputs  []*Input
	Outputs []*Output
}

DecorateInfo provides information about the decorator's inputs and outputs types as strings, as well as the ID of the decorator supplied to the Container.

type DecorateOption

type DecorateOption interface {
	// contains filtered or unexported methods
}

DecorateOption modifies the default behavior of Decorate.

func FillDecorateInfo

func FillDecorateInfo(info *DecorateInfo) DecorateOption

FillDecorateInfo is a DecorateOption that writes info on what Dig was able to get out of the provided decorator into the provided DecorateInfo.

type GetOption

type GetOption interface {
	// contains filtered or unexported methods
}

An GetOption modifies the default behavior of Get. It's included for future functionality; currently, there are no concrete implementations.

type ID

type ID int

ID is a unique integer representing the constructor node in the dependency graph.

type In

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

In may be embedded into structs to request dig to treat them as special parameter structs. When a constructor accepts such a struct, instead of the struct becoming a dependency for that constructor, all its fields become dependencies instead. See the section on Parameter Objects in the package-level documentation for more information.

Fields of the struct may optionally be tagged to customize the behavior of dig. The following tags are supported,

name        Requests a value with the same name and type from the
            container. See Named Values for more information.
optional    If set to true, indicates that the dependency is optional and
            the constructor gracefully handles its absence.
group       Name of the Value Group from which this field will be filled.
            The field must be a slice type. See Value Groups in the
            package documentation for more information.

type Input

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

Input contains information on an input parameter of a function.

func (*Input) String

func (i *Input) String() string

type InvokeOption

type InvokeOption interface {
	// contains filtered or unexported methods
}

An InvokeOption modifies the default behavior of Invoke. It's included for future functionality; currently, there are no concrete implementations.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option configures a Container. It's included for future functionality; currently, there are no concrete implementations.

func DeferAcyclicVerification

func DeferAcyclicVerification() Option

DeferAcyclicVerification is an Option to override the default behavior of container.Provide, deferring the dependency graph validation to no longer run after each call to container.Provide. The container will instead verify the graph on first `Invoke`.

Applications adding providers to a container in a tight loop may experience performance improvements by initializing the container with this option.

func DryRun

func DryRun(dry bool) Option

DryRun is an Option which, when set to true, disables invocation of functions supplied to Provide and Invoke. Use this to build no-op containers.

type Out

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

Out may be embedded into structs to request dig to treat them as special result structs. When a constructor returns such a struct, instead of the struct becoming a result of the constructor, all its fields become results of the constructor. See the section on Result Objects in the package-level documentation for more information.

Fields of the struct may optionally be tagged to customize the behavior of dig. The following tags are supported,

name        Specifies the name of the value. Only a field on a dig.In
            struct with the same 'name' annotation can receive this
            value. See Named Values for more information.
group       Name of the Value Group to which this field's value is being
            sent. See Value Groups in the package documentation for more
            information.

type Output

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

Output contains information on an output produced by a function.

func (*Output) String

func (o *Output) String() string

type ProvideInfo

type ProvideInfo struct {
	ID      ID
	Inputs  []*Input
	Outputs []*Output
}

ProvideInfo provides information about the constructor's inputs and outputs types as strings, as well as the ID of the constructor supplied to the Container. It contains ID for the constructor, as well as slices of Input and Output types, which are Stringers that report the types of the parameters and results respectively.

type ProvideOption

type ProvideOption interface {
	// contains filtered or unexported methods
}

A ProvideOption modifies the default behavior of Provide.

func As

func As(i ...interface{}) ProvideOption

As is a ProvideOption that specifies that the value produced by the constructor implements one or more other interfaces and is provided to the container as those interfaces.

As expects one or more pointers to the implemented interfaces. Values produced by constructors will be then available in the container as implementations of all of those interfaces, but not as the value itself.

For example, the following will make io.Reader and io.Writer available in the container, but not buffer.

c.Provide(newBuffer, dig.As(new(io.Reader), new(io.Writer)))

That is, the above is equivalent to the following.

c.Provide(func(...) (io.Reader, io.Writer) {
  b := newBuffer(...)
  return b, b
})

If used with dig.Name, the type produced by the constructor and the types specified with dig.As will all use the same name. For example,

c.Provide(newFile, dig.As(new(io.Reader)), dig.Name("temp"))

The above is equivalent to the following.

type Result struct {
  dig.Out

  Reader io.Reader `name:"temp"`
}

c.Provide(func(...) Result {
  f := newFile(...)
  return Result{
    Reader: f,
  }
})

This option cannot be provided for constructors which produce result objects.

func Export

func Export(export bool) ProvideOption

Export is a ProvideOption which specifies that the provided function should be made available to all Scopes available in the application, regardless of which Scope it was provided from. By default, it is false.

For example,

c := New()
s1 := c.Scope("child 1")
s2:= c.Scope("child 2")
s1.Provide(func() *bytes.Buffer { ... })

does not allow the constructor returning *bytes.Buffer to be made available to the root Container c or its sibling Scope s2.

With Export, you can make this constructor available to all the Scopes:

s1.Provide(func() *bytes.Buffer { ... }, Export(true))

func FillProvideInfo

func FillProvideInfo(info *ProvideInfo) ProvideOption

FillProvideInfo is a ProvideOption that writes info on what Dig was able to get out of the provided constructor into the provided ProvideInfo.

func Group

func Group(group string) ProvideOption

Group is a ProvideOption that specifies that all values produced by a constructor should be added to the specified group. See also the package documentation about Value Groups.

This option cannot be provided for constructors which produce result objects.

func LocationForPC

func LocationForPC(pc uintptr) ProvideOption

LocationForPC is a ProvideOption which specifies an alternate function program counter address to be used for debug information. The package, name, file and line number of this alternate function address will be used in err messages and DOT graphs. This option is intended to be used with functions created with the reflect.MakeFunc method whose err messages are otherwise hard to understand

func Name

func Name(name string) ProvideOption

Name is a ProvideOption that specifies that all values produced by a constructor should have the given name. See also the package documentation about Named Values.

Given,

func NewReadOnlyConnection(...) (*Connection, err)
func NewReadWriteConnection(...) (*Connection, err)

The following will provide two connections to the container: one under the name "ro" and the other under the name "rw".

c.Provide(NewReadOnlyConnection, dig.Name("ro"))
c.Provide(NewReadWriteConnection, dig.Name("rw"))

This option cannot be provided for constructors which produce result objects.

type RemoveOption

type RemoveOption interface {
	// contains filtered or unexported methods
}

An RemoveOption modifies the default behavior of Remove. It's included for future functionality; currently, there are no concrete implementations.

type Scope

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

Scope is a scoped DAG of types and their dependencies. A Scope may also have one or more child Scopes that inherit from it.

func (*Scope) Decorate

func (s *Scope) Decorate(decorator interface{}, opts ...DecorateOption) error

Decorate provides a decorator for a type that has already been provided in the Scope.

Similar to Provide, Decorate takes in a function with zero or more dependencies and one or more results. Decorate can be used to modify a type that was already introduced to the Scope, or completely replace it with a new object.

For example,

s.Decorate(func(log *zap.Logger) *zap.Logger {
  return log.Named("myapp")
})

This takes in a value, augments it with a name, and returns a replacement for it. Functions in the Scope's dependency graph that use *zap.Logger will now use the *zap.Logger returned by this decorator.

A decorator can also take in multiple parameters and replace one of them:

s.Decorate(func(log *zap.Logger, cfg *Config) *zap.Logger {
  return log.Named(cfg.Name)
})

Or replace a subset of them:

s.Decorate(func(
  log *zap.Logger,
  cfg *Config,
  scope metrics.Scope
) (*zap.Logger, metrics.Scope) {
  log = log.Named(cfg.Name)
  scope = scope.With(metrics.Tag("service", cfg.Name))
  return log, scope
})

Decorating a Scope affects all the child scopes of this Scope.

Similar to a provider, the decorator function gets called *at most once*.

func (*Scope) Get

func (s *Scope) Get(service reflect.Type, opts ...GetOption) ([]interface{}, error)

Get runs the given function after instantiating its dependencies.

Any arguments that the function has are treated as its dependencies. The dependencies are instantiated in an unspecified order along with any dependencies that they might have.

The function may return an err to indicate failure. The err will be returned to the caller as-is.

func (*Scope) Invoke

func (s *Scope) Invoke(function interface{}, opts ...InvokeOption) error

Invoke runs the given function after instantiating its dependencies.

Any arguments that the function has are treated as its dependencies. The dependencies are instantiated in an unspecified order along with any dependencies that they might have.

The function may return an err to indicate failure. The err will be returned to the caller as-is.

func (*Scope) Provide

func (s *Scope) Provide(constructor interface{}, opts ...ProvideOption) error

Provide teaches the Scope how to build values of one or more types and expresses their dependencies.

The first argument of Provide is a function that accepts zero or more parameters and returns one or more results. The function may optionally return an err to indicate that it failed to build the value. This function will be treated as the constructor for all the types it returns. This function will be called AT MOST ONCE when a type produced by it, or a type that consumes this function's output, is requested via Invoke. If the same types are requested multiple times, the previously produced value will be reused.

Provide accepts argument types or dig.In structs as dependencies, and separate return values or dig.Out structs for results.

When a constructor is Provided to a Scope, it will propagate this to any Scopes that are descendents, but not ancestors of this Scope. To provide a constructor to all the Scopes available, provide it to Container, which is the root Scope.

func (*Scope) Remove

func (s *Scope) Remove(ctor interface{}, opts ...RemoveOption) error

Remove removes the instance of a registered service.

func (*Scope) Scope

func (s *Scope) Scope(name string, opts ...ScopeOption) *Scope

Scope creates a new Scope with the given name and options from current Scope. Any constructors that the current Scope knows about, as well as any modifications made to it in the future will be propagated to the child scope. However, no modifications made to the child scope being created will be propagated to the parent Scope.

func (*Scope) String

func (s *Scope) String() string

String representation of the entire Scope

type ScopeOption

type ScopeOption interface {
	// contains filtered or unexported methods
}

A ScopeOption modifies the default behavior of Scope; currently, there are no implementations.

type VisualizeOption

type VisualizeOption interface {
	// contains filtered or unexported methods
}

A VisualizeOption modifies the default behavior of Visualize.

func VisualizeError

func VisualizeError(err error) VisualizeOption

VisualizeError includes a visualization of the given err in the output of Visualize if an err was returned by Invoke or Provide.

if err := c.Provide(...); err != nil {
  dig.Visualize(c, w, dig.VisualizeError(err))
}

This option has no effect if the err was nil or if it didn't contain any information to visualize.

Directories

Path Synopsis
internal
digtest
Package digtest provides utilities used by dig internally to test its own functionality.
Package digtest provides utilities used by dig internally to test its own functionality.
dot

Jump to

Keyboard shortcuts

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