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

support Provide() for lazy get #31

Open
wants to merge 1 commit 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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ type TypeMapper interface {
// 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
// Provide the dynamic type of interface{} returns,
Provide(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.
Expand Down
55 changes: 48 additions & 7 deletions inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type TypeMapper interface {
// 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
// Provide the dynamic type of interface{} returns,
Provide(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.
Expand All @@ -53,8 +55,9 @@ type TypeMapper interface {
}

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

// InterfaceOf dereferences a pointer to an Interface type.
Expand All @@ -76,7 +79,8 @@ 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]reflect.Value),
providers: make(map[reflect.Type]reflect.Value),
}
}

Expand Down Expand Up @@ -148,8 +152,22 @@ func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
return i
}

// Provide the dynamic type of provider returns,
// It returns the TypeMapper registered in.
func (inj *injector) Provide(provider interface{}) TypeMapper {
val := reflect.ValueOf(provider)
t := val.Type()
numout := t.NumOut()
for i := 0; i < numout; i++ {
out := t.Out(i)
inj.providers[out] = val
}
return inj
}

// Maps the given reflect.Type to the given reflect.Value and returns
// the Typemapper the mapping has been registered in.
// It panics if invoke provider failed.
func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
i.values[typ] = val
return i
Expand All @@ -162,19 +180,42 @@ func (i *injector) Get(t reflect.Type) reflect.Value {
return val
}

// try to find providers
if provider, ok := i.providers[t]; ok {
// invoke provider to inject return values
results, err := i.Invoke(provider.Interface())
if err != nil {
panic(err)
}
for _, result := range results {
resultType := result.Type()

i.values[resultType] = result

// provider should not be called again
delete(i.providers, resultType)

if resultType == t {
val = result
}
}
if val.IsValid() {
return val
}
}

// no concrete types found, try to find implementors
// if t is an interface
if t.Kind() == reflect.Interface {
for k, v := range i.values {
if k.Implements(t) {
val = v
break
if k.Implements(t) && v.IsValid() {
return v
}
}
}

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

Expand Down
45 changes: 43 additions & 2 deletions inject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package inject_test

import (
"fmt"
"github.com/codegangsta/inject"
"reflect"
"testing"

"github.com/tsaikd/inject"
)

type SpecialString interface {
Expand All @@ -21,7 +22,7 @@ type Greeter struct {
}

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

/* Test Helpers */
Expand Down Expand Up @@ -157,3 +158,43 @@ func TestInjectImplementors(t *testing.T) {

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

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

expect(t, injector.Get(reflect.TypeOf(&TestStruct{})).IsValid(), false)

injector.Provide(func() *TestStruct {
return &TestStruct{
Dep3: "test",
}
})

injectedStruct := injector.Get(reflect.TypeOf(&TestStruct{}))
expect(t, injectedStruct.IsValid(), true)
if injectedStruct.IsValid() {
expect(t, injectedStruct.Interface().(*TestStruct).Dep3, "test")
}

_, err := injector.Invoke(func(s1 *TestStruct) {
expect(t, s1.Dep3, "test")
})
expect(t, err, nil)
}

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

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

injector.Provide(func() fmt.Stringer {
return &Greeter{"Jeremy"}
})

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

_, err := injector.Invoke(func(stringer fmt.Stringer) {
expect(t, stringer.String(), "Hello, My name is Jeremy")
})
expect(t, err, nil)
}