Giter Site home page Giter Site logo

di's Introduction

Hi, I'm Max

di's People

Contributors

achoarnold avatar chirino avatar johnmaguire avatar matdurand 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  avatar  avatar

di's Issues

Singleton

Hi,

Is there a concept of a singleton? For example, database connections?

Thank you

Factory function returning other di.Provide

Sorry for the multiple questions. I did search through the previous issues, but could not find what I was looking for.

I'm trying to have a function that would return a variable number of dependencies to add to the DI container, but where the function also received some dependencies from previously declared di.Provide. I saw this answer which seems to hint that it's possible for a di.Provide to return di.Option, but I cannot make it work.

Here is an example:


type moduleConfig struct {
	di.Inject
	cfg1 string `di:"cfg=m1.v1"`
	cfg2 string `di:"cfg=m2.v1"`
}

func provideModules(cfg moduleConfig) di.Option {
	//imagine some hypothetical conditions to decide which modules to create. 0-n modules can be created
	//using moduleConfig to decide...
	return di.Options(
		di.Provide(m1.NewModule1, di.As(new(component.Module))),
		di.Provide(m2.NewModule2, di.As(new(component.Module))),
	)
}

func startModules(modules []component.Module) error {
    //... do something with the declared modules
    return nil
}

func main() {
	c, err := di.New(
		di.ProvideValue("aaa", di.Tags{"cfg": "m1.v1"}),
		di.ProvideValue("bbb", di.Tags{"cfg": "m2.v1"}),
		di.Provide(provideModules),
	)
	if err != nil {
		panic(err)
	}

	err = c.Invoke(startModules)
	if err != nil {
		panic(err)
	}
}

Possibility to disable singleton behavior.

Does it possible to disable singleton behavior for some dependencies? Probably some ResolveOption or ProvideOption for that.

For example I want use goava/di container for testing with mocks. I have two different setups:

  • di.New(...) with production implementation of interfaces which is used for release builds.
  • di.New(...) with mocks which replace external APIs implementations.

Majority of mock libs assumes that each test recreate mock object but di container always returns same instance of mock object.
I can call di.New for each test case but it is not looks good.

Provide values via custom string types

Hey,

Maybe a bit related to #39 but on the Provide side of things.

It seems that I cannot do this, why? Seems like a better way than using tags to identify strings.

package main

import (
	"github.com/defval/di"
)

type ProjectName string

func main() {
	_, err := di.New(
		di.ProvideValue("project1", di.As(new(ProjectName))),
	)
	if err != nil {
		panic(err)
	}
}

I'm getting *main.ProjectName: not a pointer to interface

Unable to make interfaces group by using the same funtion interface implenentations

The title :)
https://play.golang.org/p/ZvWhZaxyzM6

package main

import (
	"context"
	"log"

	"github.com/goava/di"
)

type Interface interface {
	Do(ctx context.Context) error
}

type FuncInterface func(ctx context.Context) error
func (c FuncInterface) Do(ctx context.Context) error {
	return c(ctx)
}

func createFirst() FuncInterface {
	return func(ctx context.Context) error {
		return nil
	}
}

func createSecond() FuncInterface {
	return func(ctx context.Context) error {
		return nil
	}
}

func main() {
	container, err := di.New(
		di.Provide(createFirst, di.As(new(Interface))),
		di.Provide(createSecond, di.As(new(Interface))),
	)
	if err != nil {
		log.Fatal(err)
	}

	var interfaces []Interface
	err = container.Resolve(&interfaces)
	if err != nil {
		log.Fatal(err)
	}
}

The error:

main.FuncInterface already exists in dependency graph

Duplicate instance with di.As

Hello
Here the the test case :

package main

import (
	"fmt"
	"github.com/goava/di"
)

type I interface{}

type A struct {}
type B struct {}

func main() {
	c, err := di.New()
	if err != nil {
		panic(err)
	}
	c.Provide(func() I {
		return A{}
	}, di.As(new(I)))
	c.Provide(func() I {
		return B{}
	}, di.As(new(I)))

	is := make([]I, 0)
	err = c.Resolve(&is)
	if err != nil {
		panic(err)
	}
	fmt.Println("Found ", len(is), " instances") 
}

This test display "Found 4 instances".

package main

import (
	"fmt"
	"github.com/goava/di"
)

type I interface{}

type A struct {}
type B struct {}

func main() {
	c, err := di.New()
	if err != nil {
		panic(err)
	}
	c.Provide(func() interface{} {
		return A{}
	}, di.As(new(I)))
	c.Provide(func() interface{} {
		return B{}
	}, di.As(new(I)))

	is := make([]I, 0)
	err = c.Resolve(&is)
	if err != nil {
		panic(err)
	}
	fmt.Println("Found ", len(is), " instances")
}

