container

package module
v3.3.2 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2023 License: MIT Imports: 4 Imported by: 50

README

Go Reference CI CodeQL Go Report Card Coverage Status Mentioned in Awesome Go

Container

GoLobby Container is a lightweight yet powerful IoC (dependency injection) container for Go projects. It's built neat, easy-to-use, and performance-in-mind to be your ultimate requirement.

Features:

  • Singleton and Transient bindings
  • Named dependencies (bindings)
  • Resolve by functions, variables, and structs
  • Must helpers that convert errors to panics
  • Optional lazy loading of bindings
  • Global instance for small applications
  • 100% Test coverage!

Documentation

Required Go Versions

It requires Go v1.11 or newer versions.

Installation

To install this package, run the following command in your project directory.

go get github.com/golobby/container/v3

Next, include it in your application:

import "github.com/golobby/container/v3"
Introduction

GoLobby Container is used to bind abstractions to their implementations. Binding is the process of introducing appropriate concretes (implementations) of abstractions to an IoC container. In this process, you also determine the resolving type, singleton or transient. In singleton bindings, the container provides an instance once and returns it for all the requests. In transient bindings, the container always returns a brand-new instance for each request. After the binding process, you can ask the IoC container to make the appropriate implementation of the abstraction that your code needs. Then your code will depend on abstractions, not implementations!

Quick Start

The following example demonstrates a simple binding and resolving.

// Bind Config interface to JsonConfig struct
err := container.Singleton(func() Config {
    return &JsonConfig{...}
})

var c Config
err := container.Resolve(&c)
// `c` will be the instance of JsonConfig
Typed Binding
Singleton

The following snippet expresses singleton binding.

err := container.Singleton(func() Abstraction {
  return Implementation
})

// If you might return an error...

err := container.Singleton(func() (Abstraction, error) {
  return Implementation, nil
})

It takes a resolver (function) whose return type is the abstraction and the function body returns the concrete (implementation).

The example below shows a singleton binding.

err := container.Singleton(func() Database {
  return &MySQL{}
})
Transient

The example below shows a transient binding.

err := container.Transient(func() Shape {
  return &Rectangle{}
})
Named Bindings

You may have different concretes for an abstraction. In this case, you can use named bindings instead of typed bindings. Named bindings take the dependency name into account as well. The rest is similar to typed bindings. The following examples demonstrate some named bindings.

// Singleton
err := container.NamedSingleton("square", func() Shape {
    return &Rectangle{}
})
err := container.NamedSingleton("rounded", func() Shape {
    return &Circle{}
})

// Transient
err := container.NamedTransient("sql", func() Database {
    return &MySQL{}
})
err := container.NamedTransient("noSql", func() Database {
    return &MongoDB{}
})
Resolver Errors

The process of creating concrete (resolving) might face an error. In this case, you can return the error as the second return value like the example below.

err := container.Transient(func() (Shape, error) {
  return nil, errors.New("my-app: cannot create a Shape implementation")
})

It could be applied to other binding types.

Resolving

Container resolves the dependencies with the Resolve(), Call(), and Fill() methods.

Using References

The Resolve() method takes reference of the abstraction type and fills it with the appropriate concrete.

var a Abstraction
err := container.Resolve(&a)
// `a` will be an implementation of the Abstraction

Example of resolving using references:

var m Mailer
err := container.Resolve(&m)
// `m` will be an implementation of the Mailer interface
m.Send("contact@miladrahimi.com", "Hello Milad!")

Example of named-resolving using references:

var s Shape
err := container.NamedResolve(&s, "rounded")
// `s` will be an implementation of the Shape that named rounded
Using Closures

The Call() method takes a receiver (function) with arguments of abstractions you need. It calls it with parameters of appropriate concretes.

err := container.Call(func(a Abstraction) {
    // `a` will be an implementation of the Abstraction
})

Example of resolving using closures:

err := container.Call(func(db Database) {
  // `db` will be an implementation of the Database interface
  db.Query("...")
})

You can also resolve multiple abstractions like the following example:

err := container.Call(func(db Database, s Shape) {
  db.Query("...")
  s.Area()
})

You are able to raise an error in your receiver function, as well.

err := container.Call(func(db Database) error {
  return db.Ping()
})
// err could be `db.Ping()` error.

Caution: The Call() method does not support named bindings.

Using Structs

The Fill() method takes a struct (pointer) and resolves its fields.

The example below expresses how the Fill() method works.

type App struct {
    mailer Mailer   `container:"type"`
    sql    Database `container:"name"`
    noSql  Database `container:"name"`
    other  int
}

myApp := App{}

err := container.Fill(&myApp)

// [Typed Bindings]
// `myApp.mailer` will be an implementation of the Mailer interface

