diff --git a/README.md b/README.md index 679abe0..401192d 100644 --- a/README.md +++ b/README.md @@ -74,18 +74,18 @@ Invoker represents an interface for calling functions via reflection. ```go type TypeMapper interface { // Maps the interface{} value based on its immediate type from reflect.TypeOf. - Map(interface{}) TypeMapper + Map(string, interface{}) TypeMapper // Maps the interface{} value based on the pointer of an Interface provided. // This is really only useful for mapping a value as an interface, as interfaces // cannot at this time be referenced directly without a pointer. - MapTo(interface{}, interface{}) TypeMapper + MapTo(string, interface{}, interface{}) TypeMapper // Provides a possibility to directly insert a mapping based on type and value. // This makes it possible to directly map type arguments not possible to instantiate // with reflect like unidirectional channels. - Set(reflect.Type, reflect.Value) TypeMapper + Set(string, reflect.Type, reflect.Value) TypeMapper // Returns the Value that is mapped to the current type. Returns a zeroed Value if // the Type has not been mapped. - Get(reflect.Type) reflect.Value + Get(string, reflect.Type) reflect.Value } ``` diff --git a/inject.go b/inject.go index 3ff713c..89a4905 100644 --- a/inject.go +++ b/inject.go @@ -4,6 +4,12 @@ package inject import ( "fmt" "reflect" + "strings" +) + +const ( + // DefaultID for objects to be mapped + DefaultID = "default" ) // Injector represents an interface for mapping and injecting dependencies into structs @@ -38,22 +44,22 @@ type Invoker interface { // TypeMapper represents an interface for mapping interface{} values based on type. type TypeMapper interface { // Maps the interface{} value based on its immediate type from reflect.TypeOf. - Map(interface{}) TypeMapper + Map(string, interface{}) TypeMapper // Maps the interface{} value based on the pointer of an Interface provided. // This is really only useful for mapping a value as an interface, as interfaces // cannot at this time be referenced directly without a pointer. - MapTo(interface{}, interface{}) TypeMapper + MapTo(string, interface{}, interface{}) TypeMapper // Provides a possibility to directly insert a mapping based on type and value. // This makes it possible to directly map type arguments not possible to instantiate // with reflect like unidirectional channels. - Set(reflect.Type, reflect.Value) TypeMapper + Set(string, reflect.Type, reflect.Value) TypeMapper // Returns the Value that is mapped to the current type. Returns a zeroed Value if // the Type has not been mapped. - Get(reflect.Type) reflect.Value + Get(string, reflect.Type) reflect.Value } type injector struct { - values map[reflect.Type]reflect.Value + values map[reflect.Type]map[string]reflect.Value parent Injector } @@ -76,7 +82,7 @@ func InterfaceOf(value interface{}) reflect.Type { // New returns a new Injector. func New() Injector { return &injector{ - values: make(map[reflect.Type]reflect.Value), + values: make(map[reflect.Type]map[string]reflect.Value), } } @@ -91,7 +97,7 @@ func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func for i := 0; i < t.NumIn(); i++ { argType := t.In(i) - val := inj.Get(argType) + val := inj.Get(DefaultID, argType) if !val.IsValid() { return nil, fmt.Errorf("Value not found for type %v", argType) } @@ -106,6 +112,7 @@ func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { // that is tagged with 'inject'. // Returns an error if the injection fails. func (inj *injector) Apply(val interface{}) error { + var err error v := reflect.ValueOf(val) for v.Kind() == reflect.Ptr { @@ -121,16 +128,20 @@ func (inj *injector) Apply(val interface{}) error { for i := 0; i < v.NumField(); i++ { f := v.Field(i) structField := t.Field(i) - if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { + id := structField.Tag.Get("inject") + if f.CanSet() && strings.HasPrefix(string(structField.Tag), "inject") { ft := f.Type() - v := inj.Get(ft) + v := inj.Get(id, ft) if !v.IsValid() { - return fmt.Errorf("Value not found for type %v", ft) + err = fmt.Errorf("Value not found for type %v", ft) + continue } - f.Set(v) } + } + if err != nil { + return err } return nil @@ -138,25 +149,42 @@ func (inj *injector) Apply(val interface{}) error { // Maps the concrete value of val to its dynamic type using reflect.TypeOf, // It returns the TypeMapper registered in. -func (i *injector) Map(val interface{}) TypeMapper { - i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) +func (i *injector) Map(id string, val interface{}) TypeMapper { + tryDefaultID(&id) + + if _, has := i.values[reflect.TypeOf(val)]; !has { + i.values[reflect.TypeOf(val)] = make(map[string]reflect.Value) + } + i.values[reflect.TypeOf(val)][id] = reflect.ValueOf(val) return i } -func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { - i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) +func (i *injector) MapTo(id string, val interface{}, ifacePtr interface{}) TypeMapper { + tryDefaultID(&id) + if _, has := i.values[InterfaceOf(ifacePtr)][id]; !has { + i.values[InterfaceOf(ifacePtr)] = make(map[string]reflect.Value) + } + i.values[InterfaceOf(ifacePtr)][id] = reflect.ValueOf(val) return i } // Maps the given reflect.Type to the given reflect.Value and returns // the Typemapper the mapping has been registered in. -func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { - i.values[typ] = val +func (i *injector) Set(id string, typ reflect.Type, val reflect.Value) TypeMapper { + tryDefaultID(&id) + if _, has := i.values[typ]; !has { + i.values[typ] = make(map[string]reflect.Value) + } + i.values[typ][id] = val return i } -func (i *injector) Get(t reflect.Type) reflect.Value { - val := i.values[t] +func (i *injector) Get(id string, t reflect.Type) reflect.Value { + tryDefaultID(&id) + if _, has := i.values[t]; !has { + i.values[t] = make(map[string]reflect.Value) + } + val := i.values[t][id] if val.IsValid() { return val @@ -167,7 +195,7 @@ func (i *injector) Get(t reflect.Type) reflect.Value { if t.Kind() == reflect.Interface { for k, v := range i.values { if k.Implements(t) { - val = v + val = v[id] break } } @@ -175,7 +203,7 @@ func (i *injector) Get(t reflect.Type) reflect.Value { // Still no type found, try to look it up on the parent if !val.IsValid() && i.parent != nil { - val = i.parent.Get(t) + val = i.parent.Get(id, t) } return val @@ -185,3 +213,9 @@ func (i *injector) Get(t reflect.Type) reflect.Value { func (i *injector) SetParent(parent Injector) { i.parent = parent } + +func tryDefaultID(id *string) { + if *id == "" { + *id = DefaultID + } +} diff --git a/inject_test.go b/inject_test.go index eb94471..4ead35e 100644 --- a/inject_test.go +++ b/inject_test.go @@ -2,16 +2,17 @@ package inject_test import ( "fmt" - "github.com/codegangsta/inject" "reflect" "testing" + + "github.com/silentred/inject" ) type SpecialString interface { } type TestStruct struct { - Dep1 string `inject:"t" json:"-"` + Dep1 string `inject:"default" json:"-"` Dep2 SpecialString `inject` Dep3 string } @@ -42,15 +43,15 @@ func Test_InjectorInvoke(t *testing.T) { expect(t, injector == nil, false) dep := "some dependency" - injector.Map(dep) + injector.Map("", dep) dep2 := "another dep" - injector.MapTo(dep2, (*SpecialString)(nil)) + injector.MapTo("", dep2, (*SpecialString)(nil)) dep3 := make(chan *SpecialString) dep4 := make(chan *SpecialString) typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem()) typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem()) - injector.Set(typRecv, reflect.ValueOf(dep3)) - injector.Set(typSend, reflect.ValueOf(dep4)) + injector.Set("", typRecv, reflect.ValueOf(dep3)) + injector.Set("", typSend, reflect.ValueOf(dep4)) _, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) { expect(t, d1, dep) @@ -69,9 +70,9 @@ func Test_InjectorInvokeReturnValues(t *testing.T) { expect(t, injector == nil, false) dep := "some dependency" - injector.Map(dep) + injector.Map("", dep) dep2 := "another dep" - injector.MapTo(dep2, (*SpecialString)(nil)) + injector.MapTo("", dep2, (*SpecialString)(nil)) result, err := injector.Invoke(func(d1 string, d2 SpecialString) string { expect(t, d1, dep) @@ -86,7 +87,7 @@ func Test_InjectorInvokeReturnValues(t *testing.T) { func Test_InjectorApply(t *testing.T) { injector := inject.New() - injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil)) + injector.Map("", "a dep").MapTo("", "another dep", (*SpecialString)(nil)) s := TestStruct{} err := injector.Apply(&s) @@ -123,37 +124,37 @@ func Test_InjectorSet(t *testing.T) { chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) - injector.Set(typSend, chanSend) - injector.Set(typRecv, chanRecv) + injector.Set("", typSend, chanSend) + injector.Set("", typRecv, chanRecv) - expect(t, injector.Get(typSend).IsValid(), true) - expect(t, injector.Get(typRecv).IsValid(), true) - expect(t, injector.Get(chanSend.Type()).IsValid(), false) + expect(t, injector.Get("", typSend).IsValid(), true) + expect(t, injector.Get("", typRecv).IsValid(), true) + expect(t, injector.Get("", chanSend.Type()).IsValid(), false) } func Test_InjectorGet(t *testing.T) { injector := inject.New() - injector.Map("some dependency") + injector.Map("", "some dependency") - expect(t, injector.Get(reflect.TypeOf("string")).IsValid(), true) - expect(t, injector.Get(reflect.TypeOf(11)).IsValid(), false) + expect(t, injector.Get("", reflect.TypeOf("string")).IsValid(), true) + expect(t, injector.Get("", reflect.TypeOf(11)).IsValid(), false) } func Test_InjectorSetParent(t *testing.T) { injector := inject.New() - injector.MapTo("another dep", (*SpecialString)(nil)) + injector.MapTo("", "another dep", (*SpecialString)(nil)) injector2 := inject.New() injector2.SetParent(injector) - expect(t, injector2.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true) + expect(t, injector2.Get("", inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true) } func TestInjectImplementors(t *testing.T) { injector := inject.New() g := &Greeter{"Jeremy"} - injector.Map(g) + injector.Map("", g) - expect(t, injector.Get(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true) + expect(t, injector.Get("", inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true) }