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

Use id to identify objects of the same type #38

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
```

Expand Down
76 changes: 55 additions & 21 deletions inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand All @@ -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),
}
}

Expand All @@ -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)
}
Expand All @@ -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 {
Expand All @@ -121,42 +128,63 @@ 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
}

// 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
Expand All @@ -167,15 +195,15 @@ 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
}
}
}

// 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
Expand All @@ -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
}
}
43 changes: 22 additions & 21 deletions inject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}