This test display "Found 2 instances"

The bug, if it is, occurs if constructor functions return a type which match those provided in di.As()

Handle embed unnamed dependency

Test case (container_test:840):

t.Run("constructor with injectable embed pointer", func(t *testing.T) {
	c, err := di.New()
	require.NoError(t, err)
	type InjectableType struct {
		di.Inject
		*http.ServeMux
	}
	mux := &http.ServeMux{}
	require.NoError(t, c.Provide(func() *http.ServeMux { return mux }))
	require.NoError(t, c.Provide(func() *InjectableType { return &InjectableType{} }))
	var result *InjectableType
	require.NoError(t, c.Resolve(&result))
	require.NotNil(t, result.ServeMux)
	require.Equal(t, fmt.Sprintf("%p", mux), fmt.Sprintf("%p", result.ServeMux))
})

Inject struct fields

my project full of injection functions like this

type Controller struct {
	user   types.UserService
	cfg    *config.Config
	friend types.FriendService
}

func NewController(cfg *config.Config, user types.UserService, friend types.FriendService) *Controller {
	return &Controller{
		user:   user,
		cfg:    cfg,
		friend: friend,
	}
}

I hope to support tag auto inject E.g: di.Parameter

type Controller struct {
	User   types.UserService   `di:""` // has `di` tag auto inject, di tag with name
	Cfg    *config.Config      `di:""`
	Friend types.FriendService `di:""`
}

func NewController() *Controller {
	return &Controller{}
}

Here is my simple implementation

diff --git a/parameter.go b/parameter.go
index acfe8e0..58a504b 100644
--- a/parameter.go
+++ b/parameter.go
@@ -2,6 +2,8 @@ package di
 
 import (
 	"reflect"
+	"strings"
+	"unsafe"
 )
 
 // isEmbedParameter
@@ -61,9 +63,63 @@ func (p parameter) ResolveValue(c *Container) (reflect.Value, error) {
 	if cleanup != nil {
 		c.cleanups = append(c.cleanups, cleanup)
 	}
+	// inject struct
+	err = p.ResolveProperty(c, value)
+	if err != nil {
+		return reflect.Value{}, err
+	}
 	return value, nil
 }
 
