diff --git a/README.md b/README.md index 679abe0..88664af 100644 --- a/README.md +++ b/README.md @@ -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) } ``` diff --git a/inject.go b/inject.go index 3ff713c..c3b60c6 100644 --- a/inject.go +++ b/inject.go @@ -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. @@ -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. @@ -164,14 +174,20 @@ 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 { @@ -179,9 +195,12 @@ func (i *injector) Get(t reflect.Type) reflect.Value { } return val - } func (i *injector) SetParent(parent Injector) { i.parent = parent } + +func (inj *injector) SetOptions(options InjectorOptions) { + inj.options = options +} diff --git a/inject_test.go b/inject_test.go index eb94471..571d5e6 100644 --- a/inject_test.go +++ b/inject_test.go @@ -1,8 +1,7 @@ -package inject_test +package inject import ( "fmt" - "github.com/codegangsta/inject" "reflect" "testing" ) @@ -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 { @@ -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" @@ -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" @@ -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)) @@ -98,10 +105,10 @@ 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 @@ -109,11 +116,11 @@ func Test_InterfaceOf(t *testing.T) { 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) @@ -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") @@ -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 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))) } diff --git a/translations/README_zh_cn.md b/translations/README_zh_cn.md index 0ac3d3f..c9122ef 100644 --- a/translations/README_zh_cn.md +++ b/translations/README_zh_cn.md @@ -35,6 +35,8 @@ type Injector interface { // SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖, // 将会继续从它的父injector中找,直到返回error. SetParent(Injector) + // SetOptions提供一个接口用于设置injector. + SetOptions(InjectorOptions) } ```