Giter Club home page Giter Club logo

go-inject's People

Contributors

andrewtar avatar monnoroch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

go-inject's Issues

Add ability to generate anonymous unique annotations

This is used to override annotations to avoid duplication of providers.
For example, some module(waitModule) extends the another provider of another module(serverModule), performing waiting, but keeping the exposed provider signature.
And is also necessary to allow to create several instances of this module in the one injector.

In the following example, NextAnonimousAnnotatation () generates an anonymous unique type, that used in private scope of the waitModule module.

type server struct {}
type serverModule struct {
    endpoint string
}
func (self serverModule) ProvideEndpoint() (string, server) {
    return self.endpoint, server{}
}

type readyServer struct {}
type waitModule struct {}
func (_ waitModule) ProvideValue(endpoint string, _ server) (string, readyServer, error) {
    return endpoint, readyServer{}, waitEndpoint(endpoint)
}
func ModuleWithReadyServer(annotation inject.Annotation) inject.Module {
    privateServerAnnotation := NextAnonimousAnnotatation()
    return inject.CombineModules(
        rewrite.RewriteAnnotations(serverModule{}, map[inject.Annotation]inject.Annotation{
            server{}: privateServerAnnotation,
        }),
        rewrite.RewriteAnnotations(waitModule{}, map[inject.Annotation]inject.Annotation{
            server{}: privateServerAnnotation,
            readyServer{}: annotation,
        }),
}

type server1 struct {}
type server2 struct {}
injector, err := inject.InjectorOf(
    ModuleWithReadyServer(server1{endpoint:"server1.com"}),
    ModuleWithReadyServer(server2{endpoint:"server1.com"}),
)
endpoint1 := injector.MustGet(new(string), server1{}).(string)
endpoint2 := injector.MustGet(new(string), server2{}).(string)

Injecting "all values of this type"

We would like to define multiple providers of the same type with different annotations and then inject them all.

For example, consider a Checker interface for checking for a condition:

package checker

type Checker interface {
  Check() bool
}

type funcChecker struct {
  checker func() bool
}

func NewChecker(checker func() bool) Checker {
  return funcChecker{checker}
}

One could install multiple modules, that potentially don't know about each other:

package p1

func (self module1) ProvideChecker() (checker.Checker, annotation1) {
  return checker.NewChecker(func() bool { return true }), annotation1{}
}

...

package p2

func (self module2) ProvideChecker() (checker.Checker, annotation2) {
  return checker.NewChecker(func() bool { return false }), annotation2{}
}

We want it to be possible to inject all installed checkers as an array:

package p3

type Component {
  checkers []checker.Checker
}

func (self module) ProvideComponent(
    checkers []checker.Checker, // doesn't work -- no annotation
) (Component, annotation3) {
  return Component{checkers}, annotation3{}
}

This is usually needed for framework-type libraries that want, for example, gather all server dependencies in a list and check that they all are ready before starting the server itself.

I propose creating an annotation inject.All that would allow depending on the array of all values of the same type, like so:

func (self module) ProvideComponent(
    checkers []checker.Checker, inject.All,
) (Component, annotation3) {
  return Component{checkers}, annotation3{}
}

No additional providers are needed, the injector knows about this special annotation and generates an appropriate provider that loops through all available keys with the required type, instantiates their values and injects the array with all of them.

Alternative design

As an alternative that can be implemented without changing the core, we could create a listinject.ListModule module with dynamic providers that would be able to like so:

injector, _ :=  inject.InjectorOf(
  module1{},
  module2{},
  listinject.ListModule().
      ForType(new(checker.Checker)).
      WithAnnotations(annotation1{}, annotation2{}).
      WithOutputAnnotation(annotation3{}),
)

And then inject the list as:

func (self module) ProvideComponent(
    checkers []checker.Checker, annotation3,
) (Component, annotation3) {
  return Component{checkers}, annotation3{}
}

Pros:

  • No need to change the core of the library
  • Explicit is better than implicit

Cons:

  • A list of annotations has to be hard coded, which is error-prone when you truly want ALL (dependencies)

Auto injecting struct fields

Goal:

type injectedAnnotation struct{}

type grpcClientModule struct {
    annotation inject.Annotation
}

func (self testInjectedAnnotationsModule) ProvideClient(
    endpoint string, _ injectedAnnotation,
) (int, injectedAnnotation) {
    return grpc.NewClientWithEndpoint(endpoint), injectedAnnotation{}
}

func (self testInjectedAnnotationsModule) ProvideAnnotation() (inject.Annotation, injectedAnnotation) {
    return self.annotation, injectedAnnotation{}
}

type Frontend struct {}
type Auth struct {}

type endpointsModule struct {}

func (self endpointsModule) ProvideFrontendEndpoint() (string, Frontend) {
    return "frontend:80", Frontend{}
}

func (self endpointsModule) ProvideAuthEndpoint() (string, Auth) {
    return "auth:80", Auth{}
}

type MyServer struct {
  AuthClient grpc.Client (@Auth) // these annotations are pseudo-code
  FrontendClient grpc.Client (@Frontend)
}

func main() {
    injector = inject.InjectorOf(
        grpcClientModule{
            annotation: Frontend{},
        }, grpcClientModule{
            annotation: Auth{},
        }, endpointsModule{},
        inject.AutoInjectModule(new(MyServer)),
    )
    // Тote that we never wrote the MyServer provider manually.
    myServer := injector.MustGet(new(MyServer), inject.AutoInject{}).(MyServer)
}

The core problem is the annotation syntax.

Refine calling lazy providers in run-time

Right now this makes everything fail without an error message if a provider panics. Either:

  • Throw a nice error that this is forbidden, or
  • Support it

The first option is safer for forward-compatibility, so let's do that real quick, but ultimately this need to be thought through.

Replace dynamic annotations with dynamic provider modules

Dynamic annotations are a poor idea:

  • they are complicated
  • annotation providers are very similar to regular providers, but not quite, so they are confusing
  • they only solve one specific use case

We should replace them with a more generic feature that will also allow implementing auto injection modules and other user-defined extensions: dynamic provider modules. Design TBD.

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.