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).