diff --git a/.travis.yml b/.travis.yml index 7f09f65..b8e40fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: go go: - - 1.6 + - "1.10" before_install: - - go get -v github.com/golang/lint/golint + - go get -v golang.org/x/lint/golint - go get -v golang.org/x/tools/cmd/cover install: @@ -16,4 +16,4 @@ script: - go vet inject.go - $HOME/gopath/bin/golint inject.go - go test -cpu=2 -race -v ./... - - go test -cpu=2 -covermode=atomic ./... + - go test -cpu=2 -covermode=atomic ./... \ No newline at end of file diff --git a/common_test.go b/common_test.go index bc5bf7b..3787383 100644 --- a/common_test.go +++ b/common_test.go @@ -1,4 +1,4 @@ -package inject_test +package goject_test import "testing" diff --git a/example_test.go b/example_test.go index df436d0..3ff574e 100644 --- a/example_test.go +++ b/example_test.go @@ -1,11 +1,11 @@ -package inject_test +package goject_test import ( "fmt" "net/http" "os" - "github.com/facebookgo/inject" + "github.com/imaramos/goject" ) // Our Awesome Application renders a message using two APIs in our fake @@ -31,7 +31,7 @@ func (a *HomePlanetRenderApp) Render(id uint64) string { type NameAPI struct { // Here and below in PlanetAPI we add the tag to an interface value. // This value cannot automatically be created (by definition) and - // hence must be explicitly provided to the graph. + // hence must be explicitly provided to the Container. HTTPTransport http.RoundTripper `inject:""` } @@ -52,11 +52,11 @@ func (p *PlanetAPI) Planet(id uint64) string { } func Example() { - // Typically an application will have exactly one object graph, and + // Typically an application will have exactly one object Container, and // you will create it and use it within a main function: - var g inject.Graph + var g goject.Container - // We provide our graph two "seed" objects, one our empty + // We provide our Container two "seed" objects, one our empty // HomePlanetRenderApp instance which we're hoping to get filled out, // and second our DefaultTransport to satisfy our HTTPTransport // dependency. We have to provide the DefaultTransport because the @@ -66,8 +66,8 @@ func Example() { // the dependency since it implements the interface: var a HomePlanetRenderApp err := g.Provide( - &inject.Object{Value: &a}, - &inject.Object{Value: http.DefaultTransport}, + &goject.Object{Value: &a}, + &goject.Object{Value: http.DefaultTransport}, ) if err != nil { fmt.Fprintln(os.Stderr, err) @@ -83,9 +83,9 @@ func Example() { } // There is a shorthand API for the simple case which combines the - // three calls above is available as inject.Populate: + // three calls above is available as goject.Populate: // - // inject.Populate(&a, http.DefaultTransport) + // goject.Populate(&a, http.DefaultTransport) // // The above API shows the underlying API which also allows the use of // named instances for more complex scenarios. diff --git a/inject.go b/goject.go similarity index 86% rename from inject.go rename to goject.go index 300b9a3..d5a595b 100644 --- a/inject.go +++ b/goject.go @@ -1,8 +1,8 @@ -// Package inject provides a reflect based injector. A large application built +// package goject provides a reflect based injector. A large application built // with dependency injection in mind will typically involve the boring work of -// setting up the object graph. This library attempts to take care of this +// setting up the object Container. This library attempts to take care of this // boring work by creating and connecting the various objects. Its use involves -// you seeding the object graph with some (possibly incomplete) objects, where +// you seeding the object Container with some (possibly incomplete) objects, where // the underlying types have been tagged for injection. Given this, the // library will populate the objects creating new ones as necessary. It uses // singletons by default, supports optional private instances as well as named @@ -23,10 +23,11 @@ // of the associated type. The second triggers creation of a private instance // for the associated type. Finally the last form is asking for a named // dependency called "dev logger". -package inject +package goject import ( "bytes" + "errors" "fmt" "math/rand" "reflect" @@ -35,15 +36,15 @@ import ( ) // Logger allows for simple logging as inject traverses and populates the -// object graph. +// object Container. type Logger interface { Debugf(format string, v ...interface{}) } -// Populate is a short-hand for populating a graph with the given incomplete +// Populate is a short-hand for populating a Container with the given incomplete // object values. func Populate(values ...interface{}) error { - var g Graph + var g Container for _, v := range values { if err := g.Provide(&Object{Value: v}); err != nil { return err @@ -52,7 +53,7 @@ func Populate(values ...interface{}) error { return g.Populate() } -// An Object in the Graph. +// An Object in the Container. type Object struct { Value interface{} Name string // Optional @@ -82,17 +83,17 @@ func (o *Object) addDep(field string, dep *Object) { o.Fields[field] = dep } -// The Graph of Objects. -type Graph struct { +// The Container of Objects. +type Container struct { Logger Logger // Optional, will trigger debug logging. unnamed []*Object unnamedType map[reflect.Type]bool named map[string]*Object } -// Provide objects to the Graph. The Object documentation describes +// Provide objects to the Container. The Object documentation describes // the impact of various fields. -func (g *Graph) Provide(objects ...*Object) error { +func (g *Container) Provide(objects ...*Object) error { for _, o := range objects { o.reflectType = reflect.TypeOf(o.Value) o.reflectValue = reflect.ValueOf(o.Value) @@ -153,7 +154,7 @@ func (g *Graph) Provide(objects ...*Object) error { } // Populate the incomplete Objects. -func (g *Graph) Populate() error { +func (g *Container) Populate() error { for _, o := range g.named { if o.Complete { continue @@ -165,7 +166,7 @@ func (g *Graph) Populate() error { } // We append and modify our slice as we go along, so we don't use a standard - // range loop, and do a single pass thru each object in our graph. + // range loop, and do a single pass thru each object in our Container. i := 0 for { if i == len(g.unnamed) { @@ -209,7 +210,41 @@ func (g *Graph) Populate() error { return nil } -func (g *Graph) populateExplicit(o *Object) error { +// Resolve sets the value of dst to an assignable instance +func (g *Container) Resolve(dst interface{}) error { + return g.resolve(dst, g.unnamed...) +} + +// ResolveByName sets the value of dst to an assignable instance with the provided name +func (g *Container) ResolveByName(dst interface{}, name string) error { + object, ok := g.named[name] + + if !ok { + return fmt.Errorf("No provided object with the name: %s", name) + } + + return g.resolve(dst, object) +} + +func (g *Container) resolve(dst interface{}, objects ...*Object) error { + dstPtrValue := reflect.ValueOf(dst) + + if !isPointer(dstPtrValue) { + return errors.New("dst its not a pointer") + } + + for _, object := range objects { + objectValue := reflect.ValueOf(object.Value) + + if copyValueIfAssignable(objectValue, dstPtrValue) { + return nil + } + } + + return errors.New("No provided object is assignable to dst") +} + +func (g *Container) populateExplicit(o *Object) error { // Ignore named value types. if o.Name != "" && !isStructPtr(o.reflectType) { return nil @@ -411,7 +446,7 @@ StructLoop: return nil } -func (g *Graph) populateUnnamedInterface(o *Object) error { +func (g *Container) populateUnnamedInterface(o *Object) error { // Ignore named value types. if o.Name != "" && !isStructPtr(o.reflectType) { return nil @@ -510,7 +545,7 @@ func (g *Graph) populateUnnamedInterface(o *Object) error { // Objects returns all known objects, named as well as unnamed. The returned // elements are not in a stable order. -func (g *Graph) Objects() []*Object { +func (g *Container) Objects() []*Object { objects := make([]*Object, 0, len(g.unnamed)+len(g.named)) for _, o := range g.unnamed { if !o.embedded { @@ -566,6 +601,10 @@ func isStructPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct } +func isPointer(value reflect.Value) bool { + return value.Type().Kind() == reflect.Ptr +} + func isNilOrZero(v reflect.Value, t reflect.Type) bool { switch v.Kind() { default: @@ -574,3 +613,25 @@ func isNilOrZero(v reflect.Value, t reflect.Type) bool { return v.IsNil() } } + +func copyValueIfAssignable(src, destPtr reflect.Value) bool { + dstValue := destPtr.Elem() + + srcType := src.Type() + + switch dstValue.Kind() { + case reflect.Interface: + if srcType.AssignableTo(dstValue.Type()) { + dstValue.Set(src) + return true + } + + case reflect.Struct: + if srcType.AssignableTo(destPtr.Type()) { + dstValue.Set(src.Elem()) + return true + } + } + + return false +} diff --git a/inject_test.go b/goject_test.go similarity index 65% rename from inject_test.go rename to goject_test.go index 6433eed..ee8f2db 100644 --- a/inject_test.go +++ b/goject_test.go @@ -1,4 +1,4 @@ -package inject_test +package goject_test import ( "fmt" @@ -8,14 +8,14 @@ import ( "time" "github.com/facebookgo/ensure" - "github.com/facebookgo/inject" + "github.com/imaramos/goject" - injecttesta "github.com/facebookgo/inject/injecttesta" - injecttestb "github.com/facebookgo/inject/injecttestb" + injecttesta "github.com/imaramos/goject/injecttesta" + injecttestb "github.com/imaramos/goject/injecttestb" ) func init() { - // we rely on math.Rand in Graph.Objects() and this gives it some randomness. + // we rely on math.Rand in Container.Objects() and this gives it some randomness. rand.Seed(time.Now().UnixNano()) } @@ -46,7 +46,7 @@ func TestRequireTag(t *testing.T) { B *TypeNestedStruct `inject:""` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A != nil { @@ -63,12 +63,12 @@ type TypeWithNonPointerInject struct { func TestErrorOnNonPointerInject(t *testing.T) { var a TypeWithNonPointerInject - err := inject.Populate(&a) + err := goject.Populate(&a) if err == nil { t.Fatalf("expected error for %+v", a) } - const msg = "found inject tag on unsupported field A in type *inject_test.TypeWithNonPointerInject" + const msg = "found inject tag on unsupported field A in type *goject_test.TypeWithNonPointerInject" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -80,12 +80,12 @@ type TypeWithNonPointerStructInject struct { func TestErrorOnNonPointerStructInject(t *testing.T) { var a TypeWithNonPointerStructInject - err := inject.Populate(&a) + err := goject.Populate(&a) if err == nil { t.Fatalf("expected error for %+v", a) } - const msg = "found inject tag on unsupported field A in type *inject_test.TypeWithNonPointerStructInject" + const msg = "found inject tag on unsupported field A in type *goject_test.TypeWithNonPointerStructInject" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -97,7 +97,7 @@ func TestInjectSimple(t *testing.T) { B *TypeNestedStruct `inject:""` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A == nil { @@ -121,7 +121,7 @@ func TestDoesNotOverwrite(t *testing.T) { B *TypeNestedStruct `inject:""` } v.A = a - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A != a { @@ -138,7 +138,7 @@ func TestPrivate(t *testing.T) { B *TypeNestedStruct `inject:""` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A == nil { @@ -161,12 +161,12 @@ type TypeWithJustColon struct { func TestTagWithJustColon(t *testing.T) { var a TypeWithJustColon - err := inject.Populate(&a) + err := goject.Populate(&a) if err == nil { t.Fatalf("expected error for %+v", a) } - const msg = "unexpected tag format `inject:` for field A in type *inject_test.TypeWithJustColon" + const msg = "unexpected tag format `inject:` for field A in type *goject_test.TypeWithJustColon" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -178,29 +178,29 @@ type TypeWithOpenQuote struct { func TestTagWithOpenQuote(t *testing.T) { var a TypeWithOpenQuote - err := inject.Populate(&a) + err := goject.Populate(&a) if err == nil { t.Fatalf("expected error for %+v", a) } - const msg = "unexpected tag format `inject:\"` for field A in type *inject_test.TypeWithOpenQuote" + const msg = "unexpected tag format `inject:\"` for field A in type *goject_test.TypeWithOpenQuote" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } } func TestProvideWithFields(t *testing.T) { - var g inject.Graph + var g goject.Container a := &TypeAnswerStruct{} - err := g.Provide(&inject.Object{Value: a, Fields: map[string]*inject.Object{}}) + err := g.Provide(&goject.Object{Value: a, Fields: map[string]*goject.Object{}}) ensure.NotNil(t, err) - ensure.DeepEqual(t, err.Error(), "fields were specified on object *inject_test.TypeAnswerStruct when it was provided") + ensure.DeepEqual(t, err.Error(), "fields were specified on object *goject_test.TypeAnswerStruct when it was provided") } func TestProvideNonPointer(t *testing.T) { - var g inject.Graph + var g goject.Container var i int - err := g.Provide(&inject.Object{Value: i}) + err := g.Provide(&goject.Object{Value: i}) if err == nil { t.Fatal("expected error") } @@ -212,9 +212,9 @@ func TestProvideNonPointer(t *testing.T) { } func TestProvideNonPointerStruct(t *testing.T) { - var g inject.Graph + var g goject.Container var i *int - err := g.Provide(&inject.Object{Value: i}) + err := g.Provide(&goject.Object{Value: i}) if err == nil { t.Fatal("expected error") } @@ -226,19 +226,19 @@ func TestProvideNonPointerStruct(t *testing.T) { } func TestProvideTwoOfTheSame(t *testing.T) { - var g inject.Graph + var g goject.Container a := TypeAnswerStruct{} - err := g.Provide(&inject.Object{Value: &a}) + err := g.Provide(&goject.Object{Value: &a}) if err != nil { t.Fatal(err) } - err = g.Provide(&inject.Object{Value: &a}) + err = g.Provide(&goject.Object{Value: &a}) if err == nil { t.Fatal("expected error") } - const msg = "provided two unnamed instances of type *github.com/facebookgo/inject_test.TypeAnswerStruct" + const msg = "provided two unnamed instances of type *github.com/imaramos/goject_test.TypeAnswerStruct" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -246,27 +246,27 @@ func TestProvideTwoOfTheSame(t *testing.T) { func TestProvideTwoOfTheSameWithPopulate(t *testing.T) { a := TypeAnswerStruct{} - err := inject.Populate(&a, &a) + err := goject.Populate(&a, &a) if err == nil { t.Fatal("expected error") } - const msg = "provided two unnamed instances of type *github.com/facebookgo/inject_test.TypeAnswerStruct" + const msg = "provided two unnamed instances of type *github.com/imaramos/goject_test.TypeAnswerStruct" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } } func TestProvideTwoWithTheSameName(t *testing.T) { - var g inject.Graph + var g goject.Container const name = "foo" a := TypeAnswerStruct{} - err := g.Provide(&inject.Object{Value: &a, Name: name}) + err := g.Provide(&goject.Object{Value: &a, Name: name}) if err != nil { t.Fatal(err) } - err = g.Provide(&inject.Object{Value: &a, Name: name}) + err = g.Provide(&goject.Object{Value: &a, Name: name}) if err == nil { t.Fatal("expected error") } @@ -278,16 +278,16 @@ func TestProvideTwoWithTheSameName(t *testing.T) { } func TestNamedInstanceWithDependencies(t *testing.T) { - var g inject.Graph + var g goject.Container a := &TypeNestedStruct{} - if err := g.Provide(&inject.Object{Value: a, Name: "foo"}); err != nil { + if err := g.Provide(&goject.Object{Value: a, Name: "foo"}); err != nil { t.Fatal(err) } var c struct { A *TypeNestedStruct `inject:"foo"` } - if err := g.Provide(&inject.Object{Value: &c}); err != nil { + if err := g.Provide(&goject.Object{Value: &c}); err != nil { t.Fatal(err) } @@ -301,14 +301,14 @@ func TestNamedInstanceWithDependencies(t *testing.T) { } func TestTwoNamedInstances(t *testing.T) { - var g inject.Graph + var g goject.Container a := &TypeAnswerStruct{} b := &TypeAnswerStruct{} - if err := g.Provide(&inject.Object{Value: a, Name: "foo"}); err != nil { + if err := g.Provide(&goject.Object{Value: a, Name: "foo"}); err != nil { t.Fatal(err) } - if err := g.Provide(&inject.Object{Value: b, Name: "bar"}); err != nil { + if err := g.Provide(&goject.Object{Value: b, Name: "bar"}); err != nil { t.Fatal(err) } @@ -316,7 +316,7 @@ func TestTwoNamedInstances(t *testing.T) { A *TypeAnswerStruct `inject:"foo"` B *TypeAnswerStruct `inject:"bar"` } - if err := g.Provide(&inject.Object{Value: &c}); err != nil { + if err := g.Provide(&goject.Object{Value: &c}); err != nil { t.Fatal(err) } @@ -338,23 +338,23 @@ type TypeWithMissingNamed struct { func TestTagWithMissingNamed(t *testing.T) { var a TypeWithMissingNamed - err := inject.Populate(&a) + err := goject.Populate(&a) if err == nil { t.Fatalf("expected error for %+v", a) } - const msg = "did not find object named foo required by field A in type *inject_test.TypeWithMissingNamed" + const msg = "did not find object named foo required by field A in type *goject_test.TypeWithMissingNamed" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } } func TestCompleteProvides(t *testing.T) { - var g inject.Graph + var g goject.Container var v struct { A *TypeAnswerStruct `inject:""` } - if err := g.Provide(&inject.Object{Value: &v, Complete: true}); err != nil { + if err := g.Provide(&goject.Object{Value: &v, Complete: true}); err != nil { t.Fatal(err) } @@ -367,11 +367,11 @@ func TestCompleteProvides(t *testing.T) { } func TestCompleteNamedProvides(t *testing.T) { - var g inject.Graph + var g goject.Container var v struct { A *TypeAnswerStruct `inject:""` } - if err := g.Provide(&inject.Object{Value: &v, Complete: true, Name: "foo"}); err != nil { + if err := g.Provide(&goject.Object{Value: &v, Complete: true, Name: "foo"}); err != nil { t.Fatal(err) } @@ -389,12 +389,12 @@ type TypeInjectInterfaceMissing struct { func TestInjectInterfaceMissing(t *testing.T) { var v TypeInjectInterfaceMissing - err := inject.Populate(&v) + err := goject.Populate(&v) if err == nil { t.Fatal("did not find expected error") } - const msg = "found no assignable value for field Answerable in type *inject_test.TypeInjectInterfaceMissing" + const msg = "found no assignable value for field Answerable in type *goject_test.TypeInjectInterfaceMissing" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -407,7 +407,7 @@ type TypeInjectInterface struct { func TestInjectInterface(t *testing.T) { var v TypeInjectInterface - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.Answerable == nil || v.Answerable != v.A { @@ -426,14 +426,14 @@ type TypeWithInvalidNamedType struct { } func TestInvalidNamedInstanceType(t *testing.T) { - var g inject.Graph + var g goject.Container a := &TypeAnswerStruct{} - if err := g.Provide(&inject.Object{Value: a, Name: "foo"}); err != nil { + if err := g.Provide(&goject.Object{Value: a, Name: "foo"}); err != nil { t.Fatal(err) } var c TypeWithInvalidNamedType - if err := g.Provide(&inject.Object{Value: &c}); err != nil { + if err := g.Provide(&goject.Object{Value: &c}); err != nil { t.Fatal(err) } @@ -442,7 +442,7 @@ func TestInvalidNamedInstanceType(t *testing.T) { t.Fatal("did not find expected error") } - const msg = "object named foo of type *inject_test.TypeNestedStruct is not assignable to field A (*inject_test.TypeAnswerStruct) in type *inject_test.TypeWithInvalidNamedType" + const msg = "object named foo of type *goject_test.TypeNestedStruct is not assignable to field A (*goject_test.TypeAnswerStruct) in type *goject_test.TypeWithInvalidNamedType" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -454,12 +454,12 @@ type TypeWithInjectOnPrivateField struct { func TestInjectOnPrivateField(t *testing.T) { var a TypeWithInjectOnPrivateField - err := inject.Populate(&a) + err := goject.Populate(&a) if err == nil { t.Fatal("did not find expected error") } - const msg = "inject requested on unexported field a in type *inject_test.TypeWithInjectOnPrivateField" + const msg = "inject requested on unexported field a in type *goject_test.TypeWithInjectOnPrivateField" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -471,12 +471,12 @@ type TypeWithInjectOnPrivateInterfaceField struct { func TestInjectOnPrivateInterfaceField(t *testing.T) { var a TypeWithInjectOnPrivateField - err := inject.Populate(&a) + err := goject.Populate(&a) if err == nil { t.Fatal("did not find expected error") } - const msg = "inject requested on unexported field a in type *inject_test.TypeWithInjectOnPrivateField" + const msg = "inject requested on unexported field a in type *goject_test.TypeWithInjectOnPrivateField" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -489,12 +489,12 @@ type TypeInjectPrivateInterface struct { func TestInjectPrivateInterface(t *testing.T) { var v TypeInjectPrivateInterface - err := inject.Populate(&v) + err := goject.Populate(&v) if err == nil { t.Fatal("did not find expected error") } - const msg = "found private inject tag on interface field Answerable in type *inject_test.TypeInjectPrivateInterface" + const msg = "found private inject tag on interface field Answerable in type *goject_test.TypeInjectPrivateInterface" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -508,12 +508,12 @@ type TypeInjectTwoSatisfyInterface struct { func TestInjectTwoSatisfyInterface(t *testing.T) { var v TypeInjectTwoSatisfyInterface - err := inject.Populate(&v) + err := goject.Populate(&v) if err == nil { t.Fatal("did not find expected error") } - const msg = "found two assignable values for field Answerable in type *inject_test.TypeInjectTwoSatisfyInterface. one type *inject_test.TypeAnswerStruct with value &{0 0} and another type *inject_test.TypeNestedStruct with value" + const msg = "found two assignable values for field Answerable in type *goject_test.TypeInjectTwoSatisfyInterface. one type *goject_test.TypeAnswerStruct with value &{0 0} and another type *goject_test.TypeNestedStruct with value" if !strings.HasPrefix(err.Error(), msg) { t.Fatalf("expected prefix:\n%s\nactual:\n%s", msg, err.Error()) } @@ -526,9 +526,9 @@ type TypeInjectNamedTwoSatisfyInterface struct { } func TestInjectNamedTwoSatisfyInterface(t *testing.T) { - var g inject.Graph + var g goject.Container var v TypeInjectNamedTwoSatisfyInterface - if err := g.Provide(&inject.Object{Name: "foo", Value: &v}); err != nil { + if err := g.Provide(&goject.Object{Name: "foo", Value: &v}); err != nil { t.Fatal(err) } @@ -537,7 +537,7 @@ func TestInjectNamedTwoSatisfyInterface(t *testing.T) { t.Fatal("was expecting error") } - const msg = "found two assignable values for field Answerable in type *inject_test.TypeInjectNamedTwoSatisfyInterface. one type *inject_test.TypeAnswerStruct with value &{0 0} and another type *inject_test.TypeNestedStruct with value" + const msg = "found two assignable values for field Answerable in type *goject_test.TypeInjectNamedTwoSatisfyInterface. one type *goject_test.TypeAnswerStruct with value &{0 0} and another type *goject_test.TypeNestedStruct with value" if !strings.HasPrefix(err.Error(), msg) { t.Fatalf("expected prefix:\n%s\nactual:\n%s", msg, err.Error()) } @@ -548,9 +548,9 @@ type TypeWithInjectNamedOnPrivateInterfaceField struct { } func TestInjectNamedOnPrivateInterfaceField(t *testing.T) { - var g inject.Graph + var g goject.Container var v TypeWithInjectNamedOnPrivateInterfaceField - if err := g.Provide(&inject.Object{Name: "foo", Value: &v}); err != nil { + if err := g.Provide(&goject.Object{Name: "foo", Value: &v}); err != nil { t.Fatal(err) } @@ -559,7 +559,7 @@ func TestInjectNamedOnPrivateInterfaceField(t *testing.T) { t.Fatal("was expecting error") } - const msg = "inject requested on unexported field a in type *inject_test.TypeWithInjectNamedOnPrivateInterfaceField" + const msg = "inject requested on unexported field a in type *goject_test.TypeWithInjectNamedOnPrivateInterfaceField" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -570,13 +570,13 @@ type TypeWithNonPointerNamedInject struct { } func TestErrorOnNonPointerNamedInject(t *testing.T) { - var g inject.Graph - if err := g.Provide(&inject.Object{Name: "foo", Value: 42}); err != nil { + var g goject.Container + if err := g.Provide(&goject.Object{Name: "foo", Value: 42}); err != nil { t.Fatal(err) } var v TypeWithNonPointerNamedInject - if err := g.Provide(&inject.Object{Value: &v}); err != nil { + if err := g.Provide(&goject.Object{Value: &v}); err != nil { t.Fatal(err) } @@ -597,7 +597,7 @@ func TestInjectInline(t *testing.T) { } `inject:"inline"` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.Inline.A == nil { @@ -622,7 +622,7 @@ func TestInjectInlineOnPointer(t *testing.T) { } `inject:""` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.Inline.A == nil { @@ -644,12 +644,12 @@ func TestInjectInvalidInline(t *testing.T) { A *TypeAnswerStruct `inject:"inline"` } - err := inject.Populate(&v) + err := goject.Populate(&v) if err == nil { t.Fatal("was expecting an error") } - const msg = `inline requested on non inlined field A in type *struct { A *inject_test.TypeAnswerStruct "inject:\"inline\"" }` + const msg = `inline requested on non inlined field A in type *struct { A *goject_test.TypeAnswerStruct "inject:\"inline\"" }` if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -662,12 +662,12 @@ func TestInjectInlineMissing(t *testing.T) { } `inject:""` } - err := inject.Populate(&v) + err := goject.Populate(&v) if err == nil { t.Fatal("was expecting an error") } - const msg = `inline struct on field Inline in type *struct { Inline struct { B *inject_test.TypeNestedStruct "inject:\"\"" } "inject:\"\"" } requires an explicit "inline" tag` + const msg = `inline struct on field Inline in type *struct { Inline struct { B *goject_test.TypeNestedStruct "inject:\"\"" } "inject:\"\"" } requires an explicit "inline" tag` if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -682,12 +682,12 @@ type TypeWithInlineStructWithPrivate struct { func TestInjectInlinePrivate(t *testing.T) { var v TypeWithInlineStructWithPrivate - err := inject.Populate(&v) + err := goject.Populate(&v) if err == nil { t.Fatal("was expecting an error") } - const msg = "cannot use private inject on inline struct on field Inline in type *inject_test.TypeWithInlineStructWithPrivate" + const msg = "cannot use private inject on inline struct on field Inline in type *goject_test.TypeWithInlineStructWithPrivate" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -699,7 +699,7 @@ type TypeWithStructValue struct { func TestInjectWithStructValue(t *testing.T) { var v TypeWithStructValue - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.Inline.A == nil { @@ -713,8 +713,8 @@ type TypeWithNonpointerStructValue struct { func TestInjectWithNonpointerStructValue(t *testing.T) { var v TypeWithNonpointerStructValue - var g inject.Graph - if err := g.Provide(&inject.Object{Value: &v}); err != nil { + var g goject.Container + if err := g.Provide(&goject.Object{Value: &v}); err != nil { t.Fatal(err) } if err := g.Populate(); err != nil { @@ -725,7 +725,7 @@ func TestInjectWithNonpointerStructValue(t *testing.T) { } n := len(g.Objects()) if n != 3 { - t.Fatalf("expected 3 object in graph, got %d", n) + t.Fatalf("expected 3 object in Container, got %d", n) } } @@ -735,7 +735,7 @@ func TestPrivateIsFollowed(t *testing.T) { A *TypeNestedStruct `inject:"private"` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A.A == nil { @@ -750,7 +750,7 @@ func TestDoesNotOverwriteInterface(t *testing.T) { B *TypeNestedStruct `inject:""` } v.A = a - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A != a { @@ -767,7 +767,7 @@ func TestInterfaceIncludingPrivate(t *testing.T) { B *TypeNestedStruct `inject:"private"` C *TypeAnswerStruct `inject:""` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A == nil { @@ -791,7 +791,7 @@ func TestInjectMap(t *testing.T) { var v struct { A map[string]int `inject:"private"` } - if err := inject.Populate(&v); err != nil { + if err := goject.Populate(&v); err != nil { t.Fatal(err) } if v.A == nil { @@ -805,12 +805,12 @@ type TypeInjectWithMapWithoutPrivate struct { func TestInjectMapWithoutPrivate(t *testing.T) { var v TypeInjectWithMapWithoutPrivate - err := inject.Populate(&v) + err := goject.Populate(&v) if err == nil { t.Fatalf("expected error for %+v", v) } - const msg = "inject on map field A in type *inject_test.TypeInjectWithMapWithoutPrivate must be named or private" + const msg = "inject on map field A in type *goject_test.TypeInjectWithMapWithoutPrivate must be named or private" if err.Error() != msg { t.Fatalf("expected:\n%s\nactual:\n%s", msg, err.Error()) } @@ -822,14 +822,14 @@ type TypeForObjectString struct { } func TestObjectString(t *testing.T) { - var g inject.Graph + var g goject.Container a := &TypeNestedStruct{} - if err := g.Provide(&inject.Object{Value: a, Name: "foo"}); err != nil { + if err := g.Provide(&goject.Object{Value: a, Name: "foo"}); err != nil { t.Fatal(err) } var c TypeForObjectString - if err := g.Provide(&inject.Object{Value: &c}); err != nil { + if err := g.Provide(&goject.Object{Value: &c}); err != nil { t.Fatal(err) } @@ -843,14 +843,14 @@ func TestObjectString(t *testing.T) { } ensure.SameElements(t, actual, []string{ - "*inject_test.TypeForObjectString", - "*inject_test.TypeNestedStruct", - "*inject_test.TypeNestedStruct named foo", - "*inject_test.TypeAnswerStruct", + "*goject_test.TypeForObjectString", + "*goject_test.TypeNestedStruct", + "*goject_test.TypeNestedStruct named foo", + "*goject_test.TypeAnswerStruct", }) } -type TypeForGraphObjects struct { +type TypeForContainerObjects struct { TypeNestedStruct `inject:"inline"` A *TypeNestedStruct `inject:"foo"` E struct { @@ -858,11 +858,11 @@ type TypeForGraphObjects struct { } `inject:"inline"` } -func TestGraphObjects(t *testing.T) { - var g inject.Graph +func TestContainerObjects(t *testing.T) { + var g goject.Container err := g.Provide( - &inject.Object{Value: &TypeNestedStruct{}, Name: "foo"}, - &inject.Object{Value: &TypeForGraphObjects{}}, + &goject.Object{Value: &TypeNestedStruct{}, Name: "foo"}, + &goject.Object{Value: &TypeForContainerObjects{}}, ) ensure.Nil(t, err) ensure.Nil(t, g.Populate()) @@ -873,11 +873,11 @@ func TestGraphObjects(t *testing.T) { } ensure.SameElements(t, actual, []string{ - "*inject_test.TypeAnswerStruct", - "*inject_test.TypeForGraphObjects", - "*inject_test.TypeNestedStruct named foo", - "*inject_test.TypeNestedStruct", - `*struct { B *inject_test.TypeNestedStruct "inject:\"\"" }`, + "*goject_test.TypeAnswerStruct", + "*goject_test.TypeForContainerObjects", + "*goject_test.TypeNestedStruct named foo", + "*goject_test.TypeNestedStruct", + `*struct { B *goject_test.TypeNestedStruct "inject:\"\"" }`, }) } @@ -920,18 +920,18 @@ type TypeForLogging struct { } func TestInjectLogging(t *testing.T) { - g := inject.Graph{ + g := goject.Container{ Logger: &logger{ Expected: []string{ - "provided *inject_test.TypeForLoggingCreated named name_for_logging", - "provided *inject_test.TypeForLogging", - "provided embedded *inject_test.TypeForLoggingEmbedded", - "created *inject_test.TypeForLoggingCreated", - "assigned newly created *inject_test.TypeForLoggingCreated to field TypeForLoggingCreated in *inject_test.TypeForLogging", - "assigned existing *inject_test.TypeForLoggingCreated to field TypeForLoggingCreated in *inject_test.TypeForLoggingEmbedded", - "assigned *inject_test.TypeForLoggingCreated named name_for_logging to field TypeForLoggingCreatedNamed in *inject_test.TypeForLoggingEmbedded", - "made map for field Map in *inject_test.TypeForLoggingEmbedded", - "assigned existing *inject_test.TypeForLoggingCreated to interface field TypeForLoggingInterface in *inject_test.TypeForLoggingEmbedded", + "provided *goject_test.TypeForLoggingCreated named name_for_logging", + "provided *goject_test.TypeForLogging", + "provided embedded *goject_test.TypeForLoggingEmbedded", + "created *goject_test.TypeForLoggingCreated", + "assigned newly created *goject_test.TypeForLoggingCreated to field TypeForLoggingCreated in *goject_test.TypeForLogging", + "assigned existing *goject_test.TypeForLoggingCreated to field TypeForLoggingCreated in *goject_test.TypeForLoggingEmbedded", + "assigned *goject_test.TypeForLoggingCreated named name_for_logging to field TypeForLoggingCreatedNamed in *goject_test.TypeForLoggingEmbedded", + "made map for field Map in *goject_test.TypeForLoggingEmbedded", + "assigned existing *goject_test.TypeForLoggingCreated to interface field TypeForLoggingInterface in *goject_test.TypeForLoggingEmbedded", }, T: t, }, @@ -939,8 +939,8 @@ func TestInjectLogging(t *testing.T) { var v TypeForLogging err := g.Provide( - &inject.Object{Value: &TypeForLoggingCreated{}, Name: "name_for_logging"}, - &inject.Object{Value: &v}, + &goject.Object{Value: &TypeForLoggingCreated{}, Name: "name_for_logging"}, + &goject.Object{Value: &v}, ) if err != nil { t.Fatal(err) @@ -961,11 +961,11 @@ type TypeForNamedWithUnnamed struct { } func TestForNamedWithUnnamed(t *testing.T) { - var g inject.Graph + var g goject.Container var v TypeForNamedWithUnnamed err := g.Provide( - &inject.Object{Value: &v, Name: "foo"}, + &goject.Object{Value: &v, Name: "foo"}, ) if err != nil { t.Fatal(err) @@ -982,10 +982,10 @@ func TestForNamedWithUnnamed(t *testing.T) { } func TestForSameNameButDifferentPackage(t *testing.T) { - var g inject.Graph + var g goject.Container err := g.Provide( - &inject.Object{Value: &injecttesta.Foo{}}, - &inject.Object{Value: &injecttestb.Foo{}}, + &goject.Object{Value: &injecttesta.Foo{}}, + &goject.Object{Value: &injecttestb.Foo{}}, ) if err != nil { t.Fatal(err) @@ -994,3 +994,125 @@ func TestForSameNameButDifferentPackage(t *testing.T) { t.Fatal(err) } } + +func TestResolveWithInterfaceVariable(t *testing.T) { + var g goject.Container + a := &TypeAnswerStruct{answer: 1, private: 2} + g.Provide(&goject.Object{Value: a}) + + var result Answerable + + err := g.Resolve(&result) + + ensure.Nil(t, err) + ensure.DeepEqual(t, result, a) +} + +func TestResolveWithStructVariable(t *testing.T) { + var g goject.Container + a := &TypeAnswerStruct{answer: 1, private: 2} + g.Provide(&goject.Object{Value: a}) + + var result TypeAnswerStruct + + err := g.Resolve(&result) + + ensure.Nil(t, err) + ensure.DeepEqual(t, &result, a) +} + +func TestResolveNoProvidedInstanceOfSameType(t *testing.T) { + var g goject.Container + l := &logger{} + g.Provide(&goject.Object{Value: l}) + + var result TypeAnswerStruct + + err := g.Resolve(&result) + + ensure.NotNil(t, err) + ensure.DeepEqual(t, err.Error(), "No provided object is assignable to dst") +} + +func TestResolveWithNoPointer(t *testing.T) { + var g goject.Container + a := &TypeAnswerStruct{answer: 1, private: 2} + g.Provide(&goject.Object{Value: a}) + + var result TypeAnswerStruct + + err := g.Resolve(result) + + ensure.NotNil(t, err) + ensure.DeepEqual(t, err.Error(), "dst its not a pointer") +} + +func TestResolveByNameWithInterfaceVariable(t *testing.T) { + var g goject.Container + a := &TypeAnswerStruct{answer: 1, private: 2} + const name = "name" + g.Provide(&goject.Object{Name: name, Value: a}) + + var result Answerable + + err := g.ResolveByName(&result, name) + + ensure.Nil(t, err) + ensure.DeepEqual(t, result, a) +} + +func TestResolveByNameWithStructVariable(t *testing.T) { + var g goject.Container + a := &TypeAnswerStruct{answer: 1, private: 2} + const name = "name" + g.Provide(&goject.Object{Name: name, Value: a}) + + var result TypeAnswerStruct + + err := g.ResolveByName(&result, name) + + ensure.Nil(t, err) + ensure.DeepEqual(t, &result, a) +} + +func TestResolveByNameNoProvidedInstanceOfSameType(t *testing.T) { + var g goject.Container + l := &logger{} + const name = "name" + g.Provide(&goject.Object{Name: name, Value: l}) + + var result TypeAnswerStruct + + err := g.ResolveByName(&result, name) + + ensure.NotNil(t, err) + ensure.DeepEqual(t, err.Error(), "No provided object is assignable to dst") +} + +func TestResolveByNameNoProvidedInstanceWithSameName(t *testing.T) { + var g goject.Container + a := &TypeAnswerStruct{answer: 1, private: 2} + const name = "name" + g.Provide(&goject.Object{Value: a}) + + var result TypeAnswerStruct + + err := g.ResolveByName(&result, name) + + ensure.NotNil(t, err) + ensure.DeepEqual(t, err.Error(), fmt.Sprintf("No provided object with the name: %s", name)) +} + +func TestResolveByNameWithNoPointer(t *testing.T) { + var g goject.Container + a := &TypeAnswerStruct{answer: 1, private: 2} + const name = "name" + g.Provide(&goject.Object{Name: name, Value: a}) + + var result TypeAnswerStruct + + err := g.ResolveByName(result, name) + + ensure.NotNil(t, err) + ensure.DeepEqual(t, err.Error(), "dst its not a pointer") +} diff --git a/license b/license index 953e8f7..0be319d 100644 --- a/license +++ b/license @@ -1,30 +1,21 @@ -BSD License +MIT License -For inject software +Copyright (c) 2018-Present, Emanuel Ramos, Inc. -Copyright (c) 2015, Facebook, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/patents b/patents deleted file mode 100644 index f33dc80..0000000 --- a/patents +++ /dev/null @@ -1,33 +0,0 @@ -Additional Grant of Patent Rights Version 2 - -"Software" means the inject software distributed by Facebook, Inc. - -Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software -("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable -(subject to the termination provision below) license under any Necessary -Claims, to make, have made, use, sell, offer to sell, import, and otherwise -transfer the Software. For avoidance of doubt, no license is granted under -Facebook’s rights in any patent claims that are infringed by (i) modifications -to the Software made by you or any third party or (ii) the Software in -combination with any software or other technology. - -The license granted hereunder will terminate, automatically and without notice, -if you (or any of your subsidiaries, corporate affiliates or agents) initiate -directly or indirectly, or take a direct financial interest in, any Patent -Assertion: (i) against Facebook or any of its subsidiaries or corporate -affiliates, (ii) against any party if such Patent Assertion arises in whole or -in part from any software, technology, product or service of Facebook or any of -its subsidiaries or corporate affiliates, or (iii) against any party relating -to the Software. Notwithstanding the foregoing, if Facebook or any of its -subsidiaries or corporate affiliates files a lawsuit alleging patent -infringement against you in the first instance, and you respond by filing a -patent infringement counterclaim in that lawsuit against that party that is -unrelated to the Software, the license granted hereunder will not terminate -under section (i) of this paragraph due to such counterclaim. - -A "Necessary Claim" is a claim of a patent owned by Facebook that is -necessarily infringed by the Software standing alone. - -A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, -or contributory infringement or inducement to infringe any patent, including a -cross-claim or counterclaim. diff --git a/readme.md b/readme.md index 6f6e1bd..794727c 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,8 @@ -inject [![Build Status](https://secure.travis-ci.org/facebookgo/inject.png)](https://travis-ci.org/facebookgo/inject) +Goject [![Build Status](https://secure.travis-ci.org/imaramos/goject.png)](https://travis-ci.org/imaramos/goject) ====== -Documentation: https://godoc.org/github.com/facebookgo/inject +Goject is intended to be a easy way to do dependency injection in Go Applications. + +This is a fork of https://github.com/facebookgo/inject with the purpose of filling the lack of support and extensability. + +Documentation: https://godoc.org/github.com/imaramos/goject