Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Panic on ambiguous implementations #37

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type Injector interface {
// dependency in its Type map it will check its parent before returning an
// error.
SetParent(Injector)
// SetOptions sets options to configure the injector.
SetOptions(InjectorOptions)
}
```

Expand Down
29 changes: 24 additions & 5 deletions inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type Injector interface {
// dependency in its Type map it will check its parent before returning an
// error.
SetParent(Injector)
// SetOptions sets options to configure the injector.
SetOptions(InjectorOptions)
}

// Applicator represents an interface for mapping dependencies to a struct.
Expand Down Expand Up @@ -52,9 +54,17 @@ type TypeMapper interface {
Get(reflect.Type) reflect.Value
}

// InjectorOptions contains options to configure the injector
type InjectorOptions struct {
// If PanicOnAmbiguity is set to true, Get method will panic if it finds multiple
// implementations that satisfy the given type.
PanicOnAmbiguity bool
}

type injector struct {
values map[reflect.Type]reflect.Value
parent Injector
options InjectorOptions
values map[reflect.Type]reflect.Value
parent Injector
}

// InterfaceOf dereferences a pointer to an Interface type.
Expand Down Expand Up @@ -164,24 +174,33 @@ func (i *injector) Get(t reflect.Type) reflect.Value {

// no concrete types found, try to find implementors
// if t is an interface
var impls []reflect.Value
if t.Kind() == reflect.Interface {
for k, v := range i.values {
if k.Implements(t) {
val = v
break
impls = append(impls, v)
}
}
}
if len(impls) > 1 && i.options.PanicOnAmbiguity {
panic(fmt.Sprintf("Expected single matching implementation for type <%v> but found %v: %v", t, len(impls), impls))
}
if len(impls) > 0 {
val = impls[0]
}

// Still no type found, try to look it up on the parent
if !val.IsValid() && i.parent != nil {
val = i.parent.Get(t)
}

return val

}

func (i *injector) SetParent(parent Injector) {
i.parent = parent
}

func (inj *injector) SetOptions(options InjectorOptions) {
inj.options = options
}
60 changes: 45 additions & 15 deletions inject_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package inject_test
package inject

import (
"fmt"
"github.com/codegangsta/inject"
"reflect"
"testing"
)
Expand All @@ -24,6 +23,14 @@ func (g *Greeter) String() string {
return "Hello, My name is" + g.Name
}

type Greeter2 struct {
Name string
}

func (g *Greeter2) String() string {
return "Hello, My name is" + g.Name
}

/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
if a != b {
Expand All @@ -38,7 +45,7 @@ func refute(t *testing.T, a interface{}, b interface{}) {
}

func Test_InjectorInvoke(t *testing.T) {
injector := inject.New()
injector := New()
expect(t, injector == nil, false)

dep := "some dependency"
Expand All @@ -65,7 +72,7 @@ func Test_InjectorInvoke(t *testing.T) {
}

func Test_InjectorInvokeReturnValues(t *testing.T) {
injector := inject.New()
injector := New()
expect(t, injector == nil, false)

dep := "some dependency"
Expand All @@ -84,7 +91,7 @@ func Test_InjectorInvokeReturnValues(t *testing.T) {
}

func Test_InjectorApply(t *testing.T) {
injector := inject.New()
injector := New()

injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil))

Expand All @@ -98,22 +105,22 @@ func Test_InjectorApply(t *testing.T) {
}

func Test_InterfaceOf(t *testing.T) {
iType := inject.InterfaceOf((*SpecialString)(nil))
iType := InterfaceOf((*SpecialString)(nil))
expect(t, iType.Kind(), reflect.Interface)

iType = inject.InterfaceOf((**SpecialString)(nil))
iType = InterfaceOf((**SpecialString)(nil))
expect(t, iType.Kind(), reflect.Interface)

// Expecting nil
defer func() {
rec := recover()
refute(t, rec, nil)
}()
iType = inject.InterfaceOf((*testing.T)(nil))
iType = InterfaceOf((*testing.T)(nil))
}

func Test_InjectorSet(t *testing.T) {
injector := inject.New()
injector := New()
typ := reflect.TypeOf("string")
typSend := reflect.ChanOf(reflect.SendDir, typ)
typRecv := reflect.ChanOf(reflect.RecvDir, typ)
Expand All @@ -132,7 +139,7 @@ func Test_InjectorSet(t *testing.T) {
}

func Test_InjectorGet(t *testing.T) {
injector := inject.New()
injector := New()

injector.Map("some dependency")

Expand All @@ -141,19 +148,42 @@ func Test_InjectorGet(t *testing.T) {
}

func Test_InjectorSetParent(t *testing.T) {
injector := inject.New()
injector := New()
injector.MapTo("another dep", (*SpecialString)(nil))

injector2 := inject.New()
injector2 := New()
injector2.SetParent(injector)

expect(t, injector2.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true)
expect(t, injector2.Get(InterfaceOf((*SpecialString)(nil))).IsValid(), true)
}

func TestInjectImplementors(t *testing.T) {
injector := inject.New()
injector := New()
g := &Greeter{"Jeremy"}
injector.Map(g)

expect(t, injector.Get(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
expect(t, injector.Get(InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
}

func TestInjectImplementors_AmbiguousImplementation(t *testing.T) {
injector := New()
g1, g2 := &Greeter{"Jeremy"}, &Greeter2{"Tom"}
injector.Map(g1).Map(g2)

expect(t, injector.Get(InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
}

func TestInjectImplementors_AmbiguousImplementationPanic(t *testing.T) {
defer func() {
r := recover()
expect(t, r, "Expected single matching implementation for type <fmt.Stringer> but found 2: [<*inject.Greeter Value> <*inject.Greeter2 Value>]")
}()

injector := New()
injector.SetOptions(InjectorOptions{
PanicOnAmbiguity: true,
})
g1, g2 := &Greeter{"Jeremy"}, &Greeter2{"Tom"}
injector.Map(g1).Map(g2)
injector.Get(InterfaceOf((*fmt.Stringer)(nil)))
}
2 changes: 2 additions & 0 deletions translations/README_zh_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type Injector interface {
// SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖,
// 将会继续从它的父injector中找,直到返回error.
SetParent(Injector)
// SetOptions提供一个接口用于设置injector.
SetOptions(InjectorOptions)
}
```

Expand Down