// [Named Bindings]
// `myApp.sql` will be a sql implementation of the Database interface
// `myApp.noSql` will be a noSql implementation of the Database interface

// `myApp.other` will be ignored since it has no `container` tag
Binding time

You can resolve dependencies at the binding time if you need previous dependencies for the new one.

The following example shows resolving dependencies at binding time.

// Bind Config to JsonConfig
err := container.Singleton(func() Config {
    return &JsonConfig{...}
})

// Bind Database to MySQL
err := container.Singleton(func(c Config) Database {
    // `c` will be an instance of `JsonConfig`
    return &MySQL{
        Username: c.Get("DB_USERNAME"),
        Password: c.Get("DB_PASSWORD"),
    }
})
Standalone Instance

By default, the Container keeps your bindings in the global instance. Sometimes you may want to create a standalone instance for a part of your application. If so, create a standalone instance like the example below.

c := container.New()

err := c.Singleton(func() Database {
    return &MySQL{}
})

err := c.Call(func(db Database) {
    db.Query("...")
})

The rest stays the same. The global container is still available.

Must Helpers

You might believe that the container shouldn't raise any error and/or you prefer panics. In this case, Must helpers are for you. Must helpers are global methods that panic instead of returning errors.

c := container.New()
// Global instance:
// c := container.Global

container.MustSingleton(c, func() Shape {
    return &Circle{a: 13}
})

container.MustCall(c, func(s Shape) {
    // ...
})

// Other Must Helpers:
// container.MustSingleton()
// container.MustSingletonLazy()
// container.MustNamedSingleton()
// container.MustNamedSingletonLazy()
// container.MustTransient()
// container.MustTransientLazy()
// container.MustNamedTransient()
// container.MustNamedTransientLazy()
// container.MustCall()
// container.MustResolve()
// container.MustNamedResolve()
// container.MustFill()
Lazy Binding

Both the singleton and transient binding calls have a lazy version. Lazy versions defer calling the provided resolver function until the first call. For singleton bindings, It calls the resolver function only once and stores the result.

Lazy binding methods:

  • container.SingletonLazy()
  • container.NamedSingletonLazy()
  • container.TransientLazy()
  • container.NamedTransientLazy()
Performance

The package Container inevitably uses reflection for binding and resolving processes. If performance is a concern, try to bind and resolve the dependencies where it runs only once, like the main and init functions.

License

GoLobby Container is released under the MIT License.

Documentation

Overview

Package container is a lightweight yet powerful IoC container for Go projects. It provides an easy-to-use interface and performance-in-mind container to be your ultimate requirement.

Index

Constants

This section is empty.

Variables

View Source
var Global = New()

Global is the global concrete of the Container.

Functions

func Call

func Call(receiver interface{}) error

Call calls the same method of the global concrete.

func Fill

func Fill(receiver interface{}) error

Fill calls the same method of the global concrete.

func MustCall added in v3.2.0

func MustCall(c Container, receiver interface{})

MustCall wraps the `Call` method and panics on errors instead of returning the errors.

func MustFill added in v3.2.0

func MustFill(c Container, receiver interface{})

MustFill wraps the `Fill` method and panics on errors instead of returning the errors.

func MustNamedResolve added in v3.2.0

func MustNamedResolve(c Container, abstraction interface{}, name string)

MustNamedResolve wraps the `NamedResolve` method and panics on errors instead of returning the errors.

func MustNamedSingleton added in v3.2.0

func MustNamedSingleton(c Container, name string, resolver interface{})

MustNamedSingleton wraps the `NamedSingleton` method and panics on errors instead of returning the errors.

func MustNamedSingletonLazy added in v3.3.0

func MustNamedSingletonLazy(c Container, name string, resolver interface{})

MustNamedSingleton wraps the `NamedSingletonLazy` method and panics on errors instead of returning the errors.

func MustNamedTransient added in v3.2.0

func MustNamedTransient(c Container, name string, resolver interface{})

MustNamedTransient wraps the `NamedTransient` method and panics on errors instead of returning the errors.

func MustNamedTransientLazy added in v3.3.0

func MustNamedTransientLazy(c Container, name string, resolver interface{})

MustNamedTransient wraps the `NamedTransientLazy` method and panics on errors instead of returning the errors.

func MustResolve added in v3.2.0

func MustResolve(c Container, abstraction interface{})

MustResolve wraps the `Resolve` method and panics on errors instead of returning the errors.

func MustSingleton added in v3.2.0

func MustSingleton(c Container, resolver interface{})

MustSingleton wraps the `Singleton` method and panics on errors instead of returning the errors.

func MustSingletonLazy added in v3.3.0

func MustSingletonLazy(c Container, resolver interface{})

MustSingleton wraps the `SingletonLazy` method and panics on errors instead of returning the errors.

func MustTransient added in v3.2.0

