Coder Social home page Coder Social logo

golobby / container Goto Github PK

View Code? Open in Web Editor NEW
545.0 8.0 35.0 122 KB

A lightweight yet powerful IoC dependency injection container for the Go programming language

License: MIT License

Go 100.00%
ioc ioc-container container dependency-injection go golang golang-package di-container di-framework inversion-of-control

container's People

Contributors

amirrezaask avatar cililing avatar dependabot[bot] avatar fifsky avatar miladrahimi avatar pavel-durov avatar place1 avatar sedhossein avatar vanodevium avatar wirecat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

container's Issues

README documentation is not up to date

I just installed the package and gave it a go. I was following the README code examples, but I got some type errors.
Installation:

$ go get github.com/golobby/container/v3

My code following README example:

type Test interface {
	Test()
}
type TestImpl struct {
}

func (iml *TestImpl) Test() {
	fmt.Println("Test")
}

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

	var c TestImpl
	err := container.Resolve(&c)
	// `c` will be the instance of JsonConfig
}

Errors:

container.Singleton((func() Test literal)) (no value) used as valuecompilerTooManyValues

Resolve not declared by package containercompilerUndeclaredImportedName

It looks like there:

  • Instead of resolve there's a Make function defined
  • sIngleton function doesn't return an error?

Is it an issue with outdated documentation, or am I doing something weird here?

Thanks :)

will this project detect circular dependency?

i am looking for an ioc container that can solve circular dependency.

i cannot find source code which do detect circular dependency from the project.

anyway, this project design is a very useful example for me, thank you!

go.work issue

not work if i add go.work file with inner dependencies

Singleton's are created twice when resolver returns (T, error)

i've added some example code below and i've found that the singleton is created twice.

c := container.New()

c.Singleton(func() (ExampleService, error) {
	fmt.Println("singleton created")
	return CreateExampleService()
})

the issue only occurs because the resolver function returns a 2-tuple of (ExampleService, error)

the issue is happening because of the invoke call happening in this loop (I think) https://github.com/golobby/container/blob/master/container.go#L101

Implicit binding

It could be nice if it supports receiving implementations when the requested type is implementation not interface.

Enable to Get Error on Binding

Hello, I am using your great library for Dependency injection.
However, I want to retrieve errors when binding.
For instance, my code below is to open SQLite DB, but when Opening DB failed, I have to call panic() to show an error.

container.Singleton(func() *gorm.DB {
		DBFile := "/tmp/test.db"
		dialector := sqlite.Open(DBFile)
		db, err := gorm.Open(dialector)

		if err != nil {
			panic(err)
		}

		return db.Debug()
	})

So, I want you to enable me to run the code like below to retrieve the error.
Right now, when error occurs, the code below just cause nil reference error.

container.Singleton(func() (*gorm.DB, error) {
		DBFile := "/tmp/test.db"
		dialector := sqlite.Open(DBFile)
		db, err := gorm.Open(dialector)

		if err != nil {
			return nil, err
		}

		return db.Debug(), nil
	})

Thanks,

Singleton always cast second parameter as error

First of all, thanks for this amazing library.
Here is what my singleton call is like

container.Singleton(func() (*gorm.DB, error) {
        dbFile := "/tmp/test.db"
	dialector := sqlite.Open(dbFile)
	db, err := gorm.Open(dialector)

	if err != nil {
		return nil, err
	}

	return db.Debug(), nil
})

But I am always getting panic: interface conversion: interface is nil, not error even when error is nil.

I think there should be a check for nil before casting to error type in

return values[0].Interface(), values[1].Interface().(error)

Can you please look into it?

Thanks

Any plans to support child or cloned containers?

A feature that would be extremely useful would be extremely useful would be to support the creation of child or cloned containers. A child container is more complex, being an empty container with a reference to a parent container and walking up the hierarchy as it tries to resolve types, but a clone would just duplicate existing bindings at the point it was cloned and then set itself as the Container object binding in place of the original container.

The use case for this is to allow for a "scoped" container to be created. For example when handling a HTTP request objects like a context.Context generated by a HTTP request, as well as the Request and ResponseWriter could be populated to a child/clone container while handling the request and then the Call method could be used to populate required Classes for a handler method.

Singleton resolvers that return error during resolution should not cache the concrete instance

Description

Singleton resolvers that return error during resolution should not cache the concrete instance

Steps to Reproduce

Given the following Go struct

type Foo struct {
    Value string
}

func NewFoo(value string) *Foo {
  return &Foo{ Value: value }
}

When using the container in this package if I register a resolver for a singleton where it could conditionally fail based on some expectation:

container.Singleton(func() (*Foo, error) {
  if expectationsAreMet {
    return NewFoo("test"), nil
  } else {
    return nil, errors.New("cannot create this type right now")
  }
})

Then when the caller attempts to re-resolve the type the cached nil value is returned instead of the resolver trying again.

var instance *Foo
err := container.Resolve(&instance) // Resolution can fail here if external expectation is not met
if err != nil {
  // Perform some bootstrapping to meeting required expectation
  external.MeetExpectation()
  
  // Try to resolve the type again after expectations are met
  err = container.Resolve(&instance) // Resolver should try again now that expectation has been met.
}

Expected

The registered singleton resolver should run again since the original resolution failed and return an error

Actual

The nil value is stored as the concrete value for the singleton and is returned on subsequent Resolve(...) calls.

How to get all named instances?

We have some bindings of named implementations:

container.NamedSingleton("square", func() Shape {
    return &Rectangle{}
})
container.NamedSingleton("rounded", func() Shape {
    return &Circle{}
})

How to get all registered implementations of Shape?
Something like this:

