mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
Update to 8.3.3 | Even better MVC. Read HISTORY.md
Former-commit-id: 88998c317117abff1a214cf396b205d8d8ea888c
This commit is contained in:
parent
e12513a534
commit
1ffe4479f7
22
HISTORY.md
22
HISTORY.md
|
@ -19,8 +19,22 @@ 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 -u github.com/kataras/iris`.
|
||||
|
||||
|
||||
# Tu, 22 August 2017 | v8.3.2
|
||||
# We, 23 August 2017 | v8.3.3
|
||||
|
||||
Better debug messages when using MVC.
|
||||
|
||||
Add support for recursively binding and **custom controllers embedded to other custom controller**, that's the new feature. That simply means that Iris users are able to use "shared" controllers everywhere; when binding, using models, get/set persistence data, adding middleware, intercept request flow.
|
||||
|
||||
This will help web authors to split the logic at different controllers. Those controllers can be also used as "standalone" to serve a page somewhere else in the application as well.
|
||||
|
||||
My personal advice to you is to always organize and split your code nicely and wisely in order to avoid using such as an advanced MVC feature, at least any time soon.
|
||||
|
||||
I'm aware that this is not always an easy task to do, therefore is here if you ever need it :)
|
||||
|
||||
A ridiculous simple example of this feature can be found at the [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go#L424) file.
|
||||
|
||||
|
||||
# Tu, 22 August 2017 | v8.3.2
|
||||
|
||||
### MVC
|
||||
|
||||
|
@ -48,11 +62,11 @@ app.Controller(new(ProfileController), checkLogin)
|
|||
// [...]
|
||||
```
|
||||
|
||||
Usage of these kind of MVC features could be found at the [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go#L174) source file.
|
||||
Usage of these kind of MVC features could be found at the [mvc/controller_test.go](https://github.com/kataras/iris/blob/master/mvc/controller_test.go#L174) file.
|
||||
|
||||
### Other minor enhancements
|
||||
|
||||
- fix https://github.com/kataras/iris/issues/726[*](https://github.com/kataras/iris/commit/5e435fc54fe3dbf95308327c2180d1b444ef7e0d)
|
||||
- fix issue [#726](https://github.com/kataras/iris/issues/726)[*](https://github.com/kataras/iris/commit/5e435fc54fe3dbf95308327c2180d1b444ef7e0d)
|
||||
- fix redis sessiondb expiration[*](https://github.com/kataras/iris/commit/85cfc91544c981e87e09c5aa86bad4b85d0b96d3)
|
||||
- update recursively when new version is available[*](https://github.com/kataras/iris/commit/cd3c223536c6a33653a7fcf1f0648123f2b968fd)
|
||||
- some minor session enhancements[*](https://github.com/kataras/iris/commit/2830f3b50ee9c526ac792c3ce1ec1c08c24ea024)
|
||||
|
@ -336,7 +350,7 @@ useful to call middlewares or when many methods use the same collection of data.
|
|||
|
||||
Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
|
||||
|
||||
Inheritance, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
|
||||
Inheritance, recursively, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
|
||||
and it adds its logic to its `BeginRequest`, [here](https://github.com/kataras/iris/blob/master/mvc/session_controller.go).
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ Iris may have reached version 8, but we're not stopping there. We have many feat
|
|||
### 📑 Table of contents
|
||||
|
||||
* [Installation](#-installation)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-22-august-2017--v832)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#we-23-august-2017--v833)
|
||||
* [Learn](#-learn)
|
||||
* [HTTP Listening](_examples/#http-listening)
|
||||
* [Configuration](_examples/#configuration)
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
8.3.2:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-22-august-2017--v832
|
||||
8.3.3:https://github.com/kataras/iris/blob/master/HISTORY.md#we-23-august-2017--v833
|
|
@ -33,7 +33,6 @@ It doesn't always contain the "best ways" but it does cover each important featu
|
|||
* [using the `RegisterOnInterrupt`](http-listening/graceful-shutdown/default-notifier/main.go)
|
||||
* [using a custom notifier](http-listening/graceful-shutdown/custom-notifier/main.go)
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
- [Functional](configuration/functional/main.go)
|
||||
|
@ -41,7 +40,6 @@ It doesn't always contain the "best ways" but it does cover each important featu
|
|||
- [Import from YAML file](configuration/from-yaml-file/main.go)
|
||||
- [Import from TOML file](configuration/from-toml-file/main.go)
|
||||
|
||||
|
||||
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context
|
||||
|
||||
* `app.Get("{userid:int min(1)}", myHandler)`
|
||||
|
|
|
@ -77,11 +77,15 @@ type HelloController struct {
|
|||
mvc.Controller
|
||||
}
|
||||
|
||||
type myJSONData struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Get serves
|
||||
// Method: GET
|
||||
// Resource: http://localhost:8080/hello
|
||||
func (c *HelloController) Get() {
|
||||
c.Ctx.JSON(iris.Map{"message": "Hello iris web framework."})
|
||||
c.Ctx.JSON(myJSONData{"Hello iris web framework."})
|
||||
}
|
||||
|
||||
/* Can use more than one, the factory will make sure
|
||||
|
|
4
doc.go
4
doc.go
|
@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
|
|||
|
||||
Current Version
|
||||
|
||||
8.3.2
|
||||
8.3.3
|
||||
|
||||
Installation
|
||||
|
||||
|
@ -820,7 +820,7 @@ useful to call middlewares or when many methods use the same collection of data.
|
|||
|
||||
Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
|
||||
|
||||
Inheritance, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
|
||||
Inheritance, recursively, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
|
||||
and it adds its logic to its `BeginRequest`. Source file: https://github.com/kataras/iris/blob/master/mvc/session_controller.go.
|
||||
|
||||
|
||||
|
|
2
iris.go
2
iris.go
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
const (
|
||||
// Version is the current version number of the Iris Web Framework.
|
||||
Version = "8.3.2"
|
||||
Version = "8.3.3"
|
||||
)
|
||||
|
||||
// HTTP status codes as registered with IANA.
|
||||
|
|
|
@ -151,8 +151,8 @@ func ActivateController(base BaseController, bindValues []interface{},
|
|||
binder := newBinder(typ.Elem(), bindValues)
|
||||
if binder != nil {
|
||||
for _, bf := range binder.fields {
|
||||
golog.Debugf("MVC %s: binder loaded for '%s' with field index of: %d",
|
||||
ctrlName, bf.Name, bf.Index)
|
||||
golog.Debugf("MVC %s: binder loaded for '%s' with value:\n%#v",
|
||||
ctrlName, bf.getFullName(), bf.getValue())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,79 +66,19 @@ func (b *binder) lookup(elem reflect.Type) (fields []field) {
|
|||
continue
|
||||
}
|
||||
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if elemField.Type == value.Type() {
|
||||
// we area inside the correct type
|
||||
// println("[0] prepare bind filed for " + elemField.Name)
|
||||
fields = append(fields, field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
Value: value,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
f := lookupStruct(elemField.Type, value)
|
||||
if f != nil {
|
||||
fields = append(fields, field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
embedded: f,
|
||||
})
|
||||
}
|
||||
|
||||
matcher := func(elemField reflect.StructField) bool {
|
||||
return elemField.Type == value.Type()
|
||||
}
|
||||
|
||||
handler := func(f *field) {
|
||||
f.Value = value
|
||||
}
|
||||
|
||||
fields = append(fields, lookupFields(elem, matcher, handler)...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupStruct(elem reflect.Type, value reflect.Value) *field {
|
||||
// ignore if that field is not a struct
|
||||
if elem.Kind() != reflect.Struct {
|
||||
// and it's not a controller because we don't want to accidentally
|
||||
// set fields to other user fields. Or no?
|
||||
// ||
|
||||
// (elem.Name() != "" && !strings.HasSuffix(elem.Name(), "Controller")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// search by fields.
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if elemField.Type == value.Type() {
|
||||
// println("Types are equal of: " + elemField.Type.Name() + " " + elemField.Name + " and " + value.Type().Name())
|
||||
// we area inside the correct type.
|
||||
return &field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// if field is struct and the value is struct
|
||||
// then try inside its fields for a compatible
|
||||
// field type.
|
||||
if elemField.Type.Kind() == reflect.Struct && value.Type().Kind() == reflect.Struct {
|
||||
elemFieldEmb := elem.Field(i)
|
||||
f := lookupStruct(elemFieldEmb.Type, value)
|
||||
if f != nil {
|
||||
fp := &field{
|
||||
Index: i,
|
||||
Name: elemFieldEmb.Name,
|
||||
Type: elemFieldEmb.Type,
|
||||
embedded: f,
|
||||
}
|
||||
return fp
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *binder) handle(c reflect.Value) {
|
||||
// we could make check for middlewares here but
|
||||
// these could easly be used outside of the controller
|
||||
|
|
217
mvc/activator/field.go
Normal file
217
mvc/activator/field.go
Normal file
|
@ -0,0 +1,217 @@
|
|||
package activator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type field struct {
|
||||
Name string // the field's original name
|
||||
// but if a tag with `name: "other"`
|
||||
// exist then this fill is filled, otherwise it's the same as the Name.
|
||||
TagName string
|
||||
Index int
|
||||
Type reflect.Type
|
||||
Value reflect.Value
|
||||
|
||||
embedded *field
|
||||
}
|
||||
|
||||
// getIndex returns all the "dimensions"
|
||||
// of the controller struct field's position that this field is referring to,
|
||||
// recursively.
|
||||
// Usage: elem.FieldByIndex(field.getIndex())
|
||||
// for example the {0,1} means that the field is on the second field of the first's
|
||||
// field of this struct.
|
||||
func (ff field) getIndex() []int {
|
||||
deepIndex := []int{ff.Index}
|
||||
|
||||
if emb := ff.embedded; emb != nil {
|
||||
deepIndex = append(deepIndex, emb.getIndex()...)
|
||||
}
|
||||
|
||||
return deepIndex
|
||||
}
|
||||
|
||||
// getType returns the type of the referring field, recursively.
|
||||
func (ff field) getType() reflect.Type {
|
||||
typ := ff.Type
|
||||
if emb := ff.embedded; emb != nil {
|
||||
return emb.getType()
|
||||
}
|
||||
|
||||
return typ
|
||||
}
|
||||
|
||||
// getFullName returns the full name of that field
|
||||
// i.e: UserController.SessionController.Manager,
|
||||
// it's useful for debugging only.
|
||||
func (ff field) getFullName() string {
|
||||
name := ff.Name
|
||||
|
||||
if emb := ff.embedded; emb != nil {
|
||||
return name + "." + emb.getFullName()
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// getTagName returns the tag name of the referring field
|
||||
// recursively.
|
||||
func (ff field) getTagName() string {
|
||||
name := ff.TagName
|
||||
|
||||
if emb := ff.embedded; emb != nil {
|
||||
return emb.getTagName()
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// checkVal checks if that value
|
||||
// is valid to be set-ed to the new controller's instance.
|
||||
// Used on binder.
|
||||
func checkVal(val reflect.Value) bool {
|
||||
return val.IsValid() && (val.Kind() == reflect.Ptr && !val.IsNil()) && val.CanInterface()
|
||||
}
|
||||
|
||||
// getValue returns a valid value of the referring field, recursively.
|
||||
func (ff field) getValue() interface{} {
|
||||
if ff.embedded != nil {
|
||||
return ff.embedded.getValue()
|
||||
}
|
||||
|
||||
if checkVal(ff.Value) {
|
||||
return ff.Value.Interface()
|
||||
}
|
||||
|
||||
return "undefinied value"
|
||||
}
|
||||
|
||||
// sendTo should be used when this field or its embedded
|
||||
// has a Value on it.
|
||||
// It sets the field's value to the "elem" instance, it's the new controller.
|
||||
func (ff field) sendTo(elem reflect.Value) {
|
||||
// note:
|
||||
// we don't use the getters here
|
||||
// because we do recursively search by our own here
|
||||
// to be easier to debug if ever needed.
|
||||
if embedded := ff.embedded; embedded != nil {
|
||||
if ff.Index >= 0 {
|
||||
embedded.sendTo(elem.Field(ff.Index))
|
||||
}
|
||||
return
|
||||
}
|
||||
elemField := elem.Field(ff.Index)
|
||||
if elemField.Kind() == reflect.Ptr && !elemField.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
elemField.Set(ff.Value)
|
||||
}
|
||||
|
||||
// lookupTagName checks if the "elemField" struct's field
|
||||
// contains a tag `name`, if it contains then it returns its value
|
||||
// otherwise returns the field's original Name.
|
||||
func lookupTagName(elemField reflect.StructField) string {
|
||||
vname := elemField.Name
|
||||
|
||||
if taggedName, ok := elemField.Tag.Lookup("name"); ok {
|
||||
vname = taggedName
|
||||
}
|
||||
return vname
|
||||
}
|
||||
|
||||
// lookupFields iterates all "elem"'s fields and its fields
|
||||
// if structs, recursively.
|
||||
// Compares them to the "matcher", if they passed
|
||||
// then it executes the "handler" if any,
|
||||
// the handler can change the field as it wants to.
|
||||
//
|
||||
// It finally returns that collection of the valid fields, can be empty.
|
||||
func lookupFields(elem reflect.Type, matcher func(reflect.StructField) bool, handler func(*field)) (fields []field) {
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if matcher(elemField) {
|
||||
field := field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
TagName: lookupTagName(elemField),
|
||||
Type: elemField.Type,
|
||||
}
|
||||
|
||||
if handler != nil {
|
||||
handler(&field)
|
||||
}
|
||||
|
||||
// we area inside the correct type
|
||||
fields = append(fields, field)
|
||||
continue
|
||||
}
|
||||
|
||||
f := lookupStructField(elemField.Type, matcher, handler)
|
||||
if f != nil {
|
||||
fields = append(fields, field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
embedded: f,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// lookupStructField is here to search for embedded field only,
|
||||
// is working with the "lookupFields".
|
||||
// We could just one one function
|
||||
// for both structured (embedded) fields and normal fields
|
||||
// but we keep that as it's, a new function like this
|
||||
// is easier for debugging, if ever needed.
|
||||
func lookupStructField(elem reflect.Type, matcher func(reflect.StructField) bool, handler func(*field)) *field {
|
||||
// fmt.Printf("lookup struct for elem: %s\n", elem.Name())
|
||||
|
||||
// ignore if that field is not a struct
|
||||
if elem.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
// search by fields.
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if matcher(elemField) {
|
||||
// we area inside the correct type.
|
||||
f := &field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
TagName: lookupTagName(elemField),
|
||||
Type: elemField.Type,
|
||||
}
|
||||
|
||||
if handler != nil {
|
||||
handler(f)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// if field is struct and the value is struct
|
||||
// then try inside its fields for a compatible
|
||||
// field type.
|
||||
if elemField.Type.Kind() == reflect.Struct { // 3-level
|
||||
elemFieldEmb := elem.Field(i)
|
||||
f := lookupStructField(elemFieldEmb.Type, matcher, handler)
|
||||
if f != nil {
|
||||
fp := &field{
|
||||
Index: i,
|
||||
Name: elemFieldEmb.Name,
|
||||
TagName: lookupTagName(elemFieldEmb),
|
||||
Type: elemFieldEmb.Type,
|
||||
embedded: f,
|
||||
}
|
||||
return fp
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -11,14 +11,16 @@ type modelControl struct {
|
|||
}
|
||||
|
||||
func (mc *modelControl) Load(t *TController) error {
|
||||
fields := lookupFields(t, func(f reflect.StructField) bool {
|
||||
matcher := func(f reflect.StructField) bool {
|
||||
if tag, ok := f.Tag.Lookup("iris"); ok {
|
||||
if tag == "model" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
fields := lookupFields(t.Type.Elem(), matcher, nil)
|
||||
|
||||
if len(fields) == 0 {
|
||||
// first is the `Controller` so we need to
|
||||
|
@ -34,15 +36,22 @@ func (mc *modelControl) Handle(ctx context.Context, c reflect.Value, methodFunc
|
|||
elem := c.Elem() // controller should always be a pointer at this state
|
||||
|
||||
for _, f := range mc.fields {
|
||||
elemField := elem.Field(f.Index)
|
||||
|
||||
index := f.getIndex()
|
||||
typ := f.getType()
|
||||
name := f.getTagName()
|
||||
|
||||
elemField := elem.FieldByIndex(index)
|
||||
// check if current controller's element field
|
||||
// is valid, is not nil and it's type is the same (should be but make that check to be sure).
|
||||
if !elemField.IsValid() || (elemField.Kind() == reflect.Ptr && elemField.IsNil()) || elemField.Type() != f.Type {
|
||||
if !elemField.IsValid() ||
|
||||
(elemField.Kind() == reflect.Ptr && elemField.IsNil()) ||
|
||||
elemField.Type() != typ {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldValue := elemField.Interface()
|
||||
// fmt.Printf("setting %s to %#v", f.Name, fieldValue)
|
||||
ctx.ViewData(f.Name, fieldValue)
|
||||
ctx.ViewData(name, fieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,77 +6,31 @@ import (
|
|||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
type field struct {
|
||||
Name string // by-defaultis the field's name but if `name: "other"` then it's overridden.
|
||||
Index int
|
||||
Type reflect.Type
|
||||
Value reflect.Value
|
||||
|
||||
embedded *field
|
||||
}
|
||||
|
||||
func (ff field) sendTo(elem reflect.Value) {
|
||||
if embedded := ff.embedded; embedded != nil {
|
||||
if ff.Index >= 0 {
|
||||
embedded.sendTo(elem.Field(ff.Index))
|
||||
}
|
||||
return
|
||||
}
|
||||
elemField := elem.Field(ff.Index)
|
||||
if elemField.Kind() == reflect.Ptr && !elemField.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
elemField.Set(ff.Value)
|
||||
}
|
||||
|
||||
func lookupFields(t *TController, validator func(reflect.StructField) bool) (fields []field) {
|
||||
elem := t.Type.Elem()
|
||||
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
valF := t.Value.Field(i)
|
||||
|
||||
// catch persistence data by tags, i.e:
|
||||
// MyData string `iris:"persistence"`
|
||||
if validator(elemField) {
|
||||
name := elemField.Name
|
||||
if nameTag, ok := elemField.Tag.Lookup("name"); ok {
|
||||
name = nameTag
|
||||
}
|
||||
|
||||
f := field{
|
||||
Name: name,
|
||||
Index: i,
|
||||
Type: elemField.Type,
|
||||
}
|
||||
|
||||
if valF.IsValid() || (valF.Kind() == reflect.Ptr && !valF.IsNil()) {
|
||||
val := reflect.ValueOf(valF.Interface())
|
||||
if val.IsValid() || (val.Kind() == reflect.Ptr && !val.IsNil()) {
|
||||
f.Value = val
|
||||
}
|
||||
}
|
||||
|
||||
fields = append(fields, f)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type persistenceDataControl struct {
|
||||
fields []field
|
||||
}
|
||||
|
||||
func (d *persistenceDataControl) Load(t *TController) error {
|
||||
fields := lookupFields(t, func(f reflect.StructField) bool {
|
||||
if tag, ok := f.Tag.Lookup("iris"); ok {
|
||||
matcher := func(elemField reflect.StructField) bool {
|
||||
if tag, ok := elemField.Tag.Lookup("iris"); ok {
|
||||
if tag == "persistence" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
handler := func(f *field) {
|
||||
valF := t.Value.Field(f.Index)
|
||||
if valF.IsValid() || (valF.Kind() == reflect.Ptr && !valF.IsNil()) {
|
||||
val := reflect.ValueOf(valF.Interface())
|
||||
if val.IsValid() || (val.Kind() == reflect.Ptr && !val.IsNil()) {
|
||||
f.Value = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fields := lookupFields(t.Type.Elem(), matcher, handler)
|
||||
|
||||
if len(fields) == 0 {
|
||||
// first is the `Controller` so we need to
|
||||
|
|
|
@ -104,8 +104,8 @@ type testControllerPersistence struct {
|
|||
Data string `iris:"persistence"`
|
||||
}
|
||||
|
||||
func (t *testControllerPersistence) Get() {
|
||||
t.Ctx.WriteString(t.Data)
|
||||
func (c *testControllerPersistence) Get() {
|
||||
c.Ctx.WriteString(c.Data)
|
||||
}
|
||||
|
||||
func TestControllerPersistenceFields(t *testing.T) {
|
||||
|
@ -127,25 +127,25 @@ type testControllerBeginAndEndRequestFunc struct {
|
|||
//
|
||||
// useful when more than one methods using the
|
||||
// same request values or context's function calls.
|
||||
func (t *testControllerBeginAndEndRequestFunc) BeginRequest(ctx context.Context) {
|
||||
t.Controller.BeginRequest(ctx)
|
||||
t.Username = ctx.Params().Get("username")
|
||||
func (c *testControllerBeginAndEndRequestFunc) BeginRequest(ctx context.Context) {
|
||||
c.Controller.BeginRequest(ctx)
|
||||
c.Username = ctx.Params().Get("username")
|
||||
// or t.Params.Get("username") because the
|
||||
// t.Ctx == ctx and is being initialized at the t.Controller.BeginRequest.
|
||||
}
|
||||
|
||||
// called after every method (Get() or Post()).
|
||||
func (t *testControllerBeginAndEndRequestFunc) EndRequest(ctx context.Context) {
|
||||
func (c *testControllerBeginAndEndRequestFunc) EndRequest(ctx context.Context) {
|
||||
ctx.Writef("done") // append "done" to the response
|
||||
t.Controller.EndRequest(ctx)
|
||||
c.Controller.EndRequest(ctx)
|
||||
}
|
||||
|
||||
func (t *testControllerBeginAndEndRequestFunc) Get() {
|
||||
t.Ctx.Writef(t.Username)
|
||||
func (c *testControllerBeginAndEndRequestFunc) Get() {
|
||||
c.Ctx.Writef(c.Username)
|
||||
}
|
||||
|
||||
func (t *testControllerBeginAndEndRequestFunc) Post() {
|
||||
t.Ctx.Writef(t.Username)
|
||||
func (c *testControllerBeginAndEndRequestFunc) Post() {
|
||||
c.Ctx.Writef(c.Username)
|
||||
}
|
||||
|
||||
func TestControllerBeginAndEndRequestFunc(t *testing.T) {
|
||||
|
@ -229,47 +229,39 @@ type testControllerModel struct {
|
|||
TestModel2 Model `iris:"model"`
|
||||
}
|
||||
|
||||
func (t *testControllerModel) Get() {
|
||||
username := t.Ctx.Params().Get("username")
|
||||
t.TestModel = Model{Username: username}
|
||||
t.TestModel2 = Model{Username: username + "2"}
|
||||
func (c *testControllerModel) Get() {
|
||||
username := c.Ctx.Params().Get("username")
|
||||
c.TestModel = Model{Username: username}
|
||||
c.TestModel2 = Model{Username: username + "2"}
|
||||
}
|
||||
|
||||
func (t *testControllerModel) EndRequest(ctx context.Context) {
|
||||
// t.Ctx == ctx
|
||||
|
||||
m, ok := t.Ctx.GetViewData()["myModel"]
|
||||
if !ok {
|
||||
t.Ctx.Writef("fail TestModel load and set")
|
||||
func writeModels(ctx context.Context, names ...string) {
|
||||
if expected, got := len(names), len(ctx.GetViewData()); expected != got {
|
||||
ctx.Writef("expected view data length: %d but got: %d for names: %s", expected, got, names)
|
||||
return
|
||||
}
|
||||
|
||||
model, ok := m.(Model)
|
||||
for _, name := range names {
|
||||
|
||||
if !ok {
|
||||
t.Ctx.Writef("fail to override the TestModel name by the tag")
|
||||
return
|
||||
m, ok := ctx.GetViewData()[name]
|
||||
if !ok {
|
||||
ctx.Writef("fail load and set the %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
model, ok := m.(Model)
|
||||
if !ok {
|
||||
ctx.Writef("fail to override the %s' name by the tag", name)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Writef(model.Username)
|
||||
}
|
||||
}
|
||||
|
||||
// test without custom name tag, should have the field's nae.
|
||||
m, ok = t.Ctx.GetViewData()["TestModel2"]
|
||||
if !ok {
|
||||
t.Ctx.Writef("fail TestModel2 load and set")
|
||||
return
|
||||
}
|
||||
|
||||
model2, ok := m.(Model)
|
||||
|
||||
if !ok {
|
||||
t.Ctx.Writef("fail to override the TestModel2 name by the tag")
|
||||
return
|
||||
}
|
||||
|
||||
// models are being rendered via the View at ViewData but
|
||||
// we just test it here, so print it back.
|
||||
t.Ctx.Writef(model.Username + model2.Username)
|
||||
|
||||
t.Controller.EndRequest(ctx)
|
||||
func (c *testControllerModel) EndRequest(ctx context.Context) {
|
||||
writeModels(ctx, "myModel", "TestModel2")
|
||||
c.Controller.EndRequest(ctx)
|
||||
}
|
||||
func TestControllerModel(t *testing.T) {
|
||||
app := iris.New()
|
||||
|
@ -313,6 +305,7 @@ func (t *testControllerBindDeep) Get() {
|
|||
}
|
||||
func TestControllerBind(t *testing.T) {
|
||||
app := iris.New()
|
||||
|
||||
t1, t2 := "my pointer title", "val title"
|
||||
// test bind pointer to pointer of the correct type
|
||||
myTitlePtr := &testBindType{title: t1}
|
||||
|
@ -384,5 +377,62 @@ func TestControllerRelPathAndRelTmpl(t *testing.T) {
|
|||
for path, tt := range tests {
|
||||
e.GET(path).Expect().Status(httptest.StatusOK).JSON().Equal(tt)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type testCtrl0 struct {
|
||||
testCtrl00
|
||||
}
|
||||
|
||||
func (c *testCtrl0) Get() {
|
||||
username := c.Params.Get("username")
|
||||
c.Model = Model{Username: username}
|
||||
}
|
||||
|
||||
func (c *testCtrl0) EndRequest(ctx context.Context) {
|
||||
writeModels(ctx, "myModel")
|
||||
|
||||
if c.TitlePointer == nil {
|
||||
ctx.Writef("\nTitlePointer is nil!\n")
|
||||
} else {
|
||||
ctx.Writef(c.TitlePointer.title)
|
||||
}
|
||||
|
||||
//should be the same as `.testCtrl000.testCtrl0000.EndRequest(ctx)`
|
||||
c.testCtrl00.EndRequest(ctx)
|
||||
}
|
||||
|
||||
type testCtrl00 struct {
|
||||
testCtrl000
|
||||
|
||||
Model Model `iris:"model" name:"myModel"`
|
||||
}
|
||||
|
||||
type testCtrl000 struct {
|
||||
testCtrl0000
|
||||
|
||||
TitlePointer *testBindType
|
||||
}
|
||||
|
||||
type testCtrl0000 struct {
|
||||
mvc.Controller
|
||||
}
|
||||
|
||||
func (c *testCtrl0000) EndRequest(ctx context.Context) {
|
||||
ctx.Writef("finish")
|
||||
}
|
||||
|
||||
func TestControllerInsideControllerRecursively(t *testing.T) {
|
||||
var (
|
||||
username = "gerasimos"
|
||||
title = "mytitle"
|
||||
expected = username + title + "finish"
|
||||
)
|
||||
|
||||
app := iris.New()
|
||||
app.Controller("/user/{username}", new(testCtrl0),
|
||||
&testBindType{title: title})
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/user/" + username).Expect().
|
||||
Status(httptest.StatusOK).Body().Equal(expected)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user