func MustTransient(c Container, resolver interface{})

MustTransient wraps the `Transient` method and panics on errors instead of returning the errors.

func MustTransientLazy added in v3.3.0

func MustTransientLazy(c Container, resolver interface{})

MustTransientLazy wraps the `TransientLazy` method and panics on errors instead of returning the errors.

func NamedResolve

func NamedResolve(abstraction interface{}, name string) error

NamedResolve calls the same method of the global concrete.

func NamedSingleton

func NamedSingleton(name string, resolver interface{}) error

NamedSingleton calls the same method of the global concrete.

func NamedSingletonLazy added in v3.3.0

func NamedSingletonLazy(name string, resolver interface{}) error

NamedSingletonLazy calls the same method of the global concrete.

func NamedTransient

func NamedTransient(name string, resolver interface{}) error

NamedTransient calls the same method of the global concrete.

func NamedTransientLazy added in v3.3.0

func NamedTransientLazy(name string, resolver interface{}) error

NamedTransientLazy calls the same method of the global concrete.

func Reset

func Reset()

Reset calls the same method of the global concrete.

func Resolve

func Resolve(abstraction interface{}) error

Resolve calls the same method of the global concrete.

func Singleton

func Singleton(resolver interface{}) error

Singleton calls the same method of the global concrete.

func SingletonLazy added in v3.3.0

func SingletonLazy(resolver interface{}) error

SingletonLazy calls the same method of the global concrete.

func Transient

func Transient(resolver interface{}) error

Transient calls the same method of the global concrete.

func TransientLazy added in v3.3.0

func TransientLazy(resolver interface{}) error

TransientLazy calls the same method of the global concrete.

Types

type Container added in v3.1.4

type Container map[reflect.Type]map[string]*binding

Container holds the bindings and provides methods to interact with them. It is the entry point in the package.

func New

func New() Container

New creates a new concrete of the Container.

func (Container) Call added in v3.1.4

func (c Container) Call(function interface{}) error

Call takes a receiver function with one or more arguments of the abstractions (interfaces). It invokes the receiver function and passes the related concretes.

func (Container) Fill added in v3.1.4

func (c Container) Fill(structure interface{}) error

Fill takes a struct and resolves the fields with the tag `container:"inject"`

func (Container) NamedResolve added in v3.1.4

func (c Container) NamedResolve(abstraction interface{}, name string) error

NamedResolve takes abstraction and its name and fills it with the related concrete.

func (Container) NamedSingleton added in v3.1.4

func (c Container) NamedSingleton(name string, resolver interface{}) error

NamedSingleton binds a named abstraction to concrete in singleton mode.

func (Container) NamedSingletonLazy added in v3.3.0

func (c Container) NamedSingletonLazy(name string, resolver interface{}) error

NamedSingleton binds a named abstraction to concrete lazily in singleton mode. The concrete is resolved only when the abstraction is resolved for the first time.

func (Container) NamedTransient added in v3.1.4

func (c Container) NamedTransient(name string, resolver interface{}) error

NamedTransient binds a named abstraction to concrete lazily in transient mode.

func (Container) NamedTransientLazy added in v3.3.0

func (c Container) NamedTransientLazy(name string, resolver interface{}) error

NamedTransient binds a named abstraction to concrete in transient mode. Normally the resolver will be called during registration, but that is skipped in lazy mode.

func (Container) Reset added in v3.1.4

func (c Container) Reset()

Reset deletes all the existing bindings and empties the container.

func (Container) Resolve added in v3.1.4

func (c Container) Resolve(abstraction interface{}) error

Resolve takes an abstraction (reference of an interface type) and fills it with the related concrete.

func (Container) Singleton added in v3.1.4

func (c Container) Singleton(resolver interface{}) error

Singleton binds an abstraction to concrete in singleton mode. It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface). The resolver function can have arguments of abstraction that have been declared in the Container already.

func (Container) SingletonLazy added in v3.3.0

func (c Container) SingletonLazy(resolver interface{}) error

SingletonLazy binds an abstraction to concrete lazily in singleton mode. The concrete is resolved only when the abstraction is resolved for the first time. It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface). The resolver function can have arguments of abstraction that have been declared in the Container already.

func (Container) Transient added in v3.1.4

func (c Container) Transient(resolver interface{}) error

Transient binds an abstraction to concrete in transient mode. It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface). The resolver function can have arguments of abstraction that have been declared in the Container already.

func (Container) TransientLazy added in v3.3.0

func (c Container) TransientLazy(resolver interface{}) error

TransientLazy binds an abstraction to concrete lazily in transient mode. Normally the resolver will be called during registration, but that is skipped in lazy mode. It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface). The resolver function can have arguments of abstraction that have been declared in the Container already.

Jump to

Keyboard shortcuts

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