+const (
+	flagRO = 0b1100000
+)
+
+func ValuePatch(v reflect.Value) reflect.Value {
+	rv := reflect.ValueOf(&v)
+	flag := rv.Elem().FieldByName("flag")
+	ptrFlag := (*uintptr)(unsafe.Pointer(flag.UnsafeAddr()))
+	*ptrFlag = *ptrFlag &^ flagRO
+	return v
+}
+
+func (p parameter) ResolveProperty(c *Container, value reflect.Value) (err error) {
+	value = reflect.Indirect(value)
+	if value.Kind() != reflect.Struct {
+		return nil
+	}
+	vType := value.Type()
+	for i := 0; i < value.NumField(); i++ {
+		fieldType := vType.Field(i)
+		field := ValuePatch(value.Field(i))
+		tag, ok := fieldType.Tag.Lookup("di")
+		if ok {
+			var optional = false
+			var name string
+			tags := strings.Split(tag, ",")
+			for _, t := range tags {
+				t = strings.Trim(t, " ")
+				if t == "optional" {
+					optional = true
+					continue
+				}
+				name = t
+			}
+			pp := parameter{
+				name:     name,
+				typ:      fieldType.Type,
+				optional: optional,
+			}
+			param, err := pp.ResolveValue(c)
+			if err != nil {
+				return err
+			}
+			field.Set(param)
+		}
+	}
+	return nil
+}
+
 // internalParameter
 type internalParameter interface {
 	isDependencyInjectionParameter()

ValuePatch is private field set patch func, not necessary

How to build a service conditionally?

Hello.

I'm considering introducing DI into my project. Your DI implementation container looks good enough :) But I have a question: how to create services that are created through user's configuration? Here's an example:

package main

import (
	"github.com/goava/di"
	"github.com/spf13/viper"
)

type ServiceInterface interface {
	Do()
}

type DependencyA interface {}
type ServiceImplementationA struct {DependencyA}
func (s *ServiceImplementationA) Do() {}

type DependencyB interface {}
type ServiceImplementationB struct {DependencyB}
func (s *ServiceImplementationB) Do() {}

func NewService(config *viper.Viper) ServiceInterface {
	if viper.GetString("preferred_service") == "B" {
		// Resolve as ServiceImplementationB
	}

	// Otherwise resolve as ServiceImplementationA
}

func main() {
	container, _ := di.New(
		di.Provide(viper.New),
		di.Provide(NewService),
	)
	var service ServiceInterface
	container.Resolve(&service)
}

Depending on the configuration, I want to create a service interface implementation. But each implementation has its own dependencies list (I have not provided them manually in the example). I need a way to somehow call container.Resolve(&concreteImplementation) from the NewService function.

Is there any way to do this? Or maybe I just misunderstand how it should be done at all?

Add Late Provides

I am currently using a patched version of uber-go/fx and uber-go/dig where I already added these Features. It allows to execute code and add more options based on things that are in the Container.

type Configuration struct {
 	 Enabled bool
}

di.New(
di.Provide(func () *Configuration {
		return &Configuration{ true }
}),
di.Provide(func(configuration *Configuration) di.Option {
	if configuration.Enabled {
		return di.Invoke(func () { fmt.Println("Hello World") })
	}
		return nil
})
)

Important: Upcoming Transfer of This Repository on June 5th

Hello,

I hope this message finds you well. I am writing to inform you that I'm planning to transfer this Go library to my personal GitHub account this upcoming Monday, June 5th. This is an important change, and I want to explain how it could impact you, and how to mitigate any issues.

The good news is that GitHub will automatically redirect all requests from the old repository to the new one. This means that your current imports should continue to work as they are. However, the import paths in the new repository will change to reflect its new location, and I highly recommend updating your Go code to use these new import paths once the transfer is complete.

Here's what you can do to prepare:

  • Keep an eye on this repository for further updates related to the transfer
  • Once the transfer is complete, you should update your Go code to import this library from its new location: github.com/defval/di
  • If you encounter any issues or have any questions, please don't hesitate to raise an issue in the repository. I am here to help

I understand that this change might cause some inconvenience, and I apologize for that.

I greatly appreciate your understanding ๐Ÿค

Add a Application Start and Stop System

I am currently using uber-go/fx and I want to switch to DI, but sadly DI is missing a core feature to not run the invokes in compile state, instead having a Start() and Stop() function to kick off the Invokes and run until a control signal got sent, like CTRL+C.

	container := di.New(
// options
	)

	startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := container.Start(startCtx); err != nil {
		log.Fatal(err)
	}

       <- container.Done()

	stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := container.Stop(stopCtx); err != nil {
		log.Fatal(err)
	}

or less verbose if someone doesnt need full controll over the start and stop system

	container := di.New(
// options
	)

         container.Run()

Add support for di. Inject in Invoke()

Actually, the title. I've prepared an example that shows the problem:
https://play.golang.org/p/vZ2JvfmvJBB

package main

import (
	"log"

	"github.com/goava/di"
)

type Dependency struct {}

func NewDependency() *Dependency {
	return &Dependency{}
}

type Params struct {
	di.Inject

	Dependency *Dependency `di:""`
}

func InvokeWithDependencyAsParam(params Params) {
	// Some code
}

func main() {
	_, err := di.New(
		di.Provide(NewDependency),
		di.Invoke(InvokeWithDependencyAsParam),
	)
	if err != nil {
		log.Fatal(err)
	}
}

Why should I pass a pointer to the type?

Hi, I really like the library but I'm a bit of a noob and don't get why this returns False? trace prints all is registered, but I'm not sure what is going on, it also fails when I try to get that or any registered struct, sample is copied from example.

func main() {
    di.SetTracer(&di.StdTracer{})
    // create container
    c, err := di.New(
        di.Provide(NewContext),  // provide application context
        di.Provide(NewServer),   // provide http server
        di.Provide(NewServeMux), // provide http serve mux
        // controllers as []Controller group
        di.Provide(NewOrderController, di.As(new(Controller))),
        di.Provide(NewUserController, di.As(new(Controller))),
    )

    // handle container errors
    if err != nil {
        log.Fatal(err)
    }

    var server *http.Server
    fmt.Println(c.Has(server)) // returns false ???

    // invoke function
    if err := c.Invoke(StartServer); err != nil {
        log.Fatal(err)
    }
}

Thanks.

Change how Injected structs are tagged

Currently injected structs resolve using tag constraint like so:

type Parameters struct {
	di.Inject
	
	// use tag for the container to know that field need to be injected.
	Leader   *DatabaseConfig `type:"leader"`
	Follower *DatabaseConfig `type:"follower"`
}

But things start to break down if that same struct is used in other ways, for example marshalling:

type Parameters struct {
	di.Inject
	
	// use tag for the container to know that field need to be injected.
	Leader   *DatabaseConfig `type:"leader"   json:"leader"`
	Follower *DatabaseConfig `type:"follower" json:"follower"`
}

To avoid conflicting with other libs that also use tags to provide features on structs, I propose that we change how goava/di looks for struct tags that look like this instead:

type Parameters struct {
	di.Inject
	
	// use tag for the container to know that field need to be injected.
	Leader   *DatabaseConfig `di:"type=leader"`
	Follower *DatabaseConfig `di:"type=follower"`
}

container.Resolve() doesn't create signleton when dependency requested via intermidiate constructor

The title :)

