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

Added InvokeWithArgs #23

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
87 changes: 70 additions & 17 deletions inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package inject

import (
"errors"
"fmt"
"reflect"
)
Expand Down Expand Up @@ -33,6 +34,14 @@ type Invoker interface {
// a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
Invoke(interface{}) ([]reflect.Value, error)
// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type.
// The passed arguments are required to be injected in the invocation, if
// Not all the arguments can be injected an error will be raised
// Returns a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
// It panics if f is not a function
InvokeWithArgs(interface{}, ...interface{}) ([]reflect.Value, error)
}

// TypeMapper represents an interface for mapping interface{} values based on type.
Expand All @@ -52,8 +61,30 @@ type TypeMapper interface {
Get(reflect.Type) reflect.Value
}

type valueMap map[reflect.Type]reflect.Value

func (m valueMap) Get(t reflect.Type) reflect.Value {
val := m[t]

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 m {
if k.Implements(t) {
val = v
break
}
}
}
return val
}

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

Expand Down Expand Up @@ -102,6 +133,43 @@ func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
return reflect.ValueOf(f).Call(in), nil
}

// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type.
// The passed arguments are required to be injected in the invocation, if
// Not all the arguments can be injected an error will be raised
// Returns a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
// It panics if f is not a function
func (inj *injector) InvokeWithArgs(f interface{}, args ...interface{}) ([]reflect.Value, error) {
t := reflect.TypeOf(f)

argsMap := valueMap(make(map[reflect.Type]reflect.Value))
for _, a := range args {
argsMap[reflect.TypeOf(a)] = reflect.ValueOf(a)
}

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)
if !val.IsValid() {
val = argsMap.Get(argType)
if !val.IsValid() {
return nil, fmt.Errorf("Value not found for type %v", argType)
}
delete(argsMap, argType)
}

in[i] = val
}

if len(argsMap) > 0 {
return nil, errors.New("Not all arguments could be injected")
}

return reflect.ValueOf(f).Call(in), nil
}

// Maps dependencies in the Type map to each field in the struct
// that is tagged with 'inject'.
// Returns an error if the injection fails.
Expand Down Expand Up @@ -156,22 +224,7 @@ func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
}

func (i *injector) Get(t reflect.Type) reflect.Value {
val := i.values[t]

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
}
}
}
val := i.values.Get(t)

// Still no type found, try to look it up on the parent
if !val.IsValid() && i.parent != nil {
Expand Down
37 changes: 36 additions & 1 deletion 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/acasajus/inject"
)

type SpecialString interface {
Expand Down Expand Up @@ -64,6 +65,40 @@ func Test_InjectorInvoke(t *testing.T) {
expect(t, err, nil)
}

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

dep := "some dependency"
injector.Map(dep)
dep2 := 3
injector.Map(dep2)
arg1 := make(chan *SpecialString)
arg2 := &Greeter{"Me"}

_, err := injector.InvokeWithArgs(func(d1 string, d2 int, a1 chan *SpecialString, a2 *Greeter) {
expect(t, d1, dep)
expect(t, d2, dep2)
expect(t, reflect.TypeOf(a1).Elem(), reflect.TypeOf(arg1).Elem())
expect(t, reflect.TypeOf(a1).ChanDir(), reflect.BothDir)
expect(t, a2, arg2)
}, arg1, arg2)

expect(t, err, nil)

_, err = injector.InvokeWithArgs(func(d1 string, d2 int, a1 chan *SpecialString, a2 *Greeter) {
expect(t, d1, dep)
expect(t, d2, dep2)
expect(t, reflect.TypeOf(a1).Elem(), reflect.TypeOf(arg1).Elem())
expect(t, reflect.TypeOf(a1).ChanDir(), reflect.BothDir)
expect(t, a2, arg2)
}, arg1)

if err == nil || err.Error() != "Value not found for type *inject_test.Greeter" {
t.Error("Did not properly fail to invoke method")
}
}

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