From 34664aa31111be576ea4fe538a9fd867d44e5fdc Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 16 Dec 2017 06:38:28 +0200 Subject: [PATCH] OK, my dream-idea is implemented. TODO: Some examples and doc.go is not updated yet, comments on the mvc/di subpackage, the tutorial/vuejs-todo-mvc is running but not finished yet (it's using browser's localstorage and it should be replaced by the http requests that are registered via iris mvc Former-commit-id: 0ea7e01ce1d78bcb78b40f3b0f5c03ad7c9abaea --- .../iris-mvc/controllers/values_controller.go | 13 +-- _benchmarks/iris-mvc/main.go | 13 ++- .../controllers/values_controller.go | 25 ----- _benchmarks/iris-mvc2/main.go | 17 --- _benchmarks/iris/main.go | 7 +- _examples/mvc/hello-world/main.go | 59 ++++++---- _examples/mvc/overview/main.go | 44 +++++--- .../web/controllers/hello_controller.go | 6 +- .../web/controllers/movie_controller.go | 13 +-- _examples/tutorial/vuejs-todo-mvc/README.md | 1 + .../src/web/controllers/todo_controller.go | 7 +- .../tutorial/vuejs-todo-mvc/src/web/main.go | 15 ++- mvc/AUTHORS | 4 + mvc/LICENSE | 27 +++++ {mvc2 => mvc}/README.md | 0 {mvc2 => mvc}/controller.go | 66 ++--------- {mvc2 => mvc}/controller_handle_test.go | 27 +++-- {mvc2 => mvc}/controller_method_parser.go | 60 ++++++---- {mvc2 => mvc}/controller_test.go | 58 +++++----- {mvc2 => mvc}/di/di.go | 0 {mvc2 => mvc}/di/func.go | 11 +- {mvc2 => mvc}/di/object.go | 0 {mvc2 => mvc}/di/reflect.go | 0 {mvc2 => mvc}/di/struct.go | 0 {mvc2 => mvc}/di/values.go | 4 +- mvc/engine.go | 105 ++++++++++++++++++ {mvc2 => mvc}/engine_handler_test.go | 4 +- {mvc2 => mvc}/func_result.go | 4 +- {mvc2 => mvc}/func_result_test.go | 12 +- {mvc2 => mvc}/handler.go | 8 +- {mvc2 => mvc}/handler_test.go | 5 +- {mvc2 => mvc}/ideas/1/main.go | 2 +- {mvc2 => mvc}/mvc.go | 2 +- mvc2/path_param_binder.go => mvc/param.go | 42 ++++--- .../param_test.go | 2 +- mvc/reflect.go | 54 +++++++++ {mvc2 => mvc}/session.go | 2 +- {mvc2 => mvc}/session_controller.go | 11 +- mvc2/bind.go | 34 ------ mvc2/engine.go | 53 --------- mvc2/reflect.go | 28 ----- 41 files changed, 437 insertions(+), 408 deletions(-) delete mode 100644 _benchmarks/iris-mvc2/controllers/values_controller.go delete mode 100644 _benchmarks/iris-mvc2/main.go create mode 100644 _examples/tutorial/vuejs-todo-mvc/README.md create mode 100644 mvc/AUTHORS create mode 100644 mvc/LICENSE rename {mvc2 => mvc}/README.md (100%) rename {mvc2 => mvc}/controller.go (80%) rename {mvc2 => mvc}/controller_handle_test.go (93%) rename {mvc2 => mvc}/controller_method_parser.go (79%) rename {mvc2 => mvc}/controller_test.go (93%) rename {mvc2 => mvc}/di/di.go (100%) rename {mvc2 => mvc}/di/func.go (93%) rename {mvc2 => mvc}/di/object.go (100%) rename {mvc2 => mvc}/di/reflect.go (100%) rename {mvc2 => mvc}/di/struct.go (100%) rename {mvc2 => mvc}/di/values.go (99%) create mode 100644 mvc/engine.go rename {mvc2 => mvc}/engine_handler_test.go (88%) rename {mvc2 => mvc}/func_result.go (99%) rename {mvc2 => mvc}/func_result_test.go (98%) rename {mvc2 => mvc}/handler.go (92%) rename {mvc2 => mvc}/handler_test.go (98%) rename {mvc2 => mvc}/ideas/1/main.go (98%) rename {mvc2 => mvc}/mvc.go (99%) rename mvc2/path_param_binder.go => mvc/param.go (68%) rename mvc2/path_param_binder_test.go => mvc/param_test.go (99%) create mode 100644 mvc/reflect.go rename {mvc2 => mvc}/session.go (97%) rename {mvc2 => mvc}/session_controller.go (89%) delete mode 100644 mvc2/bind.go delete mode 100644 mvc2/engine.go delete mode 100644 mvc2/reflect.go diff --git a/_benchmarks/iris-mvc/controllers/values_controller.go b/_benchmarks/iris-mvc/controllers/values_controller.go index c560a641..3fe3a089 100644 --- a/_benchmarks/iris-mvc/controllers/values_controller.go +++ b/_benchmarks/iris-mvc/controllers/values_controller.go @@ -1,19 +1,8 @@ package controllers -import "github.com/kataras/iris/mvc" - // ValuesController is the equivalent // `ValuesController` of the .net core 2.0 mvc application. -type ValuesController struct { - mvc.C -} - -/* on windows tests(older) the Get was: -func (vc *ValuesController) Get() { - // id,_ := vc.Params.GetInt("id") - // vc.Ctx.WriteString("value") -} -but as Iris is always going better, now supports return values as well*/ +type ValuesController struct{} // Get handles "GET" requests to "api/values/{id}". func (vc *ValuesController) Get() string { diff --git a/_benchmarks/iris-mvc/main.go b/_benchmarks/iris-mvc/main.go index 92e03b59..5b9ca837 100644 --- a/_benchmarks/iris-mvc/main.go +++ b/_benchmarks/iris-mvc/main.go @@ -1,15 +1,18 @@ package main +/// TODO: remove this on the "master" branch, or even replace it +// with the "iris-mvc" (the new implementatioin is even faster, close to handlers version, +// with bindings or without). + import ( + "github.com/kataras/iris/_benchmarks/iris-mvc2/controllers" + "github.com/kataras/iris" - "github.com/kataras/iris/_benchmarks/iris-mvc/controllers" + "github.com/kataras/iris/mvc" ) func main() { app := iris.New() - app.Controller("/api/values/{id}", new(controllers.ValuesController)) - - // 24 August 2017: Iris has a built'n version updater but we don't need it - // when benchmarking... + mvc.New(app.Party("/api/values/{id}")).Register(new(controllers.ValuesController)) app.Run(iris.Addr(":5000"), iris.WithoutVersionChecker) } diff --git a/_benchmarks/iris-mvc2/controllers/values_controller.go b/_benchmarks/iris-mvc2/controllers/values_controller.go deleted file mode 100644 index 80ac5ba7..00000000 --- a/_benchmarks/iris-mvc2/controllers/values_controller.go +++ /dev/null @@ -1,25 +0,0 @@ -package controllers - -// import "github.com/kataras/iris/mvc2" - -// ValuesController is the equivalent -// `ValuesController` of the .net core 2.0 mvc application. -type ValuesController struct{} //{ mvc2.C } - -/* on windows tests(older) the Get was: -func (vc *ValuesController) Get() { - // id,_ := vc.Params.GetInt("id") - // vc.Ctx.WriteString("value") -} -but as Iris is always going better, now supports return values as well*/ - -// Get handles "GET" requests to "api/values/{id}". -func (vc *ValuesController) Get() string { - return "value" -} - -// Put handles "PUT" requests to "api/values/{id}". -func (vc *ValuesController) Put() {} - -// Delete handles "DELETE" requests to "api/values/{id}". -func (vc *ValuesController) Delete() {} diff --git a/_benchmarks/iris-mvc2/main.go b/_benchmarks/iris-mvc2/main.go deleted file mode 100644 index 6366ac2c..00000000 --- a/_benchmarks/iris-mvc2/main.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -/// TODO: remove this on the "master" branch, or even replace it -// with the "iris-mvc" (the new implementatioin is even faster, close to handlers version, -// with bindings or without). - -import ( - "github.com/kataras/iris" - "github.com/kataras/iris/_benchmarks/iris-mvc2/controllers" - "github.com/kataras/iris/mvc2" -) - -func main() { - app := iris.New() - mvc2.New().Controller(app.Party("/api/values/{id}"), new(controllers.ValuesController)) - app.Run(iris.Addr(":5000"), iris.WithoutVersionChecker) -} diff --git a/_benchmarks/iris/main.go b/_benchmarks/iris/main.go index ef5d1090..7c1ee6e2 100644 --- a/_benchmarks/iris/main.go +++ b/_benchmarks/iris/main.go @@ -1,13 +1,10 @@ package main -import ( - "github.com/kataras/iris" - "github.com/kataras/iris/context" -) +import "github.com/kataras/iris" func main() { app := iris.New() - app.Get("/api/values/{id}", func(ctx context.Context) { + app.Get("/api/values/{id}", func(ctx iris.Context) { ctx.WriteString("value") }) diff --git a/_examples/mvc/hello-world/main.go b/_examples/mvc/hello-world/main.go index 5d2c759a..006c818c 100644 --- a/_examples/mvc/hello-world/main.go +++ b/_examples/mvc/hello-world/main.go @@ -3,13 +3,6 @@ package main import ( "github.com/kataras/iris" "github.com/kataras/iris/mvc" - // auto-completion does not working well with type aliases - // when embedded fields. - // We should complete a report on golang repo for that at some point. - // - // Therefore import the "mvc" package manually - // here at "hello-world" so users can see that - // import path somewhere else than the "FAQ" section. "github.com/kataras/iris/middleware/logger" "github.com/kataras/iris/middleware/recover" @@ -43,27 +36,18 @@ func main() { app.Use(recover.New()) app.Use(logger.New()) - app.Controller("/", new(ExampleController)) + // Register a controller based on the root Router, "/". + mvc.New(app).Register(new(ExampleController)) // http://localhost:8080 // http://localhost:8080/ping // http://localhost:8080/hello + // http://localhost:8080/custom_path app.Run(iris.Addr(":8080")) } // ExampleController serves the "/", "/ping" and "/hello". -type ExampleController struct { - // if you build with go1.8 you have to use the mvc package always, - // otherwise - // you can, optionally - // use the type alias `iris.C`, - // same for - // context.Context -> iris.Context, - // mvc.Result -> iris.Result, - // mvc.Response -> iris.Response, - // mvc.View -> iris.View - mvc.C -} +type ExampleController struct{} // Get serves // Method: GET @@ -89,6 +73,31 @@ func (c *ExampleController) GetHello() interface{} { return map[string]string{"message": "Hello Iris!"} } +// BeforeActivate called once, before the controller adapted to the main application +// and of course before the server ran. +// After version 9 you can also add custom routes for a specific controller's methods. +// Here you can register custom method's handlers +// use the standard router with `ca.Router` to do something that you can do without mvc as well, +// and add dependencies that will be binded to a controller's fields or method function's input arguments. +func (c *ExampleController) BeforeActivate(ca *mvc.ControllerActivator) { + anyMiddlewareHere := func(ctx iris.Context) { + ctx.Application().Logger().Warnf("Inside /custom_path") + ctx.Next() + } + ca.Handle("GET", "/custom_path", "CustomHandlerWithoutFollowingTheNamingGuide", anyMiddlewareHere) + + // or even add a global middleware based on this controller's router, + // which in this example is the root "/": + // ca.Router.Use(myMiddleware) +} + +// CustomHandlerWithoutFollowingTheNamingGuide serves +// Method: GET +// Resource: http://localhost:8080/custom_path +func (c *ExampleController) CustomHandlerWithoutFollowingTheNamingGuide() string { + return "hello from the custom handler without following the naming guide" +} + // GetUserBy serves // Method: GET // Resource: http://localhost:8080/user/{username:string} @@ -121,4 +130,14 @@ func (c *ExampleController) Trace() {} func (c *ExampleController) All() {} // OR func (c *ExampleController) Any() {} + + + +func (c *ExampleController) BeforeActivate(ca *mvc.ControllerActivator) { + // 1 -> the HTTP Method + // 2 -> the route's path + // 3 -> this controller's method name that should be handler for that route. + ca.Handle("GET", "/mypath/{param}", "DoIt", optionalMiddlewareHere...) +} + */ diff --git a/_examples/mvc/overview/main.go b/_examples/mvc/overview/main.go index 2d0befc5..c0a84713 100644 --- a/_examples/mvc/overview/main.go +++ b/_examples/mvc/overview/main.go @@ -10,38 +10,52 @@ import ( "github.com/kataras/iris/_examples/mvc/overview/web/middleware" "github.com/kataras/iris" + "github.com/kataras/iris/mvc" ) func main() { app := iris.New() + app.Logger().SetLevel("debug") // Load the template files. app.RegisterView(iris.HTML("./web/views", ".html")) // Register our controllers. - app.Controller("/hello", new(controllers.HelloController)) + mvc.New(app.Party("/hello")).Register(new(controllers.HelloController)) + // You can also split the code you write to configure an mvc.Application + // using the `Configure` method, as shown below. + mvc.New(app.Party("/movies")).Configure(movies) - // Create our movie repository with some (memory) data from the datasource. - repo := repositories.NewMovieRepository(datasource.Movies) - // Create our movie service, we will bind it to the movie controller. - movieService := services.NewMovieService(repo) - - app.Controller("/movies", new(controllers.MovieController), - // Bind the "movieService" to the MovieController's Service (interface) field. - movieService, - // Add the basic authentication(admin:password) middleware - // for the /movies based requests. - middleware.BasicAuth) - - // Start the web server at localhost:8080 // http://localhost:8080/hello // http://localhost:8080/hello/iris // http://localhost:8080/movies // http://localhost:8080/movies/1 app.Run( + // Start the web server at localhost:8080 iris.Addr("localhost:8080"), + // disables updates: iris.WithoutVersionChecker, + // skip err server closed when CTRL/CMD+C pressed: iris.WithoutServerError(iris.ErrServerClosed), - iris.WithOptimizations, // enables faster json serialization and more + // enables faster json serialization and more: + iris.WithOptimizations, ) } + +// note the mvc.Application, it's not iris.Application. +func movies(app *mvc.Application) { + // Add the basic authentication(admin:password) middleware + // for the /movies based requests. + app.Router.Use(middleware.BasicAuth) + + // Create our movie repository with some (memory) data from the datasource. + repo := repositories.NewMovieRepository(datasource.Movies) + // Create our movie service, we will bind it to the movie app's dependencies. + movieService := services.NewMovieService(repo) + app.AddDependencies(movieService) + + // Register our movies controller. + // Note that you can register more than one controller + // you can alos create child mvc apps using the `movies.NewChild()` if you want. + app.Register(new(controllers.MovieController)) +} diff --git a/_examples/mvc/overview/web/controllers/hello_controller.go b/_examples/mvc/overview/web/controllers/hello_controller.go index 56518d5e..6f6eb264 100644 --- a/_examples/mvc/overview/web/controllers/hello_controller.go +++ b/_examples/mvc/overview/web/controllers/hello_controller.go @@ -10,9 +10,7 @@ import ( // HelloController is our sample controller // it handles GET: /hello and GET: /hello/{name} -type HelloController struct { - mvc.C -} +type HelloController struct{} var helloView = mvc.View{ Name: "hello/index.html", @@ -32,7 +30,7 @@ func (c *HelloController) Get() mvc.Result { return helloView } -// you can define a standard error in order to be re-usable anywhere in your app. +// you can define a standard error in order to re-use anywhere in your app. var errBadName = errors.New("bad name") // you can just return it as error or even better diff --git a/_examples/mvc/overview/web/controllers/movie_controller.go b/_examples/mvc/overview/web/controllers/movie_controller.go index 10542d1a..0555bf43 100644 --- a/_examples/mvc/overview/web/controllers/movie_controller.go +++ b/_examples/mvc/overview/web/controllers/movie_controller.go @@ -9,17 +9,10 @@ import ( "github.com/kataras/iris/_examples/mvc/overview/services" "github.com/kataras/iris" - "github.com/kataras/iris/mvc" ) // MovieController is our /movies controller. type MovieController struct { - // mvc.C is just a lightweight lightweight alternative - // to the "mvc.Controller" controller type, - // use it when you don't need mvc.Controller's fields - // (you don't need those fields when you return values from the method functions). - mvc.C - // Our MovieService, it's an interface which // is binded from the main application. Service services.MovieService @@ -53,9 +46,9 @@ func (c *MovieController) GetBy(id int64) (movie datamodels.Movie, found bool) { // PutBy updates a movie. // Demo: // curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1 -func (c *MovieController) PutBy(id int64) (datamodels.Movie, error) { +func (c *MovieController) PutBy(ctx iris.Context, id int64) (datamodels.Movie, error) { // get the request data for poster and genre - file, info, err := c.Ctx.FormFile("poster") + file, info, err := ctx.FormFile("poster") if err != nil { return datamodels.Movie{}, errors.New("failed due form file 'poster' missing") } @@ -64,7 +57,7 @@ func (c *MovieController) PutBy(id int64) (datamodels.Movie, error) { // imagine that is the url of the uploaded file... poster := info.Filename - genre := c.Ctx.FormValue("genre") + genre := ctx.FormValue("genre") return c.Service.UpdatePosterAndGenreByID(id, poster, genre) } diff --git a/_examples/tutorial/vuejs-todo-mvc/README.md b/_examples/tutorial/vuejs-todo-mvc/README.md new file mode 100644 index 00000000..b1ab8cbd --- /dev/null +++ b/_examples/tutorial/vuejs-todo-mvc/README.md @@ -0,0 +1 @@ +# Unfinished - wait until today :) \ No newline at end of file diff --git a/_examples/tutorial/vuejs-todo-mvc/src/web/controllers/todo_controller.go b/_examples/tutorial/vuejs-todo-mvc/src/web/controllers/todo_controller.go index ce2b8869..5536e0fd 100644 --- a/_examples/tutorial/vuejs-todo-mvc/src/web/controllers/todo_controller.go +++ b/_examples/tutorial/vuejs-todo-mvc/src/web/controllers/todo_controller.go @@ -4,8 +4,9 @@ import ( "github.com/kataras/iris/_examples/tutorial/vuejs-todo-mvc/src/todo" "github.com/kataras/iris" - mvc "github.com/kataras/iris/mvc2" "github.com/kataras/iris/sessions" + + "github.com/kataras/iris/mvc" ) // TodoController is our TODO app's web controller. @@ -38,10 +39,6 @@ func (c *TodoController) BeforeActivate(ca *mvc.ControllerActivator) { }) // ca.Router.Use(...).Done(...).Layout(...) - // TODO:(?) - // m := ca.Method("PutCompleteBy") - // m.Route.Use(...).Done(...) <- we don't have the route here but I can find something to solve this. - // m.Dependencies.Add(...) } // Get handles the GET: /todo route. diff --git a/_examples/tutorial/vuejs-todo-mvc/src/web/main.go b/_examples/tutorial/vuejs-todo-mvc/src/web/main.go index ee3c875e..dbfaa446 100644 --- a/_examples/tutorial/vuejs-todo-mvc/src/web/main.go +++ b/_examples/tutorial/vuejs-todo-mvc/src/web/main.go @@ -5,8 +5,9 @@ import ( "github.com/kataras/iris/_examples/tutorial/vuejs-todo-mvc/src/web/controllers" "github.com/kataras/iris" - mvc "github.com/kataras/iris/mvc2" "github.com/kataras/iris/sessions" + + "github.com/kataras/iris/mvc" ) func main() { @@ -22,14 +23,16 @@ func main() { Cookie: "_iris_session", }) - m := mvc.New() + m := mvc.New(app.Party("/todo")) - // any bindings here... - m.Bind(mvc.Session(sess)) + // any dependencies bindings here... + m.AddDependencies( + mvc.Session(sess), + new(todo.MemoryService), + ) - m.Bind(new(todo.MemoryService)) // controllers registration here... - m.Controller(app.Party("/todo"), new(controllers.TodoController)) + m.Register(new(controllers.TodoController)) // start the web server at http://localhost:8080 app.Run(iris.Addr(":8080"), iris.WithoutVersionChecker, iris.WithOptimizations) diff --git a/mvc/AUTHORS b/mvc/AUTHORS new file mode 100644 index 00000000..669cf5d3 --- /dev/null +++ b/mvc/AUTHORS @@ -0,0 +1,4 @@ +# This is the official list of Iris MVC authors for copyright +# purposes. + +Gerasimos Maropoulos diff --git a/mvc/LICENSE b/mvc/LICENSE new file mode 100644 index 00000000..469fb44d --- /dev/null +++ b/mvc/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2018 Gerasimos Maropoulos. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Iris nor the names of its +contributor, Gerasimos Maropoulos, may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/mvc2/README.md b/mvc/README.md similarity index 100% rename from mvc2/README.md rename to mvc/README.md diff --git a/mvc2/controller.go b/mvc/controller.go similarity index 80% rename from mvc2/controller.go rename to mvc/controller.go index af17b21e..13556dc8 100644 --- a/mvc2/controller.go +++ b/mvc/controller.go @@ -1,78 +1,25 @@ -package mvc2 +package mvc import ( "fmt" "reflect" "strings" - "github.com/kataras/iris/mvc2/di" + "github.com/kataras/iris/mvc/di" "github.com/kataras/iris/context" "github.com/kataras/iris/core/router" "github.com/kataras/iris/core/router/macro" ) -// BaseController is the controller interface, -// which the main request `C` will implement automatically. -// End-dev doesn't need to have any knowledge of this if she/he doesn't want to implement -// a new Controller type. -// Controller looks the whole flow as one handler, so `ctx.Next` -// inside `BeginRequest` is not be respected. -// Alternative way to check if a middleware was procceed successfully -// and called its `ctx.Next` is the `ctx.Proceed(handler) bool`. -// You have to navigate to the `context/context#Proceed` function's documentation. +// BaseController is the optional controller interface, if it's +// completed by the end controller then the BeginRequest and EndRequest +// are called between the controller's method responsible for the incoming request. type BaseController interface { BeginRequest(context.Context) EndRequest(context.Context) } -// C is the basic BaseController type that can be used as an embedded anonymous field -// to custom end-dev controllers. -// -// func(c *ExampleController) Get() string | -// (string, string) | -// (string, int) | -// int | -// (int, string | -// (string, error) | -// bool | -// (any, bool) | -// error | -// (int, error) | -// (customStruct, error) | -// customStruct | -// (customStruct, int) | -// (customStruct, string) | -// Result or (Result, error) -// where Get is an HTTP Method func. -// -// Look `core/router#APIBuilder#Controller` method too. -// -// It completes the `activator.BaseController` interface. -// -// Example at: https://github.com/kataras/iris/tree/master/_examples/mvc/overview/web/controllers. -// Example usage at: https://github.com/kataras/iris/blob/master/mvc/method_result_test.go#L17. -type C struct { - // The current context.Context. - // - // we have to name it for two reasons: - // 1: can't ignore these via reflection, it doesn't give an option to - // see if the functions is derived from another type. - // 2: end-developer may want to use some method functions - // or any fields that could be conflict with the context's. - Ctx context.Context -} - -var _ BaseController = &C{} - -// BeginRequest does nothing anymore, is here to complet ethe `BaseController` interface. -// BaseController is not required anymore, `Ctx` is binded automatically by the engine's -// wrapped Handler. -func (c *C) BeginRequest(ctx context.Context) {} - -// EndRequest does nothing, is here to complete the `BaseController` interface. -func (c *C) EndRequest(ctx context.Context) {} - // ControllerActivator returns a new controller type info description. // Its functionality can be overriden by the end-dev. type ControllerActivator struct { @@ -244,7 +191,10 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware . // get the function's input arguments' bindings. funcDependencies := c.Dependencies.Clone() funcDependencies.AddValue(pathParams...) + + // fmt.Printf("for %s | values: %s\n", funcName, funcDependencies.Values) funcInjector := funcDependencies.Func(m.Func) + // fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length) // the element value, not the pointer, wil lbe used to create a // new controller on each incoming request. diff --git a/mvc2/controller_handle_test.go b/mvc/controller_handle_test.go similarity index 93% rename from mvc2/controller_handle_test.go rename to mvc/controller_handle_test.go index 4857aeea..9be5dd29 100644 --- a/mvc2/controller_handle_test.go +++ b/mvc/controller_handle_test.go @@ -1,29 +1,22 @@ -package mvc2_test +package mvc_test import ( "testing" "github.com/kataras/iris" + "github.com/kataras/iris/context" "github.com/kataras/iris/httptest" - . "github.com/kataras/iris/mvc2" + + . "github.com/kataras/iris/mvc" ) type testControllerHandle struct { - C + Ctx context.Context Service TestService reqField string } -func (c *testControllerHandle) Get() string { - return "index" -} - -func (c *testControllerHandle) BeginRequest(ctx iris.Context) { - c.C.BeginRequest(ctx) - c.reqField = ctx.URLParam("reqfield") -} - func (c *testControllerHandle) BeforeActivate(ca *ControllerActivator) { // BeforeActivate(t *mvc.TController) { ca.Handle("GET", "/histatic", "HiStatic") ca.Handle("GET", "/hiservice", "HiService") @@ -31,6 +24,16 @@ func (c *testControllerHandle) BeforeActivate(ca *ControllerActivator) { // Befo ca.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy") } +func (c *testControllerHandle) BeginRequest(ctx iris.Context) { + c.reqField = ctx.URLParam("reqfield") +} + +func (c *testControllerHandle) EndRequest(ctx iris.Context) {} + +func (c *testControllerHandle) Get() string { + return "index" +} + func (c *testControllerHandle) HiStatic() string { return c.reqField } diff --git a/mvc2/controller_method_parser.go b/mvc/controller_method_parser.go similarity index 79% rename from mvc2/controller_method_parser.go rename to mvc/controller_method_parser.go index 63d7a671..c5d0ec50 100644 --- a/mvc2/controller_method_parser.go +++ b/mvc/controller_method_parser.go @@ -1,4 +1,4 @@ -package mvc2 +package mvc import ( "errors" @@ -96,20 +96,31 @@ func (l *methodLexer) peekPrev() (w string) { } var posWords = map[int]string{ - 0: "", - 1: "first", - 2: "second", - 3: "third", - 4: "forth", - 5: "five", - 6: "sixth", - 7: "seventh", - 8: "eighth", - 9: "ninth", + 0: "", + 1: "first", + 2: "second", + 3: "third", + 4: "forth", + 5: "five", + 6: "sixth", + 7: "seventh", + 8: "eighth", + 9: "ninth", + 10: "tenth", + 11: "eleventh", + 12: "twelfth", + 13: "thirteenth", + 14: "fourteenth", + 15: "fifteenth", + 16: "sixteenth", + 17: "seventeenth", + 18: "eighteenth", + 19: "nineteenth", + 20: "twentieth", } func genParamKey(argIdx int) string { - return "param" + posWords[argIdx] // paramfirst, paramsecond... + return "arg" + posWords[argIdx] // argfirst, argsecond... } type methodParser struct { @@ -176,7 +187,7 @@ func (p *methodParser) parse() (method, path string, err error) { // continue // } - if path, err = p.parsePathParam(path, w, funcArgPos); err != nil { + if path, funcArgPos, err = p.parsePathParam(path, w, funcArgPos); err != nil { return "", "", err } @@ -184,24 +195,22 @@ func (p *methodParser) parse() (method, path string, err error) { } // static path. path += "/" + strings.ToLower(w) - } - return } -func (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (string, error) { +func (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (string, int, error) { typ := p.fn.Type if typ.NumIn() <= funcArgPos { // By found but input arguments are not there, so act like /by path without restricts. path += "/" + strings.ToLower(w) - return path, nil + return path, funcArgPos, nil } var ( - paramKey = genParamKey(funcArgPos) // paramfirst, paramsecond... + paramKey = genParamKey(funcArgPos) // argfirst, argsecond... paramType = ast.ParamTypeString // default string ) @@ -216,10 +225,19 @@ func (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (st // it's not wildcard, so check base on our available macro types. paramType = pType } else { - return "", errors.New("invalid syntax for " + p.fn.Name) + if typ.NumIn() > funcArgPos { + // has more input arguments but we are not in the correct + // index now, maybe the first argument was an `iris/context.Context` + // so retry with the "funcArgPos" incremented. + // + // the "funcArgPos" will be updated to the caller as well + // because we return it as well. + return p.parsePathParam(path, w, funcArgPos+1) + } + return "", 0, errors.New("invalid syntax for " + p.fn.Name) } - // /{paramfirst:path}, /{paramfirst:long}... + // /{argfirst:path}, /{argfirst:long}... path += fmt.Sprintf("/{%s:%s}", paramKey, paramType.String()) if nextWord == "" && typ.NumIn() > funcArgPos+1 { @@ -232,5 +250,5 @@ func (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (st return p.parsePathParam(path, nextWord, funcArgPos+1) } - return path, nil + return path, funcArgPos, nil } diff --git a/mvc2/controller_test.go b/mvc/controller_test.go similarity index 93% rename from mvc2/controller_test.go rename to mvc/controller_test.go index 9ab27476..b7609690 100644 --- a/mvc2/controller_test.go +++ b/mvc/controller_test.go @@ -1,5 +1,5 @@ // black-box testing -package mvc2_test +package mvc_test import ( "testing" @@ -8,56 +8,57 @@ import ( "github.com/kataras/iris/context" "github.com/kataras/iris/core/router" "github.com/kataras/iris/httptest" - . "github.com/kataras/iris/mvc2" + + . "github.com/kataras/iris/mvc" ) type testController struct { - C + Ctx context.Context } -var writeMethod = func(c C) { - c.Ctx.Writef(c.Ctx.Method()) +var writeMethod = func(ctx context.Context) { + ctx.Writef(ctx.Method()) } func (c *testController) Get() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Post() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Put() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Delete() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Connect() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Head() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Patch() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Options() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testController) Trace() { - writeMethod(c.C) + writeMethod(c.Ctx) } type ( - testControllerAll struct{ C } - testControllerAny struct{ C } // exactly the same as All. + testControllerAll struct{ Ctx context.Context } + testControllerAny struct{ Ctx context.Context } // exactly the same as All. ) func (c *testControllerAll) All() { - writeMethod(c.C) + writeMethod(c.Ctx) } func (c *testControllerAny) Any() { - writeMethod(c.C) + writeMethod(c.Ctx) } func TestControllerMethodFuncs(t *testing.T) { @@ -83,7 +84,7 @@ func TestControllerMethodFuncs(t *testing.T) { } type testControllerBeginAndEndRequestFunc struct { - C + Ctx context.Context Username string } @@ -93,14 +94,12 @@ type testControllerBeginAndEndRequestFunc struct { // useful when more than one methods using the // same request values or context's function calls. func (c *testControllerBeginAndEndRequestFunc) BeginRequest(ctx context.Context) { - c.C.BeginRequest(ctx) c.Username = ctx.Params().Get("username") } // called after every method (Get() or Post()). func (c *testControllerBeginAndEndRequestFunc) EndRequest(ctx context.Context) { ctx.Writef("done") // append "done" to the response - c.C.EndRequest(ctx) } func (c *testControllerBeginAndEndRequestFunc) Get() { @@ -187,7 +186,7 @@ type Model struct { } type testControllerEndRequestAwareness struct { - C + Ctx context.Context } func (c *testControllerEndRequestAwareness) Get() { @@ -223,9 +222,9 @@ func writeModels(ctx context.Context, names ...string) { } } +func (c *testControllerEndRequestAwareness) BeginRequest(ctx context.Context) {} func (c *testControllerEndRequestAwareness) EndRequest(ctx context.Context) { writeModels(ctx, "TestModel", "myModel") - c.C.EndRequest(ctx) } func TestControllerEndRequestAwareness(t *testing.T) { @@ -249,7 +248,8 @@ type testBindType struct { } type testControllerBindStruct struct { - C + Ctx context.Context + // should start with upper letter of course TitlePointer *testBindType // should have the value of the "myTitlePtr" on test TitleValue testBindType // should have the value of the "myTitleV" on test @@ -320,6 +320,8 @@ func (c *testCtrl0) EndRequest(ctx context.Context) { } type testCtrl00 struct { + Ctx context.Context + testCtrl000 } @@ -330,9 +332,9 @@ type testCtrl000 struct { } type testCtrl0000 struct { - C } +func (c *testCtrl0000) BeginRequest(ctx context.Context) {} func (c *testCtrl0000) EndRequest(ctx context.Context) { ctx.Writef("finish") } @@ -354,11 +356,11 @@ func TestControllerInsideControllerRecursively(t *testing.T) { Status(iris.StatusOK).Body().Equal(expected) } -type testControllerRelPathFromFunc struct{ C } +type testControllerRelPathFromFunc struct{} +func (c *testControllerRelPathFromFunc) BeginRequest(ctx context.Context) {} func (c *testControllerRelPathFromFunc) EndRequest(ctx context.Context) { ctx.Writef("%s:%s", ctx.Method(), ctx.Path()) - c.C.EndRequest(ctx) } func (c *testControllerRelPathFromFunc) Get() {} @@ -416,8 +418,6 @@ func TestControllerRelPathFromFunc(t *testing.T) { } type testControllerActivateListener struct { - C - TitlePointer *testBindType } diff --git a/mvc2/di/di.go b/mvc/di/di.go similarity index 100% rename from mvc2/di/di.go rename to mvc/di/di.go diff --git a/mvc2/di/func.go b/mvc/di/func.go similarity index 93% rename from mvc2/di/func.go rename to mvc/di/func.go index ef9d76ca..be206432 100644 --- a/mvc2/di/func.go +++ b/mvc/di/func.go @@ -1,8 +1,6 @@ package di -import ( - "reflect" -) +import "reflect" type ( targetFuncInput struct { @@ -50,7 +48,6 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, v InputIndex: i, Object: b, }) - continue } } @@ -69,6 +66,7 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, v } if b.IsAssignable(inTyp) { + // println(inTyp.String() + " is assignable to " + val.Type().String()) // 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{ @@ -82,8 +80,9 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, v } } - s.Length = n - s.Valid = len(s.inputs) > 0 + // s.Length = n + s.Length = len(s.inputs) + s.Valid = s.Length > 0 return s } diff --git a/mvc2/di/object.go b/mvc/di/object.go similarity index 100% rename from mvc2/di/object.go rename to mvc/di/object.go diff --git a/mvc2/di/reflect.go b/mvc/di/reflect.go similarity index 100% rename from mvc2/di/reflect.go rename to mvc/di/reflect.go diff --git a/mvc2/di/struct.go b/mvc/di/struct.go similarity index 100% rename from mvc2/di/struct.go rename to mvc/di/struct.go diff --git a/mvc2/di/values.go b/mvc/di/values.go similarity index 99% rename from mvc2/di/values.go rename to mvc/di/values.go index 129fed0c..64301aee 100644 --- a/mvc2/di/values.go +++ b/mvc/di/values.go @@ -1,8 +1,6 @@ package di -import ( - "reflect" -) +import "reflect" type Values []reflect.Value diff --git a/mvc/engine.go b/mvc/engine.go new file mode 100644 index 00000000..e835fa4a --- /dev/null +++ b/mvc/engine.go @@ -0,0 +1,105 @@ +package mvc + +import ( + "github.com/kataras/golog" + "github.com/kataras/iris/mvc/di" + + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" +) + +// Engine contains the Dependencies which will be binded +// to the controller(s) or handler(s) that can be created +// using the Engine's `Handler` and `Controller` methods. +// +// This is not exported for being used by everyone, use it only when you want +// to share engines between multi mvc.go#Application +// or make custom mvc handlers that can be used on the standard +// iris' APIBuilder. The last one reason is the most useful here, +// although end-devs can use the `MakeHandler` as well. +// +// For a more high-level structure please take a look at the "mvc.go#Application". +type Engine struct { + Dependencies *di.D +} + +// NewEngine returns a new engine, a container for dependencies and a factory +// for handlers and controllers, this is used internally by the `mvc#Application` structure. +// Please take a look at the structure's documentation for more information. +func NewEngine() *Engine { + return &Engine{ + Dependencies: di.New().Hijack(hijacker).GoodFunc(typeChecker), + } +} + +// Clone creates and returns a new engine with the parent's(current) Dependencies. +// It copies the current "e" dependencies and returns a new engine. +func (e *Engine) Clone() *Engine { + child := NewEngine() + child.Dependencies = e.Dependencies.Clone() + return child +} + +// Handler accepts a "handler" function which can accept any input arguments that match +// with the Engine's `Dependencies` and any output result; like string, int (string,int), +// custom structs, Result(View | Response) and anything you already know that mvc implementation supports. +// It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application, +// as middleware or as simple route handler or subdomain's handler. +func (e *Engine) Handler(handler interface{}) context.Handler { + h, err := MakeHandler(handler, e.Dependencies.Values...) + if err != nil { + golog.Errorf("mvc handler: %v", err) + } + return h +} + +// Controller accepts a sub router and registers any custom struct +// as controller, if struct doesn't have any compatible methods +// neither are registered via `ControllerActivator`'s `Handle` method +// then the controller is not registered at all. +// +// A Controller may have one or more methods +// that are wrapped to a handler and registered as routes before the server ran. +// The controller's method can accept any input argument that are previously binded +// via the dependencies or route's path accepts dynamic path parameters. +// The controller's fields are also bindable via the dependencies, either a +// static value (service) or a function (dynamically) which accepts a context +// and returns a single value (this type is being used to find the relative field or method's input argument). +// +// func(c *ExampleController) Get() string | +// (string, string) | +// (string, int) | +// int | +// (int, string | +// (string, error) | +// bool | +// (any, bool) | +// error | +// (int, error) | +// (customStruct, error) | +// customStruct | +// (customStruct, int) | +// (customStruct, string) | +// Result or (Result, error) +// where Get is an HTTP Method func. +// +// Examples at: https://github.com/kataras/iris/tree/master/_examples/mvc. +func (e *Engine) Controller(router router.Party, controller interface{}, beforeActivate ...func(*ControllerActivator)) { + ca := newControllerActivator(router, controller, e.Dependencies) + + // give a priority to the "beforeActivate" + // callbacks, if any. + for _, cb := range beforeActivate { + cb(ca) + } + + // check if controller has an "BeforeActivate" function + // which accepts the controller activator and call it. + if activateListener, ok := controller.(interface { + BeforeActivate(*ControllerActivator) + }); ok { + activateListener.BeforeActivate(ca) + } + + ca.activate() +} diff --git a/mvc2/engine_handler_test.go b/mvc/engine_handler_test.go similarity index 88% rename from mvc2/engine_handler_test.go rename to mvc/engine_handler_test.go index 9b4cb417..a2a01e30 100644 --- a/mvc2/engine_handler_test.go +++ b/mvc/engine_handler_test.go @@ -1,11 +1,11 @@ -package mvc2_test +package mvc_test // black-box in combination with the handler_test import ( "testing" - . "github.com/kataras/iris/mvc2" + . "github.com/kataras/iris/mvc" ) func TestMvcEngineInAndHandler(t *testing.T) { diff --git a/mvc2/func_result.go b/mvc/func_result.go similarity index 99% rename from mvc2/func_result.go rename to mvc/func_result.go index 47a7030d..9b64c033 100644 --- a/mvc2/func_result.go +++ b/mvc/func_result.go @@ -1,11 +1,11 @@ -package mvc2 +package mvc import ( "reflect" "strings" "github.com/fatih/structs" - "github.com/kataras/iris/mvc2/di" + "github.com/kataras/iris/mvc/di" "github.com/kataras/iris/context" ) diff --git a/mvc2/func_result_test.go b/mvc/func_result_test.go similarity index 98% rename from mvc2/func_result_test.go rename to mvc/func_result_test.go index 92be4d55..7b9b04a7 100644 --- a/mvc2/func_result_test.go +++ b/mvc/func_result_test.go @@ -1,4 +1,4 @@ -package mvc2_test +package mvc_test import ( "errors" @@ -7,14 +7,15 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" "github.com/kataras/iris/httptest" - . "github.com/kataras/iris/mvc2" + + . "github.com/kataras/iris/mvc" ) // activator/methodfunc/func_caller.go. // and activator/methodfunc/func_result_dispatcher.go type testControllerMethodResult struct { - C + Ctx context.Context } func (c *testControllerMethodResult) Get() Result { @@ -105,7 +106,7 @@ func TestControllerMethodResult(t *testing.T) { } type testControllerMethodResultTypes struct { - C + Ctx context.Context } func (c *testControllerMethodResultTypes) GetText() string { @@ -227,16 +228,13 @@ func TestControllerMethodResultTypes(t *testing.T) { type testControllerViewResultRespectCtxViewData struct { T *testing.T - 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 overridden by return View {Data: context.Map...} dataWritten := ctx.GetViewData() diff --git a/mvc2/handler.go b/mvc/handler.go similarity index 92% rename from mvc2/handler.go rename to mvc/handler.go index 1213732b..0df5fb42 100644 --- a/mvc2/handler.go +++ b/mvc/handler.go @@ -1,8 +1,8 @@ -package mvc2 +package mvc import ( "fmt" - "github.com/kataras/iris/mvc2/di" + "github.com/kataras/iris/mvc/di" "reflect" "runtime" @@ -39,8 +39,8 @@ func MustMakeHandler(handler interface{}, bindValues ...reflect.Value) context.H return h } -// MakeHandler accepts a "handler" function which can accept any input that matches -// with the "binders" and any output, that matches the mvc types, like string, int (string,int), +// MakeHandler accepts a "handler" function which can accept any input arguments that match +// with the "bindValues" types and any output result, that matches the mvc types, like string, int (string,int), // custom structs, Result(View | Response) and anything that you already know that mvc implementation supports, // and returns a low-level `context/iris.Handler` which can be used anywhere in the Iris Application, // as middleware or as simple route handler or party handler or subdomain handler-router. diff --git a/mvc2/handler_test.go b/mvc/handler_test.go similarity index 98% rename from mvc2/handler_test.go rename to mvc/handler_test.go index 205b1602..fb22ea24 100644 --- a/mvc2/handler_test.go +++ b/mvc/handler_test.go @@ -1,4 +1,4 @@ -package mvc2_test +package mvc_test // black-box @@ -9,7 +9,8 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/httptest" - . "github.com/kataras/iris/mvc2" + + . "github.com/kataras/iris/mvc" ) // dynamic func diff --git a/mvc2/ideas/1/main.go b/mvc/ideas/1/main.go similarity index 98% rename from mvc2/ideas/1/main.go rename to mvc/ideas/1/main.go index a63aeda6..1de61ec9 100644 --- a/mvc2/ideas/1/main.go +++ b/mvc/ideas/1/main.go @@ -6,7 +6,7 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/sessions" - mvc "github.com/kataras/iris/mvc2" + "github.com/kataras/iris/mvc" ) func main() { diff --git a/mvc2/mvc.go b/mvc/mvc.go similarity index 99% rename from mvc2/mvc.go rename to mvc/mvc.go index c1a7e04b..98241d57 100644 --- a/mvc2/mvc.go +++ b/mvc/mvc.go @@ -1,4 +1,4 @@ -package mvc2 +package mvc import "github.com/kataras/iris/core/router" diff --git a/mvc2/path_param_binder.go b/mvc/param.go similarity index 68% rename from mvc2/path_param_binder.go rename to mvc/param.go index 1018cfb7..4596afca 100644 --- a/mvc2/path_param_binder.go +++ b/mvc/param.go @@ -1,4 +1,4 @@ -package mvc2 +package mvc import ( "reflect" @@ -16,20 +16,36 @@ func getPathParamsForInput(params []macro.TemplateParam, funcIn ...reflect.Type) return } - funcInIdx := 0 - // it's a valid param type. - for _, p := range params { - in := funcIn[funcInIdx] - paramType := p.Type - paramName := p.Name - // fmt.Printf("%s input arg type vs %s param type\n", in.Kind().String(), p.Type.Kind().String()) - if paramType.Assignable(in.Kind()) { - // fmt.Printf("path_param_binder.go: bind path param func for paramName = '%s' and paramType = '%s'\n", paramName, paramType.String()) - values = append(values, makeFuncParamGetter(paramType, paramName)) + consumedParams := make(map[int]bool, 0) + for _, in := range funcIn { + for j, p := range params { + if _, consumed := consumedParams[j]; consumed { + continue + } + paramType := p.Type + paramName := p.Name + // fmt.Printf("%s input arg type vs %s param type\n", in.Kind().String(), p.Type.Kind().String()) + if paramType.Assignable(in.Kind()) { + consumedParams[j] = true + // fmt.Printf("path_param_binder.go: bind path param func for paramName = '%s' and paramType = '%s'\n", paramName, paramType.String()) + values = append(values, makeFuncParamGetter(paramType, paramName)) + } } - - funcInIdx++ } + // funcInIdx := 0 + // // it's a valid param type. + // for _, p := range params { + // in := funcIn[funcInIdx] + // paramType := p.Type + // paramName := p.Name + // // fmt.Printf("%s input arg type vs %s param type\n", in.Kind().String(), p.Type.Kind().String()) + // if paramType.Assignable(in.Kind()) { + // // fmt.Printf("path_param_binder.go: bind path param func for paramName = '%s' and paramType = '%s'\n", paramName, paramType.String()) + // values = append(values, makeFuncParamGetter(paramType, paramName)) + // } + + // funcInIdx++ + // } return } diff --git a/mvc2/path_param_binder_test.go b/mvc/param_test.go similarity index 99% rename from mvc2/path_param_binder_test.go rename to mvc/param_test.go index e03555b2..f9c63329 100644 --- a/mvc2/path_param_binder_test.go +++ b/mvc/param_test.go @@ -1,4 +1,4 @@ -package mvc2 +package mvc import ( "testing" diff --git a/mvc/reflect.go b/mvc/reflect.go new file mode 100644 index 00000000..c28f87fc --- /dev/null +++ b/mvc/reflect.go @@ -0,0 +1,54 @@ +package mvc + +import ( + "reflect" + + "github.com/kataras/iris/context" + "github.com/kataras/iris/mvc/di" +) + +var baseControllerTyp = reflect.TypeOf((*BaseController)(nil)).Elem() + +func isBaseController(ctrlTyp reflect.Type) bool { + return ctrlTyp.Implements(baseControllerTyp) +} + +var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem() + +func isContext(inTyp reflect.Type) bool { + return inTyp.Implements(contextTyp) +} + +func getInputArgsFromFunc(funcTyp reflect.Type) []reflect.Type { + n := funcTyp.NumIn() + funcIn := make([]reflect.Type, n, n) + for i := 0; i < n; i++ { + funcIn[i] = funcTyp.In(i) + } + return funcIn +} + +var ( + typeChecker = func(fn reflect.Type) bool { + // valid if that single input arg is a typeof context.Context. + return fn.NumIn() == 1 && isContext(fn.In(0)) + } + + hijacker = 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(ctxValue []reflect.Value) reflect.Value { + return ctxValue[0] + }, + }, true + } +) diff --git a/mvc2/session.go b/mvc/session.go similarity index 97% rename from mvc2/session.go rename to mvc/session.go index 173a3c8c..b09c764f 100644 --- a/mvc2/session.go +++ b/mvc/session.go @@ -1,4 +1,4 @@ -package mvc2 +package mvc import ( "github.com/kataras/iris/context" diff --git a/mvc2/session_controller.go b/mvc/session_controller.go similarity index 89% rename from mvc2/session_controller.go rename to mvc/session_controller.go index ab6dd394..39bc9b0a 100644 --- a/mvc2/session_controller.go +++ b/mvc/session_controller.go @@ -1,4 +1,4 @@ -package mvc2 +package mvc import ( "github.com/kataras/iris/context" @@ -11,8 +11,6 @@ var defaultSessionManager = sessions.New(sessions.Config{}) // which requires a binded session manager in order to give // direct access to the current client's session via its `Session` field. type SessionController struct { - C - Manager *sessions.Sessions Session *sessions.Session } @@ -30,10 +28,8 @@ func (s *SessionController) BeforeActivate(ca *ControllerActivator) { } } -// BeginRequest calls the Controller's BeginRequest -// and tries to initialize the current user's Session. +// BeginRequest initializes the current user's Session. func (s *SessionController) BeginRequest(ctx context.Context) { - s.C.BeginRequest(ctx) if s.Manager == nil { ctx.Application().Logger().Errorf(`MVC SessionController: sessions manager is nil, report this as a bug because the SessionController should predict this on its activation state and use a default one automatically`) @@ -42,3 +38,6 @@ because the SessionController should predict this on its activation state and us s.Session = s.Manager.Start(ctx) } + +// EndRequest is here to complete the `BaseController`. +func (s *SessionController) EndRequest(ctx context.Context) {} diff --git a/mvc2/bind.go b/mvc2/bind.go deleted file mode 100644 index 63f29cb1..00000000 --- a/mvc2/bind.go +++ /dev/null @@ -1,34 +0,0 @@ -package mvc2 - -import ( - "github.com/kataras/iris/mvc2/di" - "reflect" -) - -var ( - typeChecker = func(fn reflect.Type) bool { - // invalid if that single input arg is not a typeof context.Context. - return isContext(fn.In(0)) - } - - hijacker = func(fieldOrFuncInput reflect.Type) (*di.BindObject, bool) { - if isContext(fieldOrFuncInput) { - return newContextBindObject(), true - } - return nil, false - } -) - -// 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 -// 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`. -func newContextBindObject() *di.BindObject { - return &di.BindObject{ - Type: contextTyp, - BindType: di.Dynamic, - ReturnValue: func(ctxValue []reflect.Value) reflect.Value { - return ctxValue[0] - }, - } -} diff --git a/mvc2/engine.go b/mvc2/engine.go deleted file mode 100644 index 0dc900d6..00000000 --- a/mvc2/engine.go +++ /dev/null @@ -1,53 +0,0 @@ -package mvc2 - -import ( - "github.com/kataras/iris/mvc2/di" - "github.com/kataras/golog" - - "github.com/kataras/iris/context" - "github.com/kataras/iris/core/router" -) - -type Engine struct { - Dependencies *di.D -} - -func NewEngine() *Engine { - return &Engine{ - Dependencies: di.New().Hijack(hijacker).GoodFunc(typeChecker), - } -} - -func (e *Engine) Clone() *Engine { - child := NewEngine() - child.Dependencies = e.Dependencies.Clone() - return child -} - -func (e *Engine) Handler(handler interface{}) context.Handler { - h, err := MakeHandler(handler, e.Dependencies.Values...) - if err != nil { - golog.Errorf("mvc handler: %v", err) - } - return h -} - -func (e *Engine) Controller(router router.Party, controller interface{}, beforeActivate ...func(*ControllerActivator)) { - ca := newControllerActivator(router, controller, e.Dependencies) - - // give a priority to the "beforeActivate" - // callbacks, if any. - for _, cb := range beforeActivate { - cb(ca) - } - - // check if controller has an "BeforeActivate" function - // which accepts the controller activator and call it. - if activateListener, ok := controller.(interface { - BeforeActivate(*ControllerActivator) - }); ok { - activateListener.BeforeActivate(ca) - } - - ca.activate() -} diff --git a/mvc2/reflect.go b/mvc2/reflect.go deleted file mode 100644 index e18ece73..00000000 --- a/mvc2/reflect.go +++ /dev/null @@ -1,28 +0,0 @@ -package mvc2 - -import ( - "reflect" - - "github.com/kataras/iris/context" -) - -var baseControllerTyp = reflect.TypeOf((*BaseController)(nil)).Elem() - -func isBaseController(ctrlTyp reflect.Type) bool { - return ctrlTyp.Implements(baseControllerTyp) -} - -var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem() - -func isContext(inTyp reflect.Type) bool { - return inTyp.Implements(contextTyp) -} - -func getInputArgsFromFunc(funcTyp reflect.Type) []reflect.Type { - n := funcTyp.NumIn() - funcIn := make([]reflect.Type, n, n) - for i := 0; i < n; i++ { - funcIn[i] = funcTyp.In(i) - } - return funcIn -}