Update to version 12.1.8 - Read HISTORY.md

Former-commit-id: d3d30cb15537146e3071731be9d674a5cb59de97
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-02-16 11:14:35 +02:00
parent 899aec8b19
commit 08403f0317
21 changed files with 275 additions and 240 deletions

View File

@ -21,6 +21,20 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest`. **How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest`.
# Su, 16 February 2020 | v12.1.8
New Features:
- [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)
Fixes:
- [App can't find embedded pug template files by go-bindata](https://github.com/kataras/iris/issues/1450)
New Examples:
- [_examples/mvc/grpc-compatible](_examples/mvc/grpc-compatible)
# Mo, 10 February 2020 | v12.1.7 # Mo, 10 February 2020 | v12.1.7
Implement **new** `SetRegisterRule(iris.RouteOverride, RouteSkip, RouteError)` to resolve: https://github.com/kataras/iris/issues/1448 Implement **new** `SetRegisterRule(iris.RouteOverride, RouteSkip, RouteError)` to resolve: https://github.com/kataras/iris/issues/1448

View File

@ -21,9 +21,9 @@ Los desarrolladores no están obligados a actualizar si realmente no lo necesita
**Cómo actualizar**: Abra su línea de comandos y ejecute este comando: `go get github.com/kataras/iris/v12@latest`. **Cómo actualizar**: Abra su línea de comandos y ejecute este comando: `go get github.com/kataras/iris/v12@latest`.
# Mo, 10 February 2020 | v12.1.7 # Su, 16 February 2020 | v12.1.8
Not translated yet, please navigate to the [english version](HISTORY.md#mo-10-february-2020--v1217) instead. Not translated yet, please navigate to the [english version](HISTORY.md#su-16-february-2020--v1218) instead.
# Sábado, 26 de octubre 2019 | v12.0.0 # Sábado, 26 de octubre 2019 | v12.0.0

View File

@ -1,10 +1,11 @@
<!-- # News # News
![](https://iris-go.com/images/release.png) Iris version **12.1.7** has been [released](HISTORY.md#mo-10-february-2020--v1217)! ![](https://iris-go.com/images/release.png) Iris version **12.1.8** has been [released](HISTORY.md#su-16-february-2020--v1218)!
![](https://iris-go.com/images/cli.png) The official [Iris Command Line Interface](https://github.com/kataras/iris-cli) will soon be near you in 2020! ![](https://iris-go.com/images/cli.png) The official [Iris Command Line Interface](https://github.com/kataras/iris-cli) will soon be near you in 2020!
![](https://iris-go.com/images/sponsor.png) Support your favorite web framework through [Github Sponsors Program](https://github.com/sponsors/kataras)! --> ![](https://iris-go.com/images/sponsor.png) Support your favorite web framework through [Github Sponsors Program](https://github.com/sponsors/kataras)!
# Iris Web Framework <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_KO.md"><img width="20px" src="https://iris-go.com/images/flag-south-korea.svg" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg" /></a> # Iris Web Framework <a href="README_ZH.md"><img width="20px" src="https://iris-go.com/images/flag-china.svg?v=10" /></a> <a href="README_GR.md"><img width="20px" src="https://iris-go.com/images/flag-greece.svg?v=10" /></a> <a href="README_ES.md"><img width="20px" src="https://iris-go.com/images/flag-spain.png" /></a> <a href="README_KO.md"><img width="20px" src="https://iris-go.com/images/flag-south-korea.svg" /></a> <a href="README_FA.md"><img width="20px" src="https://iris-go.com/images/flag-iran.svg" /></a> <a href="README_RU.md"><img width="20px" src="https://iris-go.com/images/flag-russia.svg" /></a>
[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://godoc.org/github.com/kataras/iris)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://www.paypal.me/kataras)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) --> [![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://godoc.org/github.com/kataras/iris)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://www.paypal.me/kataras)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->

View File

@ -1 +1 @@
12.1.7:https://github.com/kataras/iris/releases/tag/v12.1.7 12.1.8:https://github.com/kataras/iris/releases/tag/v12.1.8

View File

@ -3,6 +3,6 @@ module app
go 1.13 go 1.13
require ( require (
github.com/kataras/iris/v12 v12.1.7 github.com/kataras/iris/v12 v12.1.8
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
) )

View File

@ -31,11 +31,19 @@ func newApp() *iris.Application {
return ctx.Request().Context() return ctx.Request().Context()
}). }).
// Bind loginRequest. // Bind loginRequest.
Register(func(ctx iris.Context) loginRequest { // Register(func(ctx iris.Context) loginRequest {
var req loginRequest // var req loginRequest
ctx.ReadJSON(&req) // ctx.ReadJSON(&req)
return req // return req
}). // }).
// OR
// Bind any other structure or pointer to a structure from request's
// XML
// YAML
// Query
// Form
// JSON (default, if not client's "Content-Type" specified otherwise)
Register(mvc.AutoBinding).
Handle(&myController{}) Handle(&myController{})
return app return app

View File

@ -6,7 +6,7 @@ import (
"github.com/kataras/iris/v12/httptest" "github.com/kataras/iris/v12/httptest"
) )
func TestBindContextContext(t *testing.T) { func TestGRPCCompatible(t *testing.T) {
app := newApp() app := newApp()
e := httptest.New(t, app) e := httptest.New(t, app)

View File

@ -215,9 +215,9 @@ type bintree struct {
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"templates": &bintree{nil, map[string]*bintree{ "templates": {nil, map[string]*bintree{
"index.pug": &bintree{templatesIndexPug, map[string]*bintree{}}, "index.pug": {templatesIndexPug, map[string]*bintree{}},
"layout.pug": &bintree{templatesLayoutPug, map[string]*bintree{}}, "layout.pug": {templatesLayoutPug, map[string]*bintree{}},
}}, }},
}} }}

2
doc.go
View File

@ -38,7 +38,7 @@ Source code and other details for the project are available at GitHub:
Current Version Current Version
12.1.7 12.1.8
Installation Installation

View File

@ -1,43 +0,0 @@
package hero
import (
"reflect"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/hero/di"
)
func init() {
di.DefaultHijacker = func(fieldOrFuncInput reflect.Type) (*di.BindObject, bool) {
if !IsContext(fieldOrFuncInput) {
return nil, false
}
// this is being used on both func injector and struct injector.
// 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
// which is used as slice of reflect.Value, because of the final method's `Call`.
return &di.BindObject{
Type: contextTyp,
BindType: di.Dynamic,
ReturnValue: func(ctx context.Context) reflect.Value {
return ctx.ReflectValue()[0]
},
}, true
}
di.DefaultTypeChecker = func(fn reflect.Type) bool {
// valid if that single input arg is a typeof context.Context
// or first argument is context.Context and second argument is a variadic, which is ignored (i.e new sessions#Start).
return (fn.NumIn() == 1 || (fn.NumIn() == 2 && fn.IsVariadic())) && IsContext(fn.In(0))
}
di.DefaultErrorHandler = di.ErrorHandlerFunc(func(ctx context.Context, err error) {
if err == nil {
return
}
ctx.StatusCode(400)
ctx.WriteString(err.Error())
ctx.StopExecution()
})
}

View File

@ -9,12 +9,6 @@ import (
) )
type ( type (
// Hijacker is a type which is used to catch fields or function's input argument
// to bind a custom object based on their type.
Hijacker func(reflect.Type) (*BindObject, bool)
// TypeChecker checks if a specific field's or function input argument's
// is valid to be binded.
TypeChecker func(reflect.Type) bool
// ErrorHandler is the optional interface to handle errors per hero func, // ErrorHandler is the optional interface to handle errors per hero func,
// see `mvc/Application#HandleError` for MVC application-level error handler registration too. // see `mvc/Application#HandleError` for MVC application-level error handler registration too.
// //
@ -34,15 +28,51 @@ func (fn ErrorHandlerFunc) HandleError(ctx context.Context, err error) {
fn(ctx, err) fn(ctx, err)
} }
var ( // DefaultErrorHandler is the default error handler will be fired on
// DefaultHijacker is the hijacker used on the package-level Struct & Func functions. // any error from registering a request-scoped dynamic dependency and on a controller's method failure.
DefaultHijacker Hijacker var DefaultErrorHandler ErrorHandler = ErrorHandlerFunc(func(ctx context.Context, err error) {
// DefaultTypeChecker is the typechecker used on the package-level Struct & Func functions. if err == nil {
DefaultTypeChecker TypeChecker return
// DefaultErrorHandler is the error handler used on the package-level `Func` function }
// to catch any errors from dependencies or handlers.
DefaultErrorHandler ErrorHandler ctx.StatusCode(400)
) ctx.WriteString(err.Error())
ctx.StopExecution()
})
var emptyValue reflect.Value
// DefaultFallbackBinder used to bind any oprhan inputs. Its error is handled by the `ErrorHandler`.
var DefaultFallbackBinder FallbackBinder = func(ctx context.Context, input OrphanInput) (newValue reflect.Value, err error) {
wasPtr := input.Type.Kind() == reflect.Ptr
newValue = reflect.New(IndirectType(input.Type))
ptr := newValue.Interface()
switch ctx.GetContentTypeRequested() {
case context.ContentXMLHeaderValue:
err = ctx.ReadXML(ptr)
case context.ContentYAMLHeaderValue:
err = ctx.ReadYAML(ptr)
case context.ContentFormHeaderValue:
err = ctx.ReadQuery(ptr)
case context.ContentFormMultipartHeaderValue:
err = ctx.ReadForm(ptr)
default:
err = ctx.ReadJSON(ptr)
// json
}
// if err != nil {
// return emptyValue, err
// }
if !wasPtr {
newValue = newValue.Elem()
}
return newValue, err
}
// Struct is being used to return a new injector based on // Struct is being used to return a new injector based on
// a struct value instance, if it contains fields that the types of those // a struct value instance, if it contains fields that the types of those
@ -55,8 +85,6 @@ func Struct(s interface{}, values ...reflect.Value) *StructInjector {
return MakeStructInjector( return MakeStructInjector(
ValueOf(s), ValueOf(s),
DefaultHijacker,
DefaultTypeChecker,
SortByNumMethods, SortByNumMethods,
Values(values).CloneWithFieldsOf(s)..., Values(values).CloneWithFieldsOf(s)...,
) )
@ -74,9 +102,6 @@ func Func(fn interface{}, values ...reflect.Value) *FuncInjector {
return MakeFuncInjector( return MakeFuncInjector(
ValueOf(fn), ValueOf(fn),
DefaultHijacker,
DefaultTypeChecker,
DefaultErrorHandler,
values..., values...,
) )
} }
@ -88,28 +113,34 @@ func Func(fn interface{}, values ...reflect.Value) *FuncInjector {
type D struct { type D struct {
Values Values
hijacker Hijacker fallbackBinder FallbackBinder
goodFunc TypeChecker errorHandler ErrorHandler
errorHandler ErrorHandler sorter Sorter
sorter Sorter
} }
// OrphanInput represents an input without registered dependency.
// Used to help the framework (or the caller) auto-resolve it by the request.
type OrphanInput struct {
// Index int // function or struct field index.
Type reflect.Type
}
// FallbackBinder represents a handler of oprhan input values, handler's input arguments or controller's fields.
type FallbackBinder func(ctx context.Context, input OrphanInput) (reflect.Value, error)
// New creates and returns a new Dependency Injection container. // New creates and returns a new Dependency Injection container.
// See `Values` field and `Func` and `Struct` methods for more. // See `Values` field and `Func` and `Struct` methods for more.
func New() *D { func New() *D {
return &D{} return &D{
errorHandler: DefaultErrorHandler,
fallbackBinder: DefaultFallbackBinder,
}
} }
// Hijack sets a hijacker function, read the `Hijacker` type for more explanation. // FallbackBinder adds a binder which will handle any oprhan input values.
func (d *D) Hijack(fn Hijacker) *D { // See `FallbackBinder` type.
d.hijacker = fn func (d *D) FallbackBinder(fallbackBinder FallbackBinder) *D {
return d d.fallbackBinder = fallbackBinder
}
// GoodFunc sets a type checker for a valid function that can be binded,
// read the `TypeChecker` type for more explanation.
func (d *D) GoodFunc(fn TypeChecker) *D {
d.goodFunc = fn
return d return d
} }
@ -130,11 +161,10 @@ func (d *D) Sort(with Sorter) *D {
// parent's (current "D") hijacker, good func type checker, sorter and all dependencies values. // parent's (current "D") hijacker, good func type checker, sorter and all dependencies values.
func (d *D) Clone() *D { func (d *D) Clone() *D {
return &D{ return &D{
Values: d.Values.Clone(), Values: d.Values.Clone(),
hijacker: d.hijacker, fallbackBinder: d.fallbackBinder,
goodFunc: d.goodFunc, errorHandler: d.errorHandler,
errorHandler: d.errorHandler, sorter: d.sorter,
sorter: d.sorter,
} }
} }
@ -144,16 +174,19 @@ func (d *D) Clone() *D {
// with the injector's `Inject` and `InjectElem` methods. // with the injector's `Inject` and `InjectElem` methods.
func (d *D) Struct(s interface{}) *StructInjector { func (d *D) Struct(s interface{}) *StructInjector {
if s == nil { if s == nil {
return &StructInjector{Has: false} return &StructInjector{}
} }
return MakeStructInjector( injector := MakeStructInjector(
ValueOf(s), ValueOf(s),
d.hijacker,
d.goodFunc,
d.sorter, d.sorter,
d.Values.CloneWithFieldsOf(s)..., d.Values.CloneWithFieldsOf(s)...,
) )
injector.ErrorHandler = d.errorHandler
injector.FallbackBinder = d.fallbackBinder
return injector
} }
// Func is being used to return a new injector based on // Func is being used to return a new injector based on
@ -163,14 +196,16 @@ func (d *D) Struct(s interface{}) *StructInjector {
// with the injector's `Inject` method. // with the injector's `Inject` method.
func (d *D) Func(fn interface{}) *FuncInjector { func (d *D) Func(fn interface{}) *FuncInjector {
if fn == nil { if fn == nil {
return &FuncInjector{Has: false} return &FuncInjector{}
} }
return MakeFuncInjector( injector := MakeFuncInjector(
ValueOf(fn), ValueOf(fn),
d.hijacker,
d.goodFunc,
d.errorHandler,
d.Values..., d.Values...,
).ErrorHandler(d.errorHandler) )
injector.ErrorHandler = d.errorHandler
injector.FallbackBinder = d.fallbackBinder
return injector
} }

View File

@ -18,10 +18,10 @@ type (
FuncInjector struct { FuncInjector struct {
// the original function, is being used // the original function, is being used
// only the .Call, which is referring to the same function, always. // only the .Call, which is referring to the same function, always.
fn reflect.Value fn reflect.Value
typ reflect.Type typ reflect.Type
goodFunc TypeChecker FallbackBinder FallbackBinder
errorHandler ErrorHandler ErrorHandler ErrorHandler
inputs []*targetFuncInput inputs []*targetFuncInput
// Length is the number of the valid, final binded input arguments. // Length is the number of the valid, final binded input arguments.
@ -51,13 +51,13 @@ func (s *FuncInjector) miss(index int, remaining Values) {
// that the caller should use to bind input arguments of the "fn" function. // that the caller should use to bind input arguments of the "fn" function.
// //
// The hijack and the goodFunc are optional, the "values" is the dependencies collection. // The hijack and the goodFunc are optional, the "values" is the dependencies collection.
func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, errorHandler ErrorHandler, values ...reflect.Value) *FuncInjector { func MakeFuncInjector(fn reflect.Value, values ...reflect.Value) *FuncInjector {
typ := IndirectType(fn.Type()) typ := IndirectType(fn.Type())
s := &FuncInjector{ s := &FuncInjector{
fn: fn, fn: fn,
typ: typ, typ: typ,
goodFunc: goodFunc, FallbackBinder: DefaultFallbackBinder,
errorHandler: errorHandler, ErrorHandler: DefaultErrorHandler,
} }
if !IsFunc(typ) { if !IsFunc(typ) {
@ -71,16 +71,12 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, e
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
inTyp := typ.In(i) inTyp := typ.In(i)
if hijack != nil { if b, ok := tryBindContext(inTyp); ok {
b, ok := hijack(inTyp) s.inputs = append(s.inputs, &targetFuncInput{
InputIndex: i,
if ok && b != nil { Object: b,
s.inputs = append(s.inputs, &targetFuncInput{ })
InputIndex: i, continue
Object: b,
})
continue
}
} }
matched := false matched := false
@ -98,6 +94,50 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, e
break break
} }
// TODO: (already working on it) clean up or even re-write the whole di, hero and some of the mvc,
// this is a dirty but working-solution for #1449.
// Limitations:
// - last input argument
// - not able to customize it other than DefaultFallbackBinder on MVC (on hero it can be customized)
// - the "di" package is now depends on context package which is not an import-cycle issue, it's not imported there.
if i == n-1 {
if v.Type() == autoBindingTyp && s.FallbackBinder != nil {
canFallback := true
if k := inTyp.Kind(); k == reflect.Ptr {
if inTyp.Elem().Kind() != reflect.Struct {
canFallback = false
}
} else if k != reflect.Struct {
canFallback = false
}
if canFallback {
matched = true
s.inputs = append(s.inputs, &targetFuncInput{
InputIndex: i,
Object: &BindObject{
Type: inTyp,
BindType: Dynamic,
ReturnValue: func(ctx context.Context) reflect.Value {
value, err := s.FallbackBinder(ctx, OrphanInput{Type: inTyp})
if err != nil {
if s.ErrorHandler != nil {
s.ErrorHandler.HandleError(ctx, err)
}
}
return value
},
},
})
break
}
}
}
} }
if !matched { if !matched {
@ -108,7 +148,6 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, e
// with different set of binding "values". // with different set of binding "values".
s.miss(i, values) // send the remaining dependencies values. s.miss(i, values) // send the remaining dependencies values.
} }
} }
return s return s
@ -119,6 +158,11 @@ func (s *FuncInjector) refresh() {
s.Has = s.Length > 0 s.Has = s.Length > 0
} }
// AutoBindingValue a fake type to expliclty set the return value of hero.AutoBinding.
type AutoBindingValue struct{}
var autoBindingTyp = reflect.TypeOf(AutoBindingValue{})
func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool { func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
defer s.refresh() defer s.refresh()
@ -129,31 +173,14 @@ func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
inTyp := s.typ.In(inputIndex) inTyp := s.typ.In(inputIndex)
// the binded values to the func's inputs. // the binded values to the func's inputs.
b, err := MakeBindObject(value, s.goodFunc, s.errorHandler) b, err := MakeBindObject(value, s.ErrorHandler)
if err != nil { if err != nil {
return false return false
} }
// TODO: expose that (need to push a fix for issue #1450 first)
if b.Type == reflectValueType {
b.Type = inTyp
// returnValue := b.ReturnValue
b.ReturnValue = func(ctx context.Context) reflect.Value {
newValue := reflect.New(inTyp)
if err := ctx.ReadJSON(newValue.Interface()); err != nil {
if s.errorHandler != nil {
s.errorHandler.HandleError(ctx, err)
}
}
return newValue.Elem()
}
}
if b.IsAssignable(inTyp) { if b.IsAssignable(inTyp) {
// fmt.Printf("binded input index: %d for type: %s and value: %v with pointer: %v\n", // fmt.Printf("binded input index: %d for type: %s and value: %v with dependency: %v\n",
// i, b.Type.String(), inTyp.String(), inTyp.Pointer()) // inputIndex, b.Type.String(), inTyp.String(), b)
s.inputs = append(s.inputs, &targetFuncInput{ s.inputs = append(s.inputs, &targetFuncInput{
InputIndex: inputIndex, InputIndex: inputIndex,
Object: &b, Object: &b,
@ -164,12 +191,6 @@ func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
return false return false
} }
// ErrorHandler registers an error handler for this FuncInjector.
func (s *FuncInjector) ErrorHandler(errorHandler ErrorHandler) *FuncInjector {
s.errorHandler = errorHandler
return s
}
// Retry used to add missing dependencies, i.e path parameter builtin bindings if not already exists // Retry used to add missing dependencies, i.e path parameter builtin bindings if not already exists
// in the `hero.Handler`, once, only for that func injector. // in the `hero.Handler`, once, only for that func injector.
func (s *FuncInjector) Retry(retryFn func(inIndex int, inTyp reflect.Type, remainingValues Values) (reflect.Value, bool)) bool { func (s *FuncInjector) Retry(retryFn func(inIndex int, inTyp reflect.Type, remainingValues Values) (reflect.Value, bool)) bool {

View File

@ -45,10 +45,10 @@ type BindObject struct {
// or the input arguments (if "v.elem()" is func) // or the input arguments (if "v.elem()" is func)
// are valid to be included as the final object's dependencies, even if the caller added more // are valid to be included as the final object's dependencies, even if the caller added more
// the "di" is smart enough to select what each "v" needs and what not before serve time. // the "di" is smart enough to select what each "v" needs and what not before serve time.
func MakeBindObject(v reflect.Value, goodFunc TypeChecker, errorHandler ErrorHandler) (b BindObject, err error) { func MakeBindObject(v reflect.Value, errorHandler ErrorHandler) (b BindObject, err error) {
if IsFunc(v) { if IsFunc(v) {
b.BindType = Dynamic b.BindType = Dynamic
b.ReturnValue, b.Type, err = MakeReturnValue(v, goodFunc, errorHandler) b.ReturnValue, b.Type, err = MakeReturnValue(v, errorHandler)
} else { } else {
b.BindType = Static b.BindType = Static
b.Type = v.Type() b.Type = v.Type()
@ -58,6 +58,23 @@ func MakeBindObject(v reflect.Value, goodFunc TypeChecker, errorHandler ErrorHan
return return
} }
func tryBindContext(fieldOrFuncInput reflect.Type) (*BindObject, bool) {
if !IsContext(fieldOrFuncInput) {
return nil, false
}
// this is being used on both func injector and struct injector.
// 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
// which is used as slice of reflect.Value, because of the final method's `Call`.
return &BindObject{
Type: contextTyp,
BindType: Dynamic,
ReturnValue: func(ctx context.Context) reflect.Value {
return ctx.ReflectValue()[0]
},
}, true
}
var errBad = errors.New("bad") var errBad = errors.New("bad")
// MakeReturnValue takes any function // MakeReturnValue takes any function
@ -71,7 +88,7 @@ var errBad = errors.New("bad")
// //
// The return type of the "fn" should be a value instance, not a pointer. // The return type of the "fn" should be a value instance, not a pointer.
// The binder function should return just one value. // The binder function should return just one value.
func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker, errorHandler ErrorHandler) (func(ctx context.Context) reflect.Value, reflect.Type, error) { func MakeReturnValue(fn reflect.Value, errorHandler ErrorHandler) (func(context.Context) reflect.Value, reflect.Type, error) {
typ := IndirectType(fn.Type()) typ := IndirectType(fn.Type())
// invalid if not a func. // invalid if not a func.
@ -86,10 +103,8 @@ func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker, errorHandler ErrorH
return nil, typ, errBad return nil, typ, errBad
} }
if goodFunc != nil { if !goodFunc(typ) {
if !goodFunc(typ) { return nil, typ, errBad
return nil, typ, errBad
}
} }
firstOutTyp := typ.Out(0) firstOutTyp := typ.Out(0)
@ -114,17 +129,6 @@ func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker, errorHandler ErrorH
return firstZeroOutVal return firstZeroOutVal
} }
// if firstOutTyp == reflectValueType {
// converted := v.Convert(typ.In(0))
// fmt.Printf("object.go#124: converted: %#+v\n", converted)
// return converted //reflect.ValueOf(v.Interface())
// }
// if v.String() == "<interface {} Value>" {
// println("di/object.go: " + v.String())
// // println("di/object.go: because it's interface{} it should be returned as: " + v.Elem().Type().String() + " and its value: " + v.Elem().Interface().(string))
// return v.Elem()
// }
return v return v
} }

View File

@ -2,6 +2,8 @@ package di
import ( import (
"reflect" "reflect"
"github.com/kataras/iris/v12/context"
) )
// EmptyIn is just an empty slice of reflect.Value. // EmptyIn is just an empty slice of reflect.Value.
@ -131,6 +133,19 @@ func goodVal(v reflect.Value) bool {
return v.IsValid() return v.IsValid()
} }
var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem()
// IsContext returns true if the "inTyp" is a type of Context.
func IsContext(inTyp reflect.Type) bool {
return inTyp.Implements(contextTyp)
}
func goodFunc(fn reflect.Type) bool {
// valid if that single input arg is a typeof context.Context
// or first argument is context.Context and second argument is a variadic, which is ignored (i.e new sessions#Start).
return (fn.NumIn() == 1 || (fn.NumIn() == 2 && fn.IsVariadic())) && IsContext(fn.In(0))
}
// IsFunc returns true if the passed type is function. // IsFunc returns true if the passed type is function.
func IsFunc(kindable interface { func IsFunc(kindable interface {
Kind() reflect.Kind Kind() reflect.Kind

View File

@ -61,6 +61,9 @@ type (
Has bool Has bool
CanInject bool // if any bindable fields when the state is NOT singleton. CanInject bool // if any bindable fields when the state is NOT singleton.
Scope Scope Scope Scope
FallbackBinder FallbackBinder
ErrorHandler ErrorHandler
} }
) )
@ -103,11 +106,13 @@ var SortByNumMethods Sorter = func(t1 reflect.Type, t2 reflect.Type) bool {
// of the "v" struct value or pointer. // of the "v" struct value or pointer.
// //
// The hijack and the goodFunc are optional, the "values" is the dependencies collection. // The hijack and the goodFunc are optional, the "values" is the dependencies collection.
func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, sorter Sorter, values ...reflect.Value) *StructInjector { func MakeStructInjector(v reflect.Value, sorter Sorter, values ...reflect.Value) *StructInjector {
s := &StructInjector{ s := &StructInjector{
initRef: v, initRef: v,
initRefAsSlice: []reflect.Value{v}, initRefAsSlice: []reflect.Value{v},
elemType: IndirectType(v.Type()), elemType: IndirectType(v.Type()),
FallbackBinder: DefaultFallbackBinder,
ErrorHandler: DefaultErrorHandler,
} }
// Optionally check and keep good values only here, // Optionally check and keep good values only here,
@ -138,15 +143,12 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
for _, f := range fields { for _, f := range fields {
// fmt.Printf("[%d] field type [%s] value name [%s]\n", idx, f.Type.String(), f.Name) // fmt.Printf("[%d] field type [%s] value name [%s]\n", idx, f.Type.String(), f.Name)
if hijack != nil { if b, ok := tryBindContext(f.Type); ok {
if b, ok := hijack(f.Type); ok && b != nil { s.fields = append(s.fields, &targetStructField{
s.fields = append(s.fields, &targetStructField{ FieldIndex: f.Index,
FieldIndex: f.Index, Object: b,
Object: b, })
}) continue
continue
}
} }
var possibleValues []*targetStructField var possibleValues []*targetStructField
@ -157,9 +159,10 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
} }
// the binded values to the struct's fields. // the binded values to the struct's fields.
b, err := MakeBindObject(val, goodFunc, nil) b, err := MakeBindObject(val, nil)
if err != nil { if err != nil {
return s // if error stop here. panic(err)
// return s // if error stop here.
} }
if b.IsAssignable(f.Type) { if b.IsAssignable(f.Type) {

View File

@ -11,31 +11,17 @@ import (
"github.com/kataras/golog" "github.com/kataras/golog"
) )
var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem() // var genericFuncTyp = reflect.TypeOf(func(context.Context) reflect.Value { return reflect.Value{} })
// IsContext returns true if the "inTyp" is a type of Context. // // IsGenericFunc reports whether the "inTyp" is a type of func(Context) interface{}.
func IsContext(inTyp reflect.Type) bool { // func IsGenericFunc(inTyp reflect.Type) bool {
return inTyp.Implements(contextTyp) // return inTyp == genericFuncTyp
} // }
// var genericFuncTyp = reflect.TypeOf(func(context.Context) interface{} { return nil })
var genericFuncTyp = reflect.TypeOf(func(context.Context) reflect.Value { return reflect.Value{} })
// IsGenericFunc reports whether the "inTyp" is a type of func(Context) interface{}.
func IsGenericFunc(inTyp reflect.Type) bool {
return inTyp == genericFuncTyp
}
// checks if "handler" is context.Handler: func(context.Context). // checks if "handler" is context.Handler: func(context.Context).
func isContextHandler(handler interface{}) (context.Handler, bool) { func isContextHandler(handler interface{}) (context.Handler, bool) {
h, is := handler.(context.Handler) h, ok := handler.(context.Handler)
if !is { return h, ok
fh, is := handler.(func(context.Context))
if is {
return fh, is
}
}
return h, is
} }
func validateHandler(handler interface{}) error { func validateHandler(handler interface{}) error {
@ -72,7 +58,7 @@ func makeHandler(handler interface{}, errorHandler di.ErrorHandler, values ...re
} }
funcInjector := di.Func(fn, values...) funcInjector := di.Func(fn, values...)
funcInjector.ErrorHandler(errorHandler) funcInjector.ErrorHandler = errorHandler
valid := funcInjector.Length == n valid := funcInjector.Length == n

View File

@ -4,7 +4,6 @@ package hero_test
import ( import (
"fmt" "fmt"
"reflect"
"testing" "testing"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
@ -128,37 +127,23 @@ func TestBindFunctionAsFunctionInputArgument(t *testing.T) {
Expect().Status(iris.StatusOK).Body().Equal(expectedUsername) Expect().Status(iris.StatusOK).Body().Equal(expectedUsername)
} }
func TestBindReflectValue(t *testing.T) { func TestAutoBinding(t *testing.T) {
// TODO: THINK of simplify this,
// as 'hero' and 'mvc' are not depend on the root kataras/iris/v12 package, smart decision back then.
// e.g.
// app := iris.New()
// app.RegisterDependency(...)
// app.HandleFunc("GET POST", "/", func(input MyInput) MyOutput {})
// instead of:
// app := iris.New()
// h := hero.New()
// h.Register(...) or hero.Register for shared deps across Iris different applications.
// handler := h.Handler(func(input MyInput) MyOutput {})
// app.HandleMany("GET POST", "/", handler)
h := New() h := New()
h.Register(func(ctx iris.Context) reflect.Value { h.Register(AutoBinding)
var v interface{}
err := ctx.ReadJSON(&v) postHandler := h.Handler(func(input *testUserStruct /* ptr */) string {
if err != nil { return input.Username
t.Fatal(err)
}
return reflect.ValueOf(v)
// return reflect.Value{}
}) })
postHandler := h.Handler(func(input testUserStruct) string {
postHandler2 := h.Handler(func(input testUserStruct) string {
return input.Username return input.Username
}) })
app := iris.New() app := iris.New()
app.Post("/", postHandler) app.Post("/", postHandler)
app.Post("/2", postHandler2)
e := httptest.New(t, app) e := httptest.New(t, app)
e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().Equal("makis") e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().Equal("makis")
e.POST("/2").WithJSON(iris.Map{"username": "kataras"}).Expect().Status(httptest.StatusOK).Body().Equal("kataras")
} }

View File

@ -48,6 +48,12 @@ func (h *Hero) Dependencies() *di.Values {
return &h.values return &h.values
} }
// AutoBinding used to be registered as dependency to try to automatically
// map and bind the inputs that are not already binded with a dependency.
//
// See `DefaultFallbackBinder`.
var AutoBinding = di.AutoBindingValue{}
// Register adds one or more values as dependencies. // Register adds one or more values as dependencies.
// The value can be a single struct value-instance or a function // The value can be a single struct value-instance or a function
// which has one input and one output, the input should be // which has one input and one output, the input should be

View File

@ -41,7 +41,7 @@ import (
) )
// Version is the current version number of the Iris Web Framework. // Version is the current version number of the Iris Web Framework.
const Version = "12.1.7" const Version = "12.1.8"
// HTTP status codes as registered with IANA. // HTTP status codes as registered with IANA.
// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml. // See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml.

View File

@ -396,8 +396,6 @@ func (c *ControllerActivator) attachInjector() {
if c.injector == nil { if c.injector == nil {
c.injector = di.MakeStructInjector( c.injector = di.MakeStructInjector(
di.ValueOf(c.Value), di.ValueOf(c.Value),
di.DefaultHijacker,
di.DefaultTypeChecker,
c.sorter, c.sorter,
di.Values(c.dependencies).CloneWithFieldsOf(c.Value)..., di.Values(c.dependencies).CloneWithFieldsOf(c.Value)...,
) )
@ -426,7 +424,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
// fmt.Printf("for %s | values: %s\n", funcName, funcDependencies) // fmt.Printf("for %s | values: %s\n", funcName, funcDependencies)
funcInjector := di.Func(m.Func, funcDependencies...) funcInjector := di.Func(m.Func, funcDependencies...)
funcInjector.ErrorHandler(c.errorHandler) funcInjector.ErrorHandler = c.errorHandler
// fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length) // fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length)
if funcInjector.Has { if funcInjector.Has {
@ -492,10 +490,6 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
return // stop as soon as possible, although it would stop later on if `ctx.StopExecution` called. return // stop as soon as possible, although it would stop later on if `ctx.StopExecution` called.
} }
// for idxx, inn := range in {
// println("controller.go: execution: in.Value = "+inn.String()+" and in.Type = "+inn.Type().Kind().String()+" of index: ", idxx)
// }
hero.DispatchFuncResult(ctx, errorHandler, call(in)) hero.DispatchFuncResult(ctx, errorHandler, call(in))
return return
} }

View File

@ -104,6 +104,12 @@ func (app *Application) Configure(configurators ...func(*Application)) *Applicat
return app return app
} }
// AutoBinding used to be registered as dependency to try to automatically
// map and bind the inputs that are not already binded with a dependency.
//
// A shortcut of `hero.AutoBinding`. Read more at: `hero#DefaultFallbackBinder`.
var AutoBinding = hero.AutoBinding
// Register appends one or more values as dependencies. // Register appends one or more values as dependencies.
// The value can be a single struct value-instance or a function // The value can be a single struct value-instance or a function
// which has one input and one output, the input should be // which has one input and one output, the input should be