mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
create one generic package for dependency injection which can be used outside of Iris too - worked but unfished
Former-commit-id: a9d600321c07d7c9f39105416f14ae91528a16a3
This commit is contained in:
parent
a5fac270cf
commit
4e15f4ea88
|
@ -1,8 +1,10 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
|
// import "github.com/kataras/iris/mvc2"
|
||||||
|
|
||||||
// ValuesController is the equivalent
|
// ValuesController is the equivalent
|
||||||
// `ValuesController` of the .net core 2.0 mvc application.
|
// `ValuesController` of the .net core 2.0 mvc application.
|
||||||
type ValuesController struct{}
|
type ValuesController struct{} //{ mvc2.C }
|
||||||
|
|
||||||
/* on windows tests(older) the Get was:
|
/* on windows tests(older) the Get was:
|
||||||
func (vc *ValuesController) Get() {
|
func (vc *ValuesController) Get() {
|
||||||
|
|
|
@ -46,7 +46,7 @@ func main() {
|
||||||
|
|
||||||
// using an io.Writer for automatic buffer management (i.e. hero built-in buffer pool),
|
// using an io.Writer for automatic buffer management (i.e. hero built-in buffer pool),
|
||||||
// iris context implements the io.Writer by its ResponseWriter
|
// iris context implements the io.Writer by its ResponseWriter
|
||||||
// which is an enhanced version of the standar http.ResponseWriter
|
// which is an enhanced version of the standard http.ResponseWriter
|
||||||
// but still 100% compatible.
|
// but still 100% compatible.
|
||||||
template.UserListToWriter(userList, ctx)
|
template.UserListToWriter(userList, ctx)
|
||||||
})
|
})
|
||||||
|
|
254
mvc2/bind.go
254
mvc2/bind.go
|
@ -1,258 +1,34 @@
|
||||||
package mvc2
|
package mvc2
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"github.com/kataras/di"
|
||||||
type bindType uint32
|
"reflect"
|
||||||
|
|
||||||
const (
|
|
||||||
objectType bindType = iota // simple assignable value.
|
|
||||||
functionResultType // dynamic value, depends on the context.
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type bindObject struct {
|
var (
|
||||||
Type reflect.Type // the Type of 'Value' or the type of the returned 'ReturnValue' .
|
typeChecker = func(fn reflect.Type) bool {
|
||||||
Value reflect.Value
|
// invalid if that single input arg is not a typeof context.Context.
|
||||||
|
return isContext(fn.In(0))
|
||||||
BindType bindType
|
|
||||||
ReturnValue func(ctx []reflect.Value) reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeReturnValue takes any function
|
|
||||||
// that accept a context and returns something
|
|
||||||
// and returns a binder function, which accepts the context as slice of reflect.Value
|
|
||||||
// and returns a reflect.Value for that.
|
|
||||||
// Iris uses to
|
|
||||||
// resolve and set the input parameters when a handler is executed.
|
|
||||||
//
|
|
||||||
// The "fn" can have the following form:
|
|
||||||
// `func(iris.Context) UserViewModel`.
|
|
||||||
//
|
|
||||||
// The return type of the "fn" should be a value instance, not a pointer, for your own protection.
|
|
||||||
// The binder function should return only one value and
|
|
||||||
// it can accept only one input argument,
|
|
||||||
// the Iris' Context (`context.Context` or `iris.Context`).
|
|
||||||
func makeReturnValue(fn reflect.Value) (func([]reflect.Value) reflect.Value, reflect.Type, error) {
|
|
||||||
typ := indirectTyp(fn.Type())
|
|
||||||
|
|
||||||
// invalid if not a func.
|
|
||||||
if typ.Kind() != reflect.Func {
|
|
||||||
return nil, typ, errBad
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid if not returns one single value.
|
hijacker = func(fieldOrFuncInput reflect.Type) (*di.BindObject, bool) {
|
||||||
if typ.NumOut() != 1 {
|
if isContext(fieldOrFuncInput) {
|
||||||
return nil, typ, errBad
|
return newContextBindObject(), true
|
||||||
}
|
|
||||||
|
|
||||||
// invalid if input args length is not one.
|
|
||||||
if typ.NumIn() != 1 {
|
|
||||||
return nil, typ, errBad
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalid if that single input arg is not a typeof context.Context.
|
|
||||||
if !isContext(typ.In(0)) {
|
|
||||||
return nil, typ, errBad
|
|
||||||
}
|
|
||||||
|
|
||||||
outTyp := typ.Out(0)
|
|
||||||
zeroOutVal := reflect.New(outTyp).Elem()
|
|
||||||
|
|
||||||
bf := func(ctxValue []reflect.Value) reflect.Value {
|
|
||||||
results := fn.Call(ctxValue) // ctxValue is like that because of; read makeHandler.
|
|
||||||
if len(results) == 0 {
|
|
||||||
return zeroOutVal
|
|
||||||
}
|
}
|
||||||
|
return nil, false
|
||||||
v := results[0]
|
|
||||||
if !v.IsValid() {
|
|
||||||
return zeroOutVal
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
return bf, outTyp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBindObject(v reflect.Value) (b bindObject, err error) {
|
|
||||||
if isFunc(v) {
|
|
||||||
b.BindType = functionResultType
|
|
||||||
b.ReturnValue, b.Type, err = makeReturnValue(v)
|
|
||||||
} else {
|
|
||||||
b.BindType = objectType
|
|
||||||
b.Type = v.Type()
|
|
||||||
b.Value = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// newContextBindObject is being used on both targetFunc and targetStruct.
|
// newContextBindObject is being used on both targetFunc and targetStruct.
|
||||||
// if the func's input argument or the struct's field is a type of Context
|
// if the func's input argument or the struct's field is a type of Context
|
||||||
// then we can do a fast binding using the ctxValue
|
// then we can do a fast binding using the ctxValue
|
||||||
// which is used as slice of reflect.Value, because of the final method's `Call`.
|
// which is used as slice of reflect.Value, because of the final method's `Call`.
|
||||||
func newContextBindObject() *bindObject {
|
func newContextBindObject() *di.BindObject {
|
||||||
return &bindObject{
|
return &di.BindObject{
|
||||||
Type: contextTyp,
|
Type: contextTyp,
|
||||||
BindType: functionResultType,
|
BindType: di.Dynamic,
|
||||||
ReturnValue: func(ctxValue []reflect.Value) reflect.Value {
|
ReturnValue: func(ctxValue []reflect.Value) reflect.Value {
|
||||||
return ctxValue[0]
|
return ctxValue[0]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bindObject) IsAssignable(to reflect.Type) bool {
|
|
||||||
return equalTypes(b.Type, to)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bindObject) Assign(ctx []reflect.Value, toSetter func(reflect.Value)) {
|
|
||||||
if b.BindType == functionResultType {
|
|
||||||
toSetter(b.ReturnValue(ctx))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
toSetter(b.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
|
||||||
targetField struct {
|
|
||||||
Object *bindObject
|
|
||||||
FieldIndex []int
|
|
||||||
}
|
|
||||||
targetFuncInput struct {
|
|
||||||
Object *bindObject
|
|
||||||
InputIndex int
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type targetStruct struct {
|
|
||||||
Fields []*targetField
|
|
||||||
Valid bool // is True when contains fields and it's a valid target struct.
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTargetStruct(v reflect.Value, bindValues ...reflect.Value) *targetStruct {
|
|
||||||
typ := indirectTyp(v.Type())
|
|
||||||
s := &targetStruct{}
|
|
||||||
|
|
||||||
fields := lookupFields(typ, nil)
|
|
||||||
for _, f := range fields {
|
|
||||||
// if it's context then bind it directly here and continue to the next field.
|
|
||||||
if isContext(f.Type) {
|
|
||||||
s.Fields = append(s.Fields, &targetField{
|
|
||||||
FieldIndex: f.Index,
|
|
||||||
Object: newContextBindObject(),
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, val := range bindValues {
|
|
||||||
// the binded values to the struct's fields.
|
|
||||||
b, err := makeBindObject(val)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return s // if error stop here.
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.IsAssignable(f.Type) {
|
|
||||||
// fmt.Printf("bind the object to the field: %s at index: %#v and type: %s\n", f.Name, f.Index, f.Type.String())
|
|
||||||
s.Fields = append(s.Fields, &targetField{
|
|
||||||
FieldIndex: f.Index,
|
|
||||||
Object: &b,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Valid = len(s.Fields) > 0
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *targetStruct) Fill(destElem reflect.Value, ctx ...reflect.Value) {
|
|
||||||
for _, f := range s.Fields {
|
|
||||||
f.Object.Assign(ctx, func(v reflect.Value) {
|
|
||||||
// if isContext(v.Type()) {
|
|
||||||
// println("WTF BIND CONTEXT TYPE WHEN BASE CONTROLLER?")
|
|
||||||
// }
|
|
||||||
destElem.FieldByIndex(f.FieldIndex).Set(v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type targetFunc struct {
|
|
||||||
Inputs []*targetFuncInput
|
|
||||||
Valid bool // is True when contains func inputs and it's a valid target func.
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTargetFunc(fn reflect.Value, bindValues ...reflect.Value) *targetFunc {
|
|
||||||
typ := indirectTyp(fn.Type())
|
|
||||||
s := &targetFunc{
|
|
||||||
Valid: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isFunc(typ) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
n := typ.NumIn()
|
|
||||||
|
|
||||||
// function input can have many values of the same types,
|
|
||||||
// so keep track of them in order to not set a func input to a next bind value,
|
|
||||||
// i.e (string, string) with two different binder funcs because of the different param's name.
|
|
||||||
consumedValues := make(map[int]bool, n)
|
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
inTyp := typ.In(i)
|
|
||||||
|
|
||||||
// if it's context then bind it directly here and continue to the next func's input arg.
|
|
||||||
if isContext(inTyp) {
|
|
||||||
s.Inputs = append(s.Inputs, &targetFuncInput{
|
|
||||||
InputIndex: i,
|
|
||||||
Object: newContextBindObject(),
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for valIdx, val := range bindValues {
|
|
||||||
if _, shouldSkip := consumedValues[valIdx]; shouldSkip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inTyp := typ.In(i)
|
|
||||||
|
|
||||||
// the binded values to the func's inputs.
|
|
||||||
b, err := makeBindObject(val)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return s // if error stop here.
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.IsAssignable(inTyp) {
|
|
||||||
// fmt.Printf("binded input index: %d for type: %s and value: %v with pointer: %v\n",
|
|
||||||
// i, b.Type.String(), val.String(), val.Pointer())
|
|
||||||
s.Inputs = append(s.Inputs, &targetFuncInput{
|
|
||||||
InputIndex: i,
|
|
||||||
Object: &b,
|
|
||||||
})
|
|
||||||
|
|
||||||
consumedValues[valIdx] = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Valid = len(s.Inputs) > 0
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *targetFunc) Fill(in *[]reflect.Value, ctx ...reflect.Value) {
|
|
||||||
args := *in
|
|
||||||
for _, input := range s.Inputs {
|
|
||||||
input.Object.Assign(ctx, func(v reflect.Value) {
|
|
||||||
// fmt.Printf("assign input index: %d for value: %v\n",
|
|
||||||
// input.InputIndex, v.String())
|
|
||||||
args[input.InputIndex] = v
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
*in = args
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
package mvc2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
/// TODO:
|
|
||||||
// create another package because these bindings things are useful
|
|
||||||
// for other libraries I'm working on, so something like github.com/kataras/di
|
|
||||||
// will be great, combine these with the bind.go and controller's inside handler
|
|
||||||
// but generic things.
|
|
||||||
|
|
||||||
type ValueStore []reflect.Value
|
|
||||||
|
|
||||||
// Bind binds values to this controller, if you want to share
|
|
||||||
// binding values between controllers use the Engine's `Bind` function instead.
|
|
||||||
func (bv *ValueStore) Bind(values ...interface{}) {
|
|
||||||
for _, val := range values {
|
|
||||||
bv.bind(reflect.ValueOf(val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bv *ValueStore) bind(v reflect.Value) {
|
|
||||||
if !goodVal(v) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*bv = append(*bv, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unbind unbinds a binding value based on the type,
|
|
||||||
// it returns true if at least one field is not binded anymore.
|
|
||||||
//
|
|
||||||
// The "n" indicates the number of elements to remove, if <=0 then it's 1,
|
|
||||||
// this is useful because you may have bind more than one value to two or more fields
|
|
||||||
// with the same type.
|
|
||||||
func (bv *ValueStore) Unbind(value interface{}, n int) bool {
|
|
||||||
return bv.unbind(reflect.TypeOf(value), n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bv *ValueStore) unbind(typ reflect.Type, n int) (ok bool) {
|
|
||||||
input := *bv
|
|
||||||
for i, in := range input {
|
|
||||||
if equalTypes(in.Type(), typ) {
|
|
||||||
ok = true
|
|
||||||
input = input[:i+copy(input[i:], input[i+1:])]
|
|
||||||
if n > 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*bv = input
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindExists returns true if a binder responsible to
|
|
||||||
// bind and return a type of "typ" is already registered to this controller.
|
|
||||||
func (bv *ValueStore) BindExists(value interface{}) bool {
|
|
||||||
return bv.bindTypeExists(reflect.TypeOf(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bv *ValueStore) bindTypeExists(typ reflect.Type) bool {
|
|
||||||
input := *bv
|
|
||||||
for _, in := range input {
|
|
||||||
if equalTypes(in.Type(), typ) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindIfNotExists bind a value to the controller's field with the same type,
|
|
||||||
// if it's not binded already.
|
|
||||||
//
|
|
||||||
// Returns false if binded already or the value is not the proper one for binding,
|
|
||||||
// otherwise true.
|
|
||||||
func (bv *ValueStore) BindIfNotExists(value interface{}) bool {
|
|
||||||
return bv.bindIfNotExists(reflect.ValueOf(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bv *ValueStore) bindIfNotExists(v reflect.Value) bool {
|
|
||||||
var (
|
|
||||||
typ = v.Type() // no element, raw things here.
|
|
||||||
)
|
|
||||||
|
|
||||||
if !goodVal(v) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if bv.bindTypeExists(typ) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
bv.bind(v)
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/kataras/di"
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
"github.com/kataras/iris/core/router"
|
"github.com/kataras/iris/core/router"
|
||||||
"github.com/kataras/iris/core/router/macro"
|
"github.com/kataras/iris/core/router/macro"
|
||||||
|
@ -89,17 +91,16 @@ type ControllerActivator struct {
|
||||||
// the BeginRequest, EndRequest and OnActivate are reserved by the internal implementation.
|
// the BeginRequest, EndRequest and OnActivate are reserved by the internal implementation.
|
||||||
reservedMethods []string
|
reservedMethods []string
|
||||||
|
|
||||||
// input are always empty after the `activate`
|
// the bindings that comes from the Engine and the controller's filled fields if any.
|
||||||
// are used to build the bindings, and we need this field
|
// Can be binded to the the new controller's fields and method that is fired
|
||||||
// because we have 3 states (Engine.Input, OnActivate, Bind)
|
// on incoming requests.
|
||||||
// that we can add or override binding values.
|
Dependencies *di.D
|
||||||
ValueStore // TODO: or ... this is dirty code I will have to re format it a bit tomorrow.
|
|
||||||
|
|
||||||
// the bindings that comes from input (and Engine) and can be binded to the controller's(initRef) fields.
|
// on activate.
|
||||||
bindings *targetStruct
|
injector *di.StructInjector
|
||||||
}
|
}
|
||||||
|
|
||||||
func newControllerActivator(router router.Party, controller interface{}, bindValues ...reflect.Value) *ControllerActivator {
|
func newControllerActivator(router router.Party, controller interface{}, d *di.D) *ControllerActivator {
|
||||||
var (
|
var (
|
||||||
val = reflect.ValueOf(controller)
|
val = reflect.ValueOf(controller)
|
||||||
typ = val.Type()
|
typ = val.Type()
|
||||||
|
@ -115,7 +116,7 @@ func newControllerActivator(router router.Party, controller interface{}, bindVal
|
||||||
// the end-developer when declaring the controller,
|
// the end-developer when declaring the controller,
|
||||||
// activate listeners needs them in order to know if something set-ed already or not,
|
// activate listeners needs them in order to know if something set-ed already or not,
|
||||||
// look `BindTypeExists`.
|
// look `BindTypeExists`.
|
||||||
bindValues = append(lookupNonZeroFieldsValues(val), bindValues...)
|
d.Values = append(lookupNonZeroFieldsValues(val), d.Values...)
|
||||||
|
|
||||||
c := &ControllerActivator{
|
c := &ControllerActivator{
|
||||||
// give access to the Router to the end-devs if they need it for some reason,
|
// give access to the Router to the end-devs if they need it for some reason,
|
||||||
|
@ -133,20 +134,22 @@ func newControllerActivator(router router.Party, controller interface{}, bindVal
|
||||||
//
|
//
|
||||||
// TODO: now that BaseController is totally optionally
|
// TODO: now that BaseController is totally optionally
|
||||||
// we have to check if BeginRequest and EndRequest should be here.
|
// we have to check if BeginRequest and EndRequest should be here.
|
||||||
reservedMethods: []string{
|
reservedMethods: whatReservedMethods(typ),
|
||||||
"BeginRequest",
|
Dependencies: d,
|
||||||
"EndRequest",
|
|
||||||
"OnActivate",
|
|
||||||
},
|
|
||||||
// set the input as []reflect.Value in order to be able
|
|
||||||
// to check if a bind type is already exists, or even
|
|
||||||
// override the structBindings that are being generated later on.
|
|
||||||
ValueStore: bindValues,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func whatReservedMethods(typ reflect.Type) []string {
|
||||||
|
methods := []string{"OnActivate"}
|
||||||
|
if isBaseController(typ) {
|
||||||
|
methods = append(methods, "BeginRequest", "EndRequest")
|
||||||
|
}
|
||||||
|
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
// checks if a method is already registered.
|
// checks if a method is already registered.
|
||||||
func (c *ControllerActivator) isReservedMethod(name string) bool {
|
func (c *ControllerActivator) isReservedMethod(name string) bool {
|
||||||
for _, s := range c.reservedMethods {
|
for _, s := range c.reservedMethods {
|
||||||
|
@ -178,15 +181,8 @@ func (c *ControllerActivator) parseMethods() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBindings will override any bindings with the new "values".
|
|
||||||
func (c *ControllerActivator) SetBindings(values ...reflect.Value) {
|
|
||||||
// set field index with matching binders, if any.
|
|
||||||
c.ValueStore = values
|
|
||||||
c.bindings = newTargetStruct(c.Value, values...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ControllerActivator) activate() {
|
func (c *ControllerActivator) activate() {
|
||||||
c.SetBindings(c.ValueStore...)
|
c.injector = c.Dependencies.Struct(c.Value)
|
||||||
c.parseMethods()
|
c.parseMethods()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,11 +232,13 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
|
||||||
// end-dev's controller pointer.
|
// end-dev's controller pointer.
|
||||||
pathParams := getPathParamsForInput(tmpl.Params, funcIn[1:]...)
|
pathParams := getPathParamsForInput(tmpl.Params, funcIn[1:]...)
|
||||||
// get the function's input arguments' bindings.
|
// get the function's input arguments' bindings.
|
||||||
funcBindings := newTargetFunc(m.Func, pathParams...)
|
funcDependencies := c.Dependencies.Clone()
|
||||||
|
funcDependencies.Add(pathParams...)
|
||||||
|
funcInjector := funcDependencies.Func(m.Func)
|
||||||
|
|
||||||
// we will make use of 'n' to make a slice of reflect.Value
|
// we will make use of 'n' to make a slice of reflect.Value
|
||||||
// to pass into if the function has input arguments that
|
// to pass into if the function has input arguments that
|
||||||
// are will being filled by the funcBindings.
|
// are will being filled by the funcDependencies.
|
||||||
n := len(funcIn)
|
n := len(funcIn)
|
||||||
// the element value, not the pointer, wil lbe used to create a
|
// the element value, not the pointer, wil lbe used to create a
|
||||||
// new controller on each incoming request.
|
// new controller on each incoming request.
|
||||||
|
@ -249,19 +247,8 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
|
||||||
implementsBase := isBaseController(c.Type)
|
implementsBase := isBaseController(c.Type)
|
||||||
|
|
||||||
handler := func(ctx context.Context) {
|
handler := func(ctx context.Context) {
|
||||||
// create a new controller instance of that type(>ptr).
|
|
||||||
ctrl := reflect.New(elemTyp)
|
ctrl := reflect.New(elemTyp)
|
||||||
|
|
||||||
// // the Interface(). is faster than MethodByName or pre-selected methods.
|
|
||||||
// b := ctrl.Interface().(BaseController)
|
|
||||||
// // init the request.
|
|
||||||
// b.BeginRequest(ctx)
|
|
||||||
|
|
||||||
// // if begin request stopped the execution.
|
|
||||||
// if ctx.IsStopped() {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
if implementsBase {
|
if implementsBase {
|
||||||
// the Interface(). is faster than MethodByName or pre-selected methods.
|
// the Interface(). is faster than MethodByName or pre-selected methods.
|
||||||
b := ctrl.Interface().(BaseController)
|
b := ctrl.Interface().(BaseController)
|
||||||
|
@ -273,34 +260,32 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndRequest will be called at any case except the `BeginRequest` is
|
|
||||||
// stopped.
|
|
||||||
defer b.EndRequest(ctx)
|
defer b.EndRequest(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.bindings.Valid && !funcBindings.Valid {
|
if !c.injector.Valid && !funcInjector.Valid {
|
||||||
DispatchFuncResult(ctx, ctrl.Method(m.Index).Call(emptyIn))
|
DispatchFuncResult(ctx, ctrl.Method(m.Index).Call(emptyIn))
|
||||||
} else {
|
} else {
|
||||||
ctxValue := reflect.ValueOf(ctx)
|
ctxValue := reflect.ValueOf(ctx)
|
||||||
if c.bindings.Valid {
|
if c.injector.Valid {
|
||||||
elem := ctrl.Elem()
|
elem := ctrl.Elem()
|
||||||
c.bindings.Fill(elem, ctxValue)
|
c.injector.InjectElem(elem, ctxValue)
|
||||||
if ctx.IsStopped() {
|
if ctx.IsStopped() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// we do this in order to reduce in := make...
|
// we do this in order to reduce in := make...
|
||||||
// if not func input binders, we execute the handler with empty input args.
|
// if not func input binders, we execute the handler with empty input args.
|
||||||
if !funcBindings.Valid {
|
if !funcInjector.Valid {
|
||||||
DispatchFuncResult(ctx, ctrl.Method(m.Index).Call(emptyIn))
|
DispatchFuncResult(ctx, ctrl.Method(m.Index).Call(emptyIn))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// otherwise, it has one or more valid input binders,
|
// otherwise, it has one or more valid input binders,
|
||||||
// make the input and call the func using those.
|
// make the input and call the func using those.
|
||||||
if funcBindings.Valid {
|
if funcInjector.Valid {
|
||||||
in := make([]reflect.Value, n, n)
|
in := make([]reflect.Value, n, n)
|
||||||
in[0] = ctrl
|
in[0] = ctrl
|
||||||
funcBindings.Fill(&in, ctxValue)
|
funcInjector.Inject(&in, ctxValue)
|
||||||
if ctx.IsStopped() {
|
if ctx.IsStopped() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -309,12 +294,6 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if ctx.IsStopped() {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// b.EndRequest(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// register the handler now.
|
// register the handler now.
|
||||||
|
|
|
@ -421,8 +421,8 @@ type testControllerActivateListener struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testControllerActivateListener) OnActivate(ca *ControllerActivator) {
|
func (c *testControllerActivateListener) OnActivate(ca *ControllerActivator) {
|
||||||
if !ca.BindExists(&testBindType{}) {
|
if !ca.Dependencies.BindExists(&testBindType{}) {
|
||||||
ca.Bind(&testBindType{
|
ca.Dependencies.Bind(&testBindType{
|
||||||
title: "default title",
|
title: "default title",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@ package mvc2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
|
||||||
|
|
||||||
|
"github.com/kataras/di"
|
||||||
"github.com/kataras/golog"
|
"github.com/kataras/golog"
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
"github.com/kataras/iris/core/router"
|
"github.com/kataras/iris/core/router"
|
||||||
)
|
)
|
||||||
|
@ -16,38 +17,28 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
Input []reflect.Value
|
dependencies *di.D
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Engine {
|
func New() *Engine {
|
||||||
return new(Engine)
|
return &Engine{
|
||||||
|
dependencies: di.New().Hijack(hijacker).GoodFunc(typeChecker),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) Bind(values ...interface{}) *Engine {
|
func (e *Engine) Bind(values ...interface{}) *Engine {
|
||||||
for _, val := range values {
|
e.dependencies.Bind(values...)
|
||||||
if v := reflect.ValueOf(val); goodVal(v) {
|
|
||||||
e.Input = append(e.Input, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) Child() *Engine {
|
func (e *Engine) Child() *Engine {
|
||||||
child := New()
|
child := New()
|
||||||
|
child.dependencies = e.dependencies.Clone()
|
||||||
// copy the current parent's ctx func binders and services to this new child.
|
|
||||||
if n := len(e.Input); n > 0 {
|
|
||||||
input := make([]reflect.Value, n, n)
|
|
||||||
copy(input, e.Input)
|
|
||||||
child.Input = input
|
|
||||||
}
|
|
||||||
|
|
||||||
return child
|
return child
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) Handler(handler interface{}) context.Handler {
|
func (e *Engine) Handler(handler interface{}) context.Handler {
|
||||||
h, err := MakeHandler(handler, e.Input...)
|
h, err := MakeHandler(handler, e.dependencies.Values...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Errorf("mvc handler: %v", err)
|
golog.Errorf("mvc handler: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +46,7 @@ func (e *Engine) Handler(handler interface{}) context.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) Controller(router router.Party, controller interface{}, onActivate ...func(*ControllerActivator)) {
|
func (e *Engine) Controller(router router.Party, controller interface{}, onActivate ...func(*ControllerActivator)) {
|
||||||
ca := newControllerActivator(router, controller, e.Input...)
|
ca := newControllerActivator(router, controller, e.dependencies)
|
||||||
|
|
||||||
// give a priority to the "onActivate"
|
// give a priority to the "onActivate"
|
||||||
// callbacks, if any.
|
// callbacks, if any.
|
||||||
|
|
|
@ -267,7 +267,7 @@ func (t *testControllerViewResultRespectCtxViewData) Get() Result {
|
||||||
func TestControllerViewResultRespectCtxViewData(t *testing.T) {
|
func TestControllerViewResultRespectCtxViewData(t *testing.T) {
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
New().Controller(app, new(testControllerViewResultRespectCtxViewData), func(ca *ControllerActivator) {
|
New().Controller(app, new(testControllerViewResultRespectCtxViewData), func(ca *ControllerActivator) {
|
||||||
ca.Bind(t)
|
ca.Dependencies.Bind(t)
|
||||||
})
|
})
|
||||||
e := httptest.New(t, app)
|
e := httptest.New(t, app)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package mvc2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kataras/di"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ func MakeHandler(handler interface{}, bindValues ...reflect.Value) (context.Hand
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s := newTargetFunc(fn, bindValues...)
|
s := di.MakeFuncInjector(fn, hijacker, typeChecker, bindValues...)
|
||||||
if !s.Valid {
|
if !s.Valid {
|
||||||
pc := fn.Pointer()
|
pc := fn.Pointer()
|
||||||
fpc := runtime.FuncForPC(pc)
|
fpc := runtime.FuncForPC(pc)
|
||||||
|
@ -72,14 +73,14 @@ func MakeHandler(handler interface{}, bindValues ...reflect.Value) (context.Hand
|
||||||
callerName := fpc.Name()
|
callerName := fpc.Name()
|
||||||
|
|
||||||
err := fmt.Errorf("input arguments length(%d) and valid binders length(%d) are not equal for typeof '%s' which is defined at %s:%d by %s",
|
err := fmt.Errorf("input arguments length(%d) and valid binders length(%d) are not equal for typeof '%s' which is defined at %s:%d by %s",
|
||||||
n, len(s.Inputs), fn.Type().String(), callerFileName, callerLineNumber, callerName)
|
n, s.Length, fn.Type().String(), callerFileName, callerLineNumber, callerName)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
h := func(ctx context.Context) {
|
h := func(ctx context.Context) {
|
||||||
in := make([]reflect.Value, n, n)
|
in := make([]reflect.Value, n, n)
|
||||||
|
|
||||||
s.Fill(&in, reflect.ValueOf(ctx))
|
s.Inject(&in, reflect.ValueOf(ctx))
|
||||||
if ctx.IsStopped() {
|
if ctx.IsStopped() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,10 @@ func lookupFields(elemTyp reflect.Type, parentIndex []int) (fields []field) {
|
||||||
for i, n := 0, elemTyp.NumField(); i < n; i++ {
|
for i, n := 0, elemTyp.NumField(); i < n; i++ {
|
||||||
f := elemTyp.Field(i)
|
f := elemTyp.Field(i)
|
||||||
|
|
||||||
|
if f.PkgPath != "" {
|
||||||
|
continue // skip unexported.
|
||||||
|
}
|
||||||
|
|
||||||
if indirectTyp(f.Type).Kind() == reflect.Struct &&
|
if indirectTyp(f.Type).Kind() == reflect.Struct &&
|
||||||
!structFieldIgnored(f) {
|
!structFieldIgnored(f) {
|
||||||
fields = append(fields, lookupFields(f.Type, append(parentIndex, i))...)
|
fields = append(fields, lookupFields(f.Type, append(parentIndex, i))...)
|
||||||
|
|
|
@ -22,7 +22,7 @@ type SessionController struct {
|
||||||
// It makes sure that its "Manager" field is filled
|
// It makes sure that its "Manager" field is filled
|
||||||
// even if the caller didn't provide any sessions manager via the `app.Controller` function.
|
// even if the caller didn't provide any sessions manager via the `app.Controller` function.
|
||||||
func (s *SessionController) OnActivate(ca *ControllerActivator) {
|
func (s *SessionController) OnActivate(ca *ControllerActivator) {
|
||||||
if didntBindManually := ca.BindIfNotExists(defaultSessionManager); didntBindManually {
|
if didntBindManually := ca.Dependencies.BindIfNotExists(defaultSessionManager); didntBindManually {
|
||||||
ca.Router.GetReporter().Add(
|
ca.Router.GetReporter().Add(
|
||||||
`MVC SessionController: couldn't find any "*sessions.Sessions" bindable value to fill the "Manager" field,
|
`MVC SessionController: couldn't find any "*sessions.Sessions" bindable value to fill the "Manager" field,
|
||||||
therefore this controller is using the default sessions manager instead.
|
therefore this controller is using the default sessions manager instead.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user