partial cleanup of the macro pkg and move it from /core/router to the root because it may be used by the end-developers now to ammend the available macros per application

Former-commit-id: 951a5e7a401af25ecaa904ff6463b0def2c87afb
This commit is contained in:
Gerasimos (Makis) Maropoulos 2018-09-29 02:41:51 +03:00
parent bf880033cd
commit 6d9a35ddba
25 changed files with 119 additions and 129 deletions

View File

@ -294,7 +294,7 @@ For example: at [_examples/mvc/basic/main.go line 100](_examples/mvc/basic/main.
- fix `APIBuilder, Party#StaticWeb` and `APIBuilder, Party#StaticEmbedded` wrong strip prefix inside children parties
- keep the `iris, core/router#StaticEmbeddedHandler` and remove the `core/router/APIBuilder#StaticEmbeddedHandler`, (note the `Handler` suffix) it's global and has nothing to do with the `Party` or the `APIBuilder`
- fix high path cleaning between `{}` (we already escape those contents at the [interpreter](core/router/macro/interpreter) level but some symbols are still removed by the higher-level api builder) , i.e `\\` from the string's macro function `regex` contents as reported at [927](https://github.com/kataras/iris/issues/927) by [commit e85b113476eeefffbc7823297cc63cd152ebddfd](https://github.com/kataras/iris/commit/e85b113476eeefffbc7823297cc63cd152ebddfd)
- fix high path cleaning between `{}` (we already escape those contents at the [interpreter](macro/interpreter) level but some symbols are still removed by the higher-level api builder) , i.e `\\` from the string's macro function `regex` contents as reported at [927](https://github.com/kataras/iris/issues/927) by [commit e85b113476eeefffbc7823297cc63cd152ebddfd](https://github.com/kataras/iris/commit/e85b113476eeefffbc7823297cc63cd152ebddfd)
- sync the `golang.org/x/sys/unix` vendor
## The most important

View File

@ -83,7 +83,7 @@ For example: at [_examples/mvc/basic/main.go line 100](_examples/mvc/basic/main.
- fix `APIBuilder, Party#StaticWeb` and `APIBuilder, Party#StaticEmbedded` wrong strip prefix inside children parties
- keep the `iris, core/router#StaticEmbeddedHandler` and remove the `core/router/APIBuilder#StaticEmbeddedHandler`, (note the `Handler` suffix) it's global and has nothing to do with the `Party` or the `APIBuilder`
- fix high path cleaning between `{}` (we already escape those contents at the [interpreter](core/router/macro/interpreter) level but some symbols are still removed by the higher-level api builder) , i.e `\\` from the string's macro function `regex` contents as reported at [927](https://github.com/kataras/iris/issues/927) by [commit e85b113476eeefffbc7823297cc63cd152ebddfd](https://github.com/kataras/iris/commit/e85b113476eeefffbc7823297cc63cd152ebddfd)
- fix high path cleaning between `{}` (we already escape those contents at the [interpreter](macro/interpreter) level but some symbols are still removed by the higher-level api builder) , i.e `\\` from the string's macro function `regex` contents as reported at [927](https://github.com/kataras/iris/issues/927) by [commit e85b113476eeefffbc7823297cc63cd152ebddfd](https://github.com/kataras/iris/commit/e85b113476eeefffbc7823297cc63cd152ebddfd)
- sync the `golang.org/x/sys/unix` vendor
## The most important

View File

@ -79,7 +79,7 @@ This history entry is not translated yet to the Chinese language yet, please ref
- 修正 `APIBuilder, Party#StaticWeb``APIBuilder, Party#StaticEmbedded` 子分组内的前缀错误
- 保留 `iris, core/router#StaticEmbeddedHandler` 并移除 `core/router/APIBuilder#StaticEmbeddedHandler`, (`Handler` 后缀) 这是全局性的,与 `Party` `APIBuilder` 无关。
- 修正 路径 `{}` 中的路径清理 (我们已经在 [解释器](core/router/macro/interpreter) 级别转义了这些字符, 但是一些符号仍然被更高级别的API构建器删除) , 例如 `\\` 字符串的宏函数正则表达式内容 [927](https://github.com/kataras/iris/issues/927) by [commit e85b113476eeefffbc7823297cc63cd152ebddfd](https://github.com/kataras/iris/commit/e85b113476eeefffbc7823297cc63cd152ebddfd)
- 修正 路径 `{}` 中的路径清理 (我们已经在 [解释器](macro/interpreter) 级别转义了这些字符, 但是一些符号仍然被更高级别的API构建器删除) , 例如 `\\` 字符串的宏函数正则表达式内容 [927](https://github.com/kataras/iris/issues/927) by [commit e85b113476eeefffbc7823297cc63cd152ebddfd](https://github.com/kataras/iris/commit/e85b113476eeefffbc7823297cc63cd152ebddfd)
- 同步 `golang.org/x/sys/unix`
## 重要变更

View File

@ -83,7 +83,7 @@ func main() {
ctx.Writef("myparam1: %s | myparam2: %s", myparam1, myparam2)
})
app.Get("test_uint64/{myparam1:string}/{myparam2:uint64}", func(ctx context.Context) {
app.Get("/test_uint64/{myparam1:string}/{myparam2:uint64}", func(ctx context.Context) {
// works: ctx.Writef("Value of the parameter is: %s\n", ctx.Params().Get("myparam"))
// but better and faster because the macro converts the string to uint64 automatically:
println("type of myparam2 (should be uint64) is: " + reflect.ValueOf(ctx.Params().GetEntry("myparam2").ValueRaw).Kind().String())

View File

@ -1,6 +1,6 @@
package context
import "github.com/kataras/iris/core/router/macro"
import "github.com/kataras/iris/macro"
// RouteReadOnly allows decoupled access to the current route
// inside the context.

View File

@ -15,6 +15,11 @@ import (
)
type (
// ValueSetter is the interface which can be accepted as a generic solution of RequestParams or memstore when Set is the only requirement,
// i.e internally on macro/template/TemplateParam#Eval:paramChanger.
ValueSetter interface {
Set(key string, newValue interface{}) (Entry, bool)
}
// Entry is the entry of the context storage Store - .Values()
Entry struct {
Key string
@ -26,6 +31,8 @@ type (
Store []Entry
)
var _ ValueSetter = (*Store)(nil)
// GetByKindOrNil will try to get this entry's value of "k" kind,
// if value is not that kind it will NOT try to convert it the "k", instead
// it will return nil, except if boolean; then it will return false

View File

@ -9,7 +9,7 @@ import (
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/errors"
"github.com/kataras/iris/core/router/macro"
"github.com/kataras/iris/macro"
)
const (

View File

@ -1,98 +0,0 @@
package router
import (
"fmt"
"strings"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router/macro"
"github.com/kataras/iris/core/router/macro/interpreter/ast"
)
// compileRoutePathAndHandlers receives a route info and returns its parsed/"compiled" path
// and the new handlers (prepend all the macro's handler, if any).
//
// It's not exported for direct use.
func compileRoutePathAndHandlers(handlers context.Handlers, tmpl *macro.Template) (string, context.Handlers, error) {
// parse the path to node's path, now.
path, err := convertTmplToNodePath(tmpl)
if err != nil {
return tmpl.Src, handlers, err
}
// prepend the macro handler to the route, now,
// right before the register to the tree, so routerbuilder.UseGlobal will work as expected.
if len(tmpl.Params) > 0 {
macroEvaluatorHandler := convertTmplToHandler(tmpl)
// may return nil if no really need a macro handler evaluator
if macroEvaluatorHandler != nil {
handlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)
}
}
return path, handlers, nil
}
func convertTmplToNodePath(tmpl *macro.Template) (string, error) {
routePath := tmpl.Src
if len(tmpl.Params) > 0 {
if routePath[len(routePath)-1] == '/' {
routePath = routePath[0 : len(routePath)-2] // remove the last "/" if macro syntax instead of underline's
}
}
// if it has started with {} and it's valid
// then the tmpl.Params will be filled,
// so no any further check needed
for i, p := range tmpl.Params {
if ast.IsTrailing(p.Type) {
if i != len(tmpl.Params)-1 {
return "", fmt.Errorf("parameter type \"%s\" should be putted to the very last of a path", p.Type.Indent())
}
routePath = strings.Replace(routePath, p.Src, WildcardParam(p.Name), 1)
} else {
routePath = strings.Replace(routePath, p.Src, Param(p.Name), 1)
}
}
return routePath, nil
}
// Note: returns nil if not needed, the caller(router) should check for that before adding that on route's Middleware.
func convertTmplToHandler(tmpl *macro.Template) context.Handler {
// check if we have params like: {name:string} or {name} or {anything:path} without else keyword or any functions used inside these params.
// 1. if we don't have, then we don't need to add a handler before the main route's handler (as I said, no performance if macro is not really used)
// 2. if we don't have any named params then we don't need a handler too.
needsMacroHandler := false
for _, p := range tmpl.Params {
if p.CanEval() {
// if at least one needs it, then create the handler.
needsMacroHandler = true
break
}
}
if !needsMacroHandler {
// println("we don't need handler for: " + tmpl.Src)
return nil
}
return func(tmpl macro.Template) context.Handler {
return func(ctx context.Context) {
for _, p := range tmpl.Params {
if !p.CanEval() {
// println(p.Src + " no need to evaluate anything")
continue // allow.
}
if !p.Eval(ctx.Params().Get(p.Name), ctx.Params().Set) {
ctx.StatusCode(p.ErrCode)
ctx.StopExecution()
return
}
}
// if all passed, just continue.
ctx.Next()
}
}(*tmpl)
}

View File

@ -3,7 +3,7 @@ package router
import (
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/errors"
"github.com/kataras/iris/core/router/macro"
"github.com/kataras/iris/macro"
)
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.

View File

@ -7,7 +7,9 @@ import (
"strings"
"github.com/kataras/iris/core/netutil"
"github.com/kataras/iris/core/router/macro/interpreter/lexer"
"github.com/kataras/iris/macro"
"github.com/kataras/iris/macro/interpreter/ast"
"github.com/kataras/iris/macro/interpreter/lexer"
)
const (
@ -31,6 +33,28 @@ func WildcardParam(name string) string {
return prefix(name, WildcardParamStart)
}
func convertTmplToNodePath(tmpl *macro.Template) string {
routePath := tmpl.Src
if len(tmpl.Params) > 0 {
if routePath[len(routePath)-1] == '/' {
routePath = routePath[0 : len(routePath)-2] // remove the last "/" if macro syntax instead of underline's.
}
}
// if it has started with {} and it's valid
// then the tmpl.Params will be filled,
// so no any further check needed.
for _, p := range tmpl.Params {
if ast.IsTrailing(p.Type) {
routePath = strings.Replace(routePath, p.Src, WildcardParam(p.Name), 1)
} else {
routePath = strings.Replace(routePath, p.Src, Param(p.Name), 1)
}
}
return routePath
}
func prefix(s string, prefix string) string {
if !strings.HasPrefix(s, prefix) {
return prefix + s

View File

@ -5,7 +5,8 @@ import (
"strings"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router/macro"
"github.com/kataras/iris/macro"
"github.com/kataras/iris/macro/handler"
)
// Route contains the information about a registered Route.
@ -46,9 +47,11 @@ func NewRoute(method, subdomain, unparsedPath, mainHandlerName string,
return nil, err
}
path, handlers, err := compileRoutePathAndHandlers(handlers, tmpl)
if err != nil {
return nil, err
path := convertTmplToNodePath(tmpl)
// prepend the macro handler to the route, now,
// right before the register to the tree, so APIBuilder#UseGlobal will work as expected.
if macroEvaluatorHandler, ok := handler.MakeHandler(tmpl); ok {
handlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)
}
path = cleanPath(path) // maybe unnecessary here but who cares in this moment

52
macro/handler/handler.go Normal file
View File

@ -0,0 +1,52 @@
// Package handler is the highest level module of the macro package which makes use the rest of the macro package,
// it is mainly used, internally, by the router package.
package handler
import (
"github.com/kataras/iris/context"
"github.com/kataras/iris/macro"
)
// MakeHandler creates and returns a handler from a macro template, the handler evaluates each of the parameters if necessary at all.
// If the template does not contain any dynamic attributes and a special handler is NOT required
// then it returns a nil handler and false as its second output value,
// the caller should check those two values before any further action.
func MakeHandler(tmpl *macro.Template) (context.Handler, bool) {
needsMacroHandler := len(tmpl.Params) > 0
if !needsMacroHandler {
return nil, false
}
// check if we have params like: {name:string} or {name} or {anything:path} without else keyword or any functions used inside these params.
// 1. if we don't have, then we don't need to add a handler before the main route's handler (as I said, no performance if macro is not really used)
// 2. if we don't have any named params then we don't need a handler too.
for _, p := range tmpl.Params {
if p.CanEval() {
// if at least one needs it, then create the handler.
needsMacroHandler = true
break
}
}
if !needsMacroHandler {
return nil, false
}
handler := func(ctx context.Context) {
for _, p := range tmpl.Params {
if !p.CanEval() {
continue // allow.
}
if !p.Eval(ctx.Params().Get(p.Name), ctx.Params()) {
ctx.StatusCode(p.ErrCode)
ctx.StopExecution()
return
}
}
// if all passed, just continue.
ctx.Next()
}
return handler, true
}

View File

@ -1,7 +1,7 @@
package lexer
import (
"github.com/kataras/iris/core/router/macro/interpreter/token"
"github.com/kataras/iris/macro/interpreter/token"
)
// Lexer helps us to read/scan characters of a source and resolve their token types.

View File

@ -3,7 +3,7 @@ package lexer
import (
"testing"
"github.com/kataras/iris/core/router/macro/interpreter/token"
"github.com/kataras/iris/macro/interpreter/token"
)
func TestNextToken(t *testing.T) {

View File

@ -5,9 +5,9 @@ import (
"strconv"
"strings"
"github.com/kataras/iris/core/router/macro/interpreter/ast"
"github.com/kataras/iris/core/router/macro/interpreter/lexer"
"github.com/kataras/iris/core/router/macro/interpreter/token"
"github.com/kataras/iris/macro/interpreter/ast"
"github.com/kataras/iris/macro/interpreter/lexer"
"github.com/kataras/iris/macro/interpreter/token"
)
// Parse takes a route "fullpath"
@ -39,7 +39,7 @@ func Parse(fullpath string, paramTypes []ast.ParamType) ([]*ast.ParamStatement,
}
// if we have param type path but it's not the last path part
if ast.IsTrailing(stmt.Type) && i < len(pathParts)-1 {
return nil, fmt.Errorf("param type '%s' should be lived only inside the last path segment, but was inside: %s", stmt.Type, s)
return nil, fmt.Errorf("%s: parameter type \"%s\" should be registered to the very last of a path", s, stmt.Type.Indent())
}
statements = append(statements, stmt)

View File

@ -6,7 +6,7 @@ import (
"strings"
"testing"
"github.com/kataras/iris/core/router/macro/interpreter/ast"
"github.com/kataras/iris/macro/interpreter/ast"
)
type simpleParamType string

View File

@ -4,7 +4,7 @@ import (
"strconv"
"strings"
"github.com/kataras/iris/core/router/macro/interpreter/ast"
"github.com/kataras/iris/macro/interpreter/ast"
)
var (

View File

@ -1,11 +1,11 @@
package macro
import (
"github.com/kataras/iris/core/memstore"
"reflect"
"github.com/kataras/iris/core/router/macro/interpreter/ast"
"github.com/kataras/iris/core/router/macro/interpreter/parser"
"github.com/kataras/iris/core/memstore"
"github.com/kataras/iris/macro/interpreter/ast"
"github.com/kataras/iris/macro/interpreter/parser"
)
// Template contains a route's path full parsed template.
@ -59,7 +59,9 @@ func (p *TemplateParam) CanEval() bool {
}
// paramChanger is the same form of context's Params().Set
func (p *TemplateParam) Eval(paramValue string, paramChanger func(key string, newValue interface{}) (memstore.Entry, bool)) bool {
// we could accept a memstore.Store or even context.RequestParams
// but this form has been chosed in order to test easier and fully decoupled from a request when necessary.
func (p *TemplateParam) Eval(paramValue string, paramChanger memstore.ValueSetter) bool {
if p.TypeEvaluator == nil {
for _, fn := range p.stringInFuncs {
if !fn(paramValue) {
@ -85,7 +87,7 @@ func (p *TemplateParam) Eval(paramValue string, paramChanger func(key string, ne
}
}
paramChanger(p.Name, newValue)
paramChanger.Set(p.Name, newValue)
return true
}
@ -107,8 +109,8 @@ func Parse(src string, macros Macros) (*Template, error) {
t.Src = src
for idx, p := range params {
funcMap := macros.Lookup(p.Type)
typEval := funcMap.Evaluator
m := macros.Lookup(p.Type)
typEval := m.Evaluator
tmplParam := TemplateParam{
Src: p.Src,
@ -120,7 +122,7 @@ func Parse(src string, macros Macros) (*Template, error) {
}
for _, paramfn := range p.Funcs {
tmplFn := funcMap.getFunc(paramfn.Name)
tmplFn := m.getFunc(paramfn.Name)
if tmplFn == nil { // if not find on this type, check for Master's which is for global funcs too.
if m := macros.GetMaster(); m != nil {
tmplFn = m.getFunc(paramfn.Name)

View File

@ -7,9 +7,9 @@ import (
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router"
"github.com/kataras/iris/core/router/macro"
"github.com/kataras/iris/hero"
"github.com/kataras/iris/hero/di"
"github.com/kataras/iris/macro"
"github.com/kataras/golog"
)

View File

@ -9,7 +9,7 @@ import (
"unicode"
"github.com/kataras/iris/core/router"
"github.com/kataras/iris/core/router/macro"
"github.com/kataras/iris/macro"
)
const (

View File

@ -4,7 +4,7 @@ import (
"reflect"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router/macro"
"github.com/kataras/iris/macro"
)
func getPathParamsForInput(params []macro.TemplateParam, funcIn ...reflect.Type) (values []reflect.Value) {