From 8a9a498316cc2e8816c7c99339be46317e41e6e8 Mon Sep 17 00:00:00 2001 From: hiveminded Date: Thu, 7 Sep 2017 16:20:31 +0300 Subject: [PATCH] update to version 8.4.1 :heart: https://github.com/kataras/iris/blob/master/HISTORY.md#th-07-september-2017--v841 Former-commit-id: 9e5f0a08049b83605aa847b8f51fb856427354a6 --- HISTORY.md | 68 ++++++++++++++++++- README.md | 2 +- VERSION | 2 +- _examples/routing/dynamic-path/main.go | 7 ++ context/context.go | 12 ++++ core/host/supervisor.go | 7 +- core/memstore/memstore.go | 8 +++ core/router/macro/interpreter/ast/ast.go | 10 ++- .../macro/interpreter/parser/parser_test.go | 7 ++ core/router/macro/macro.go | 29 ++++++-- doc.go | 14 +++- iris.go | 2 +- mvc/activator/methodfunc/func_caller.go | 7 ++ mvc/activator/methodfunc/func_path.go | 12 ++-- mvc/controller_test.go | 22 ++++-- 15 files changed, 184 insertions(+), 25 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b955dccd..b9005604 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,7 +2,7 @@ ### Looking for free support? - http://support.iris-go.com + http://support.iris-go.com https://kataras.rocket.chat/channel/iris ### Looking for previous versions? @@ -18,6 +18,72 @@ 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`. +# Th, 07 September 2017 | v8.4.1 + +## Routing + +Add a macro type for booleans: `app.Get("/mypath/{paramName:boolean}", myHandler)`. + +```sh ++------------------------+ +| {param:boolean} | ++------------------------+ +bool type +only "1" or "t" or "T" or "TRUE" or "true" or "True" +or "0" or "f" or "F" or "FALSE" or "false" or "False" +``` + +Add `context.Params().GetBool(paramName string) (bool, error)` respectfully. + +```go +app := iris.New() +app.Get("/mypath/{has:boolean}", func(ctx iris.Context){ // <-- + // boolean first return value + // error as second return value + // + // error will be always nil here because + // we use the {has:boolean} so router + // makes sure that the parameter is a boolean + // otherwise it will return a 404 not found http error code + // skipping the call of this handler. + has, _ := ctx.Params().GetBool("has") // <-- + if has { + ctx.HTML("it's true") + }else { + ctx.HTML("it's false") + } +}) +// [...] +``` + +## MVC + +Support for boolean method receivers, i.e `GetBy(bool), PostBy(bool)...`. + + +```go +app := iris.New() + +app.Controller("/equality", new(Controller)) +``` + +```go +type Controller struct { + iris.Controller +} + +// handles the "/equality" path. +func (c *Controller) Get() { + +} + +// registers and handles the path: "/equality/{param:boolean}". +func (c *Controller) GetBy(is bool) { // <-- + // [...] +} +``` + +> Supported types for method functions receivers are: int, int64, bool and string. # Su, 27 August 2017 | v8.4.0 diff --git a/README.md b/README.md index 83a9d687..0186879e 100644 --- a/README.md +++ b/README.md @@ -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#su-27-august-2017--v840) +* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#th-07-september-2017--v841) * [Learn](#-learn) * [HTTP Listening](_examples/#http-listening) * [Configuration](_examples/#configuration) diff --git a/VERSION b/VERSION index c5ec764b..bf677efe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.4.0:https://github.com/kataras/iris/blob/master/HISTORY.md#su-27-august-2017--v840 \ No newline at end of file +8.4.1:https://github.com/kataras/iris/blob/master/HISTORY.md#th-07-september-2017--v841 \ No newline at end of file diff --git a/_examples/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go index 230f4acf..b7875b9a 100644 --- a/_examples/routing/dynamic-path/main.go +++ b/_examples/routing/dynamic-path/main.go @@ -47,6 +47,13 @@ func main() { // int64 type // only numbers (0-9) // + // +------------------------+ + // | {param:boolean} | + // +------------------------+ + // bool type + // only "1" or "t" or "T" or "TRUE" or "true" or "True" + // or "0" or "f" or "F" or "FALSE" or "false" or "False" + // // +------------------------+ // | {param:alphabetical} | // +------------------------+ diff --git a/context/context.go b/context/context.go index 2860a62f..7c33059e 100644 --- a/context/context.go +++ b/context/context.go @@ -107,6 +107,14 @@ func (r RequestParams) GetInt64(key string) (int64, error) { return r.store.GetInt64(key) } +// GetBool returns the user's value as bool, based on its key. +// a string which is "1" or "t" or "T" or "TRUE" or "true" or "True" +// or "0" or "f" or "F" or "FALSE" or "false" or "False". +// Any other value returns an error. +func (r RequestParams) GetBool(key string) (bool, error) { + return r.store.GetBool(key) +} + // GetDecoded returns the url-query-decoded user's value based on its key. func (r RequestParams) GetDecoded(key string) string { return DecodeQuery(DecodeQuery(r.Get(key))) @@ -1368,6 +1376,10 @@ func (ctx *context) Redirect(urlToRedirect string, statusHeader ...int) { // get the previous status code given by the end-developer. status := ctx.GetStatusCode() + if status < 300 { // the previous is not a RCF-valid redirect status. + status = 0 + } + if len(statusHeader) > 0 { // check if status code is passed via receivers. if s := statusHeader[0]; s > 0 { diff --git a/core/host/supervisor.go b/core/host/supervisor.go index 40e9c770..48001d8c 100644 --- a/core/host/supervisor.go +++ b/core/host/supervisor.go @@ -254,7 +254,12 @@ func (su *Supervisor) ListenAndServeTLS(certFile string, keyFile string) error { // manually inserted as pre-go 1.9 for any case. cfg.NextProtos = []string{"h2", "http/1.1"} su.Server.TLSConfig = cfg - return su.ListenAndServe() + + // It does nothing more than the su.Server.ListenAndServeTLS anymore. + // - no hurt if we let it as it is + // - no problem if we remove it as well + // but let's comment this as proposed, fewer code is better: + // return su.ListenAndServe() } if su.Server.TLSConfig == nil { diff --git a/core/memstore/memstore.go b/core/memstore/memstore.go index 8b092cc7..3e1ccf40 100644 --- a/core/memstore/memstore.go +++ b/core/memstore/memstore.go @@ -181,6 +181,14 @@ func (r *Store) GetInt64(key string) (int64, error) { return strconv.ParseInt(r.GetString(key), 10, 64) } +// GetBool returns the user's value as bool, based on its key. +// a string which is "1" or "t" or "T" or "TRUE" or "true" or "True" +// or "0" or "f" or "F" or "FALSE" or "false" or "False". +// Any other value returns an error. +func (r *Store) GetBool(key string) (bool, error) { + return strconv.ParseBool(key) +} + // Remove deletes an entry linked to that "key", // returns true if an entry is actually removed. func (r *Store) Remove(key string) bool { diff --git a/core/router/macro/interpreter/ast/ast.go b/core/router/macro/interpreter/ast/ast.go index b67af162..39f0be76 100644 --- a/core/router/macro/interpreter/ast/ast.go +++ b/core/router/macro/interpreter/ast/ast.go @@ -18,13 +18,18 @@ const ( // Declaration: /mypath/{myparam:string} or /mypath{myparam} ParamTypeString // ParamTypeInt is the integer, a number type. - // Allows only numbers (0-9) + // Allows only possitive numbers (0-9) // Declaration: /mypath/{myparam:int} ParamTypeInt // ParamTypeLong is the integer, a number type. - // Allows only numbers (0-9) + // Allows only possitive numbers (0-9) // Declaration: /mypath/{myparam:long} ParamTypeLong + // ParamTypeBoolean is the bool type. + // Allows only "1" or "t" or "T" or "TRUE" or "true" or "True" + // or "0" or "f" or "F" or "FALSE" or "false" or "False". + // Declaration: /mypath/{myparam:boolean} + ParamTypeBoolean // ParamTypeAlphabetical is the alphabetical/letter type type. // Allows letters only (upper or lowercase) // Declaration: /mypath/{myparam:alphabetical} @@ -49,6 +54,7 @@ var paramTypes = map[string]ParamType{ "string": ParamTypeString, "int": ParamTypeInt, "long": ParamTypeLong, + "boolean": ParamTypeBoolean, "alphabetical": ParamTypeAlphabetical, "file": ParamTypeFile, "path": ParamTypePath, diff --git a/core/router/macro/interpreter/parser/parser_test.go b/core/router/macro/interpreter/parser/parser_test.go index 70c8f110..b1ce0ad8 100644 --- a/core/router/macro/interpreter/parser/parser_test.go +++ b/core/router/macro/interpreter/parser/parser_test.go @@ -131,6 +131,13 @@ func TestParseParam(t *testing.T) { Type: ast.ParamTypeLong, ErrorCode: 404, }}, // 8 + {true, + ast.ParamStatement{ + Src: "{has:boolean else 404}", + Name: "has", + Type: ast.ParamTypeBoolean, + ErrorCode: 404, + }}, // 9 } diff --git a/core/router/macro/macro.go b/core/router/macro/macro.go index 865e35eb..1cabc7b8 100644 --- a/core/router/macro/macro.go +++ b/core/router/macro/macro.go @@ -4,6 +4,7 @@ import ( "fmt" "reflect" "regexp" + "strconv" "unicode" "github.com/kataras/iris/core/router/macro/interpreter/ast" @@ -208,17 +209,23 @@ func (m *Macro) getFunc(funcName string) ParamEvaluatorBuilder { // Map contains the default macros mapped to their types. // This is the manager which is used by the caller to register custom -// parameter functions per param-type (String, Int, Long, Alphabetical, File, Path). +// parameter functions per param-type (String, Int, Long, Boolean, Alphabetical, File, Path). type Map struct { // string type // anything String *Macro - // int type - // only numbers (0-9) + // uint type + // only possitive numbers (+0-9) + // it could be uint/uint32 but we keep int for simplicity Int *Macro // long an int64 type - // only numbers (0-9) + // only possitive numbers (+0-9) + // it could be uint64 but we keep int64 for simplicity Long *Macro + // boolean as bool type + // a string which is "1" or "t" or "T" or "TRUE" or "true" or "True" + // or "0" or "f" or "F" or "FALSE" or "false" or "False". + Boolean *Macro // alphabetical/letter type // letters only (upper or lowercase) Alphabetical *Macro @@ -242,9 +249,15 @@ type Map struct { func NewMap() *Map { return &Map{ // it allows everything, so no need for a regexp here. - String: newMacro(func(string) bool { return true }), - Int: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")), - Long: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")), + String: newMacro(func(string) bool { return true }), + Int: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")), + Long: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")), + Boolean: newMacro(func(paramValue string) bool { + // a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$ + // in this case. + _, err := strconv.ParseBool(paramValue) + return err == nil + }), Alphabetical: newMacro(MustNewEvaluatorFromRegexp("^[a-zA-Z ]+$")), File: newMacro(MustNewEvaluatorFromRegexp("^[a-zA-Z0-9_.-]*$")), // it allows everything, we have String and Path as different @@ -265,6 +278,8 @@ func (m *Map) Lookup(typ ast.ParamType) *Macro { return m.Int case ast.ParamTypeLong: return m.Long + case ast.ParamTypeBoolean: + return m.Boolean case ast.ParamTypeAlphabetical: return m.Alphabetical case ast.ParamTypeFile: diff --git a/doc.go b/doc.go index 45e4e859..24360949 100644 --- a/doc.go +++ b/doc.go @@ -832,7 +832,6 @@ Register one or more relative paths and able to get path parameters, i.e - `func(*Controller) PostProfileFollowers()` - `POST:/user/profile/followers` - `func(*Controller) GetBy(id int64)` - `GET:/user/{param:long}` - `func(*Controller) PostBy(id int64)` - `POST:/user/{param:long}` - If `app.Controller("/profile", new(profile.Controller))` - `func(*Controller) GetBy(username string)` - `GET:/profile/{param:string}` @@ -841,6 +840,12 @@ Register one or more relative paths and able to get path parameters, i.e - `func(*Controller) GetByWildard(path string)` - `GET:/assets/{param:path}` + If `app.Controller("/equality", new(profile.Equality))` + + - `func(*Controller) GetBy(is bool)` - `GET:/equality/{param:boolean}` + + Supported types for method functions receivers: int, int64, bool and string. + Using Iris MVC for code reuse @@ -901,6 +906,13 @@ Standard macro types for parameters: int64 type only numbers (0-9) + +------------------------+ + | {param:boolean} | + +------------------------+ + bool type + only "1" or "t" or "T" or "TRUE" or "true" or "True" + or "0" or "f" or "F" or "FALSE" or "false" or "False" + +------------------------+ | {param:alphabetical} | +------------------------+ diff --git a/iris.go b/iris.go index 0e911a61..81691018 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.4.0" + Version = "8.4.1" ) // HTTP status codes as registered with IANA. diff --git a/mvc/activator/methodfunc/func_caller.go b/mvc/activator/methodfunc/func_caller.go index 2ceaa4ac..96ce2cfd 100644 --- a/mvc/activator/methodfunc/func_caller.go +++ b/mvc/activator/methodfunc/func_caller.go @@ -53,6 +53,13 @@ func resolveCaller(p pathInfo) callerFunc { } } + if p.ParamType == paramTypeBoolean { + return func(ctx context.Context, f interface{}) { + paramValue, _ := ctx.Params().GetBool(paramName) + f.(func(bool))(paramValue) + } + } + // else it's string or path, both of them are simple strings. return func(ctx context.Context, f interface{}) { paramValue := ctx.Params().Get(paramName) diff --git a/mvc/activator/methodfunc/func_path.go b/mvc/activator/methodfunc/func_path.go index f9e926b1..26ba8022 100644 --- a/mvc/activator/methodfunc/func_path.go +++ b/mvc/activator/methodfunc/func_path.go @@ -20,15 +20,17 @@ type pathInfo struct { } const ( - paramTypeInt = "int" - paramTypeLong = "long" - paramTypeString = "string" - paramTypePath = "path" + paramTypeInt = "int" + paramTypeLong = "long" + paramTypeBoolean = "boolean" + paramTypeString = "string" + paramTypePath = "path" ) var macroTypes = map[string]string{ "int": paramTypeInt, "int64": paramTypeLong, + "bool": paramTypeBoolean, "string": paramTypeString, // there is "path" param type but it's being captured "on-air" // "file" param type is not supported by the current implementation, yet @@ -71,7 +73,7 @@ func resolveRelativePath(info FuncInfo) (p pathInfo, ok bool) { } } - // int and string are supported. + // int, int64, bool and string are supported. // as there is no way to get the parameter name // we will use the "param" everywhere. suffix := fmt.Sprintf("/{%s:%s}", paramName, paramType) diff --git a/mvc/controller_test.go b/mvc/controller_test.go index b6b3814e..9b7ad13b 100644 --- a/mvc/controller_test.go +++ b/mvc/controller_test.go @@ -444,15 +444,17 @@ func (c *testControllerRelPathFromFunc) EndRequest(ctx context.Context) { c.Controller.EndRequest(ctx) } -func (c *testControllerRelPathFromFunc) Get() {} +func (c *testControllerRelPathFromFunc) Get() {} +func (c *testControllerRelPathFromFunc) GetBy(int64) {} +func (c *testControllerRelPathFromFunc) GetByWildcard(string) {} + func (c *testControllerRelPathFromFunc) GetLogin() {} func (c *testControllerRelPathFromFunc) PostLogin() {} -func (c *testControllerRelPathFromFunc) GetAdminLogin() {} -func (c *testControllerRelPathFromFunc) PutSomethingIntoThis() {} -func (c *testControllerRelPathFromFunc) GetBy(int64) {} +func (c *testControllerRelPathFromFunc) GetAdminLogin() {} -func (c *testControllerRelPathFromFunc) GetByWildcard(string) {} +func (c *testControllerRelPathFromFunc) PutSomethingIntoThis() {} +func (c *testControllerRelPathFromFunc) GetSomethingBy(bool) {} func TestControllerRelPathFromFunc(t *testing.T) { app := iris.New() @@ -461,6 +463,16 @@ func TestControllerRelPathFromFunc(t *testing.T) { e := httptest.New(t, app) e.GET("/").Expect().Status(httptest.StatusOK). Body().Equal("GET:/") + + e.GET("/42").Expect().Status(httptest.StatusOK). + Body().Equal("GET:/42") + e.GET("/something/true").Expect().Status(httptest.StatusOK). + Body().Equal("GET:/something/true") + e.GET("/something/false").Expect().Status(httptest.StatusOK). + Body().Equal("GET:/something/false") + e.GET("/something/truee").Expect().Status(httptest.StatusNotFound) + e.GET("/something/falsee").Expect().Status(httptest.StatusNotFound) + e.GET("/login").Expect().Status(httptest.StatusOK). Body().Equal("GET:/login") e.POST("/login").Expect().Status(httptest.StatusOK).