var shapes map[string]Shape
container.ResolveAll(&shapes)

if this is not implemented, then it seems to me it would be a cool feature.

Possible outdated README documentaion

I just installed the package and gave it a go. I was following the README code examples, but I got some type errors.
Installation:

$ go get github.com/golobby/container/v3

go.sum:

github.com/golobby/container v1.3.0 h1:Pgk8fK9fJHuZqU924Bl8+DY2/n9jCUtsSKfiOctdQ9A=
github.com/golobby/container v1.3.0/go.mod h1:6yAH4QK+Hi8HxGuCJuAGiqS/a5n8YP+4bXNpPdKzLVM=
github.com/golobby/container/v3 v3.3.0 h1:ohPSWUUY67yvOhIBVC9Wv1pnsh8iOwRS0U1M1bjKBG8=
github.com/golobby/container/v3 v3.3.0/go.mod h1:RDdKpnKpV1Of11PFBe7Dxc2C1k2KaLE4FD47FflAmj0=

My code following README example:

type Test interface {
	Test()
}
type TestImpl struct {
}

func (iml *TestImpl) Test() {
	fmt.Println("Test")
}

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

	var c TestImpl
	err := container.Resolve(&c)
	// `c` will be the instance of JsonConfig
}

Errors:

container.Singleton((func() Test literal)) (no value) used as valuecompilerTooManyValues

Resolve not declared by package containercompilerUndeclaredImportedName

It looks like there:

  • Instead of resolve there's a Make function defined
  • sIngleton function doesn't return an error?

Is it an issue with outdated documentation, or am I doing something weird here?

Thanks :)

Integration with Viper

Hi there,

This is a great component to what I'm looking for... :) I'd like to integrate VIper with Container:

  1. Users create configuration files, say in yaml
  2. In order to load the properties, I'd like to have a strongly-typed representation using structs that directly maps to the files
  3. As a developer, I'd like to get the singleton instance of this struct so I can use it

I'd like to do something similar to ConfigurationProperties of SpringBoot from https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-external-config.html

Has someone worked on something similar?

Thank you

How we can get property of an implementation?

I am using golobby/container/v3 :

type Database interface {
}

type DatabaseImpl struct {
    MySql interface{}
    PgSql interface{}
}

container.Singleton(func() Database {
    return &DatabaseImpl {
        MySql: mysql.CreateConnection(),
        PgSql: pgsql.CreateConnection(),
    }
})

Now I want to get an instance of MySql connection, so how can I get it?

I try to do:

var d Database
container.Resolve(&d)

result, _ := d.MySql.Query(...)

but i get an error type Database has no field or method MySql

Question: Resolve multiple objects

Is there a way to resolve multiple items by its common interface?

Example:
IPlugin abstract
PluginA concrete
PluginB concrete
register both plugins by IPlugin
resolve []IPlugin where both plugins will be returned

Support synchronization of accessing container bindings

It will be nice to be able to register and access container bindings asynchronously (e.g. registration of some new binding during application run while some another parts of app resolves existent bindings). Probably some synchronized version of Container can be created for such a purposes instead of changing existent one, but anyway it will be useful feature.

Support multiple binding

The resolver function could return multiple implementations so it can bind some abstractions at once and prevent panic.

Add Must method variants

great library, would you reconsider this issue #4 and add "must" variants for the container methods.

container.MustSingleton
container.MustTransient

etc.

all container methods that can return an error and are typically called in main() or init() could have a "must" variant.

the must variant panics if an error occurs instead of returning it.

for example, in my application I register dependencies with the container in main() and the "if err != nil { panic(err) }" handling makes the code harder to read but if we leave it out then invalid container configuration can silently occur.

Get the receiver/instance from the concrete / AutoBind based on tags

Hi, I am working on a project where we use the container library.

We are trying to "tune" the usage of the DI container with custom tags, to get rid of call-chains like

func NewInstance() *Instance {
    var repo repository.Notifications
    var bridge fcm.Bridge
    var timeProvider tp.TimeProvider

    main.DI.Make(&repo)
    main.DI.Make(&bridge)
    main.DI.Make(&timeProvider)

    // return instance...
}

What I want to do is to mark some struct fields with a custom tag, let's say di:"inject" to automatically inject the fields at some point (like execute call).

What I've already achieved is the following code:

type Instance struct {
    context ctx.Context
    
    repo repository.Notifications `di:"bind"`
    ...
}

func inject(gc GoCase) {
	rv := reflect.ValueOf(gc).Elem()
	t := rv.Type()

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		rvField := rv.Field(i)
		if value, ok := field.Tag.Lookup("di"); ok {
			if value == "bind" {

				y := reflect.NewAt(rvField.Type(), unsafe.Pointer(rvField.UnsafeAddr())).Elem()
				receiverType := y.Type()
				concrete, ok := DI[receiverType] // DI is the app-container
				if !ok {
					panic("concrete not found for the abstraction" + receiverType.String())
				}

				// Concrete found, but how do I get instance/receiver from the concrete?
				// Do I need to use the reflection here? Or maybe it's better to add/expose
				// a possibility to do that in the library?
				
				fmt.Print(concrete) // just for testing
			}
		}
	}
}

My wonderings are explained in the comment.

And I see a few possibilities:

  • use reflection to find the concrete (doesn't sound good to me, to be honest)
  • expose proper fields or add a proper method in the library (like GetConcrete(Type))
  • add an "extension" to the library that allows doing the bindings based on a tag, let's say di:"inject" (I can implement it, but first I would like to know what you think about it).
  • or, maybe you see another way to do that? :)

Thanks for your input!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.