From de3fc8ae4676c107eb626b4767656961b5585118 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Tue, 10 Oct 2017 16:58:14 +0300 Subject: [PATCH] Update to version 8.5.1. It contains a minor fix. Read HISTORY.md https://github.com/kataras/iris/blob/master/HISTORY.md#tu-10-october-2017--v851 Former-commit-id: af1424c691613ccde911bef7d1371aa52e6abb79 --- HISTORY.md | 6 +++++ README.md | 3 +-- README_NEXT.md | 2 +- VERSION | 2 +- _examples/README.md | 27 +++++++++++++++++++--- doc.go | 2 +- iris.go | 2 +- mvc/activator/activator.go | 2 +- mvc/controller.go | 14 +++++++++++- mvc/method_result_test.go | 47 ++++++++++++++++++++++++++++++++++++++ mvc/method_result_view.go | 30 ++++++++++++++++++++---- 11 files changed, 122 insertions(+), 15 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index f5be8193..a3d7cfe1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -18,6 +18,12 @@ 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, 10 October 2017 | v8.5.1 + +## MVC + +- fix any manual or before middleware's `ctx.ViewData(key, value)` gets overridden by setting `mvc.Controller.Data` or `return mvc.View {Data: ...}`. See the [test case](mvc/method_result_test.go#L226). + # Mo, 09 October 2017 | v8.5.0 ## MVC diff --git a/README.md b/README.md index b39654be..29e12881 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ $ go get -u github.com/kataras/iris - Iris takes advantage of the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature. You get truly reproducible builds, as this method guards against upstream renames and deletes. -- [Latest changes | v8.5.0](https://github.com/kataras/iris/blob/master/HISTORY.md#mo-09-october-2017--v850) +- [Latest changes | v8.5.1](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-10-october-2017--v851) ## Getting Started @@ -698,7 +698,6 @@ func (s *MovieMemoryService) GetAll() []models.Movie { } ``` - ```go // file: controllers/movie_controller.go diff --git a/README_NEXT.md b/README_NEXT.md index 1d70cce8..d4279d32 100644 --- a/README_NEXT.md +++ b/README_NEXT.md @@ -86,7 +86,7 @@ _Psst_, we've produced a small video about your feelings regrating to Iris! You ### 📑 Table Of Content * [Installation](#-installation) -* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#mo-09-october-2017--v850) +* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-10-october-2017--v851) * [Learn](#-learn) * [Structuring](_examples/#structuring) * [HTTP Listening](_examples/#http-listening) diff --git a/VERSION b/VERSION index 6d4ab2f0..0ea7649d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.5.0:https://github.com/kataras/iris/blob/master/HISTORY.md#mo-09-october-2017--v850 \ No newline at end of file +8.5.1:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-10-october-2017--v851 \ No newline at end of file diff --git a/_examples/README.md b/_examples/README.md index c1397a50..58adea73 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -174,20 +174,41 @@ If `app.Controller("/assets", new(file.Controller))` - `func(*Controller) GetByWildard(path string)` - `GET:/assets/{param:path}` + Supported types for method functions receivers: int, int64, bool and string. + +Response via output arguments, optionally, i.e + +```go +func(c *ExampleController) Get() string | + (string, string) | + (string, int) | + int | + (int, string | + (string, error) | + error | + (int, error) | + (customStruct, error) | + customStruct | + (customStruct, int) | + (customStruct, string) | + mvc.Result or (mvc.Result, error) +``` + +where [mvc.Result](https://github.com/kataras/iris/blob/master/mvc/method_result.go) is an interface which contains only that function: `Dispatch(ctx iris.Context)`. + **Using Iris MVC for code reuse** By creating components that are independent of one another, developers are able to reuse components quickly and easily in other applications. The same (or similar) view for one application can be refactored for another application with different data because the view is simply handling how the data is being displayed to the user. If you're new to back-end web development read about the MVC architectural pattern first, a good start is that [wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). - Follow the examples below, - [Hello world](mvc/hello-world/main.go) - [Session Controller](mvc/session-controller/main.go) -- [A simple but featured Controller with model and views](mvc/controller-with-model-and-view). +- [A simple but featured Controller with model and views](mvc/controller-with-model-and-view) - [Login showcase](mvc/login/main.go) **NEW** - +- [Using Method Result (plus Service-oriented design)](mvc/using-method-result) ### Subdomains diff --git a/doc.go b/doc.go index 465e4d2f..52def811 100644 --- a/doc.go +++ b/doc.go @@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub: Current Version -8.5.0 +8.5.1 Installation diff --git a/iris.go b/iris.go index 9de41798..851130f6 100644 --- a/iris.go +++ b/iris.go @@ -32,7 +32,7 @@ import ( const ( // Version is the current version number of the Iris Web Framework. - Version = "8.5.0" + Version = "8.5.1" ) // HTTP status codes as registered with IANA. diff --git a/mvc/activator/activator.go b/mvc/activator/activator.go index 2d7ae221..719108c8 100644 --- a/mvc/activator/activator.go +++ b/mvc/activator/activator.go @@ -131,7 +131,7 @@ func (t TController) HandlerOf(methodFunc methodfunc.MethodFunc) context.Handler t.persistenceController.Handle(c) } - // if previous (binded) handlers stoped the execution + // if previous (binded) handlers stopped the execution // we should know that. if ctx.IsStopped() { return diff --git a/mvc/controller.go b/mvc/controller.go index 06015f8f..00a119cd 100644 --- a/mvc/controller.go +++ b/mvc/controller.go @@ -347,7 +347,19 @@ func (c *Controller) EndRequest(ctx context.Context) { ctx.ViewLayout(layout) } if len(c.Data) > 0 { - ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), c.Data) + dataKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey() + // In order to respect any c.Ctx.ViewData that may called manually before; + if ctx.Values().Get(dataKey) == nil { + // if no c.Ctx.ViewData then it's empty do a + // pure set, it's faster. + ctx.Values().Set(dataKey, c.Data) + } else { + // else do a range loop and set the data one by one. + for k, v := range c.Data { + ctx.ViewData(k, v) + } + } + } ctx.View(view) diff --git a/mvc/method_result_test.go b/mvc/method_result_test.go index a7a28b98..48965668 100644 --- a/mvc/method_result_test.go +++ b/mvc/method_result_test.go @@ -222,3 +222,50 @@ func TestControllerMethodResultTypes(t *testing.T) { // it will fire the error's text Body().Equal("omit return of testCustomStruct and fire error") } + +type testControllerViewResultRespectCtxViewData struct { + T *testing.T + mvc.C +} + +func (t *testControllerViewResultRespectCtxViewData) BeginRequest(ctx context.Context) { + t.C.BeginRequest(ctx) + ctx.ViewData("name_begin", "iris_begin") +} + +func (t *testControllerViewResultRespectCtxViewData) EndRequest(ctx context.Context) { + t.C.EndRequest(ctx) + // check if data is not overriden by return mvc.View {Data: context.Map...} + + dataWritten := ctx.GetViewData() + if dataWritten == nil { + t.T.Fatalf("view data is nil, both BeginRequest and Get failed to write the data") + return + } + + if dataWritten["name_begin"] == nil { + t.T.Fatalf(`view data[name_begin] is nil, + BeginRequest's ctx.ViewData call have been overriden by Get's return mvc.View {Data: }. + Total view data: %v`, dataWritten) + } + + if dataWritten["name"] == nil { + t.T.Fatalf("view data[name] is nil, Get's return mvc.View {Data: } didn't work. Total view data: %v", dataWritten) + } +} + +func (t *testControllerViewResultRespectCtxViewData) Get() mvc.Result { + return mvc.View{ + Name: "doesnt_exists.html", + Data: context.Map{"name": "iris"}, // we care about this only. + Code: iris.StatusInternalServerError, + } +} + +func TestControllerViewResultRespectCtxViewData(t *testing.T) { + app := iris.New() + app.Controller("/", new(testControllerViewResultRespectCtxViewData), t) + e := httptest.New(t, app) + + e.GET("/").Expect().Status(iris.StatusInternalServerError) +} diff --git a/mvc/method_result_view.go b/mvc/method_result_view.go index 216aa213..cb201b7d 100644 --- a/mvc/method_result_view.go +++ b/mvc/method_result_view.go @@ -5,6 +5,8 @@ import ( "github.com/kataras/iris/context" "github.com/kataras/iris/mvc/activator/methodfunc" + + "github.com/fatih/structs" ) // View completes the `methodfunc.Result` interface. @@ -66,12 +68,32 @@ func (r View) Dispatch(ctx context.Context) { // r as Response view. } if r.Data != nil { - ctx.Values().Set( - ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), - r.Data, - ) + // In order to respect any c.Ctx.ViewData that may called manually before; + dataKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey() + if ctx.Values().Get(dataKey) == nil { + // if no c.Ctx.ViewData then it's empty do a + // pure set, it's faster. + ctx.Values().Set(dataKey, r.Data) + } else { + // else check if r.Data is map or struct, if struct convert it to map, + // do a range loop and set the data one by one. + // context.Map is actually a map[string]interface{} but we have to make that check; + if m, ok := r.Data.(map[string]interface{}); ok { + setViewData(ctx, m) + } else if m, ok := r.Data.(context.Map); ok { + setViewData(ctx, m) + } else if structs.IsStruct(r.Data) { + setViewData(ctx, structs.Map(r)) + } + } } ctx.View(r.Name) } } + +func setViewData(ctx context.Context, data map[string]interface{}) { + for k, v := range data { + ctx.ViewData(k, v) + } +}