I can't make Go's playground to run this code since it's using a version of the project without Container available in the dependency graph.

package main

import (
	"log"

	"github.com/goava/di"
)

type Interface interface {
	Do()
}

type Implementation1 struct{}

func (d Implementation1) Do() {}

type Implementation2 struct{}

func (d Implementation2) Do() {}

func newFactory(container *di.Container) (Interface, error) {
	// Some decision logic which implementation to choose
	var implementation *Implementation1
	err := container.Resolve(&implementation)
	if err != nil {
		return nil, err
	}

	return implementation, nil
}

func newImplementation1() *Implementation1 {
	println("called implementation 1")
	return &Implementation1{}
}

func newImplementation2() *Implementation2 {
	println("called implementation 2")
	return &Implementation2{}
}

func main() {
	container, err := di.New(
		di.Provide(newFactory),
		di.Provide(newImplementation1),
		di.Provide(newImplementation2),
	)
	if err != nil {
		log.Fatal(err)
	}

	var implementationByFactory Interface
	err = container.Resolve(&implementationByFactory)
	if err != nil {
		log.Fatal(err)
	}

	var directImplementation *Implementation1
	err = container.Resolve(&directImplementation)
	if err != nil {
		log.Fatal(err)
	}
}

Output:

called implementation 1
called implementation 1

But expected only one call.

Improve tracing

Tracing is very simple. Need to show more information about interface and group resolving.

Allow decorating of services in the container

I started to try to implement this but I dont really understand how the internals are working right now. My Idea was to add another Option to di which is called Decorate.

package main

import (
	"fmt"

	"github.com/goava/di"
)

func main() {
	container, err := di.New(
		di.Provide(NewExample),
	)
	if err != nil {
		panic(err)
	}
	
	container.Invoke(func(e Example) {
		fmt.Println(e) // Will print "Example"
	})

       err = container.Decorate(DecorateExample)
       if err != nil {
		panic(err)
	}

	container.Invoke(func(e Example) {
		fmt.Println(e) // Will print "Example is now Decorated"
	})
}

type Example string

func NewExample() Example {
	return "Example"
}

func DecorateExample(e Example) Example {
	return e + " is now Decorated"
}

This can also be expaned to have options like priority to allow ordered decorations. Of course this only works when the decorated type isnt used before decoration. So you would have to call Decorate before Invoking anything.

Support the ability to chain containers (to form scopes)

It would be nice if you could auto resolve from another container. Example:

	type Message string
	parent, _ := di.New(di.ProvideValue(Message("Hello from parent")))

	child1, _ := di.New(di.Chain(parent))
	child1.Invoke(func(m Message) { fmt.Println(m) })

This would let us have common injection providers in a parent, but still have some child Containers that can inject some variations. Some DI systems call these DI scopes I think.

Overriding a value from a parent di container

Is it possible to override a value that is already defined in a parent container?

For example, let's say I want to override the Logger for a specific DI container like this

type Logger interface {
}

type Logger1 struct{}
type Logger2 struct{}

func main() {
	root, err := di.New(
		di.ProvideValue(Logger1{}, di.As(new(Logger))),
	)
	if err != nil {
		panic(err)
	}

	child, err := di.New(
		di.ProvideValue(Logger2{}, di.As(new(Logger))),
	)
	if err != nil {
		panic(err)
	}

	err = child.AddParent(root)
	if err != nil {
		panic(err)
	}

	var lg Logger
	err = child.Resolve(&lg)
	if err != nil {
		panic(err)
	}
}

I get

 multiple definitions of main.Logger, maybe you need to use group type: []main.Logger

concurrent map read and map write

When calling "Invoke" function in multiple goroutine, it can lead to concurrent read and map write in this part of the code :

// check cycle verified
if !c.verified[param.Key()] {
	err := checkCycles(c, param)
	if err != nil {
		return err
	}
	c.verified[param.Key()] = true
}

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.