From 7010af371d4b01e29ce4b1bf06afbab2f7955069 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 7 Sep 2015 17:02:58 -0400 Subject: [PATCH] Add File and line location to RouteInfo. These two fields are very helpful to generate documentation with swagger. Signed-off-by: David Calavera --- function.go | 27 +++++++++++++++++++++++++++ function_test.go | 23 +++++++++++++++++++++++ gin.go | 7 ++++++- gin_test.go | 40 +++++++++++++++------------------------- utils.go | 5 ----- utils_test.go | 8 -------- 6 files changed, 71 insertions(+), 39 deletions(-) create mode 100644 function.go create mode 100644 function_test.go diff --git a/function.go b/function.go new file mode 100644 index 00000000..23c41a69 --- /dev/null +++ b/function.go @@ -0,0 +1,27 @@ +package gin + +import ( + "reflect" + "runtime" +) + +type Function struct { + Name string + File string + Line int +} + +func parseFunction(f interface{}) Function { + ptr := reflect.ValueOf(f).Pointer() + fu := runtime.FuncForPC(ptr) + file, line := fu.FileLine(ptr) + return Function{ + Name: fu.Name(), + File: file, + Line: line, + } +} + +func nameOfFunction(f interface{}) string { + return parseFunction(f).Name +} diff --git a/function_test.go b/function_test.go new file mode 100644 index 00000000..4bd67f70 --- /dev/null +++ b/function_test.go @@ -0,0 +1,23 @@ +package gin + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func someFunction() { + // this empty function is used by TestFunctionName() +} + +func TestParseFunction(t *testing.T) { + f := parseFunction(someFunction) + assert.Equal(t, "github.com/gin-gonic/gin.someFunction", f.Name) + assert.Equal(t, "function_test.go", filepath.Base(f.File)) + assert.Equal(t, 12, f.Line) +} + +func TestFunctionName(t *testing.T) { + assert.Equal(t, nameOfFunction(someFunction), "github.com/gin-gonic/gin.someFunction") +} diff --git a/gin.go b/gin.go index 3834d67e..f1f5a2b8 100644 --- a/gin.go +++ b/gin.go @@ -38,6 +38,8 @@ type ( Method string Path string Handler string + File string + Line int } // Engine is the framework's instance, it contains the muxer, middleware and configuration settings. @@ -213,10 +215,13 @@ func (engine *Engine) Routes() (routes RoutesInfo) { func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { path += root.path if len(root.handlers) > 0 { + funcInfo := parseFunction(root.handlers.Last()) routes = append(routes, RouteInfo{ Method: method, Path: path, - Handler: nameOfFunction(root.handlers.Last()), + Handler: funcInfo.Name, + File: funcInfo.File, + Line: funcInfo.Line, }) } for _, child := range root.children { diff --git a/gin_test.go b/gin_test.go index b3b0eb6b..8a00a4db 100644 --- a/gin_test.go +++ b/gin_test.go @@ -5,6 +5,7 @@ package gin import ( + "path/filepath" "reflect" "testing" @@ -214,31 +215,20 @@ func TestListOfRoutes(t *testing.T) { list := router.Routes() assert.Len(t, list, 7) - assert.Contains(t, list, RouteInfo{ - Method: "GET", - Path: "/favicon.ico", - Handler: "github.com/gin-gonic/gin.handler_test1", - }) - assert.Contains(t, list, RouteInfo{ - Method: "GET", - Path: "/", - Handler: "github.com/gin-gonic/gin.handler_test1", - }) - assert.Contains(t, list, RouteInfo{ - Method: "GET", - Path: "/users/", - Handler: "github.com/gin-gonic/gin.handler_test2", - }) - assert.Contains(t, list, RouteInfo{ - Method: "GET", - Path: "/users/:id", - Handler: "github.com/gin-gonic/gin.handler_test1", - }) - assert.Contains(t, list, RouteInfo{ - Method: "POST", - Path: "/users/:id", - Handler: "github.com/gin-gonic/gin.handler_test2", - }) + assertRoute(t, list, "GET", "/favicon.ico", "github.com/gin-gonic/gin.handler_test1", "gin_test.go") + assertRoute(t, list, "GET", "/", "github.com/gin-gonic/gin.handler_test1", "gin_test.go") + assertRoute(t, list, "GET", "/users/", "github.com/gin-gonic/gin.handler_test2", "gin_test.go") + assertRoute(t, list, "GET", "/users/:id", "github.com/gin-gonic/gin.handler_test1", "gin_test.go") + assertRoute(t, list, "POST", "/users/:id", "github.com/gin-gonic/gin.handler_test2", "gin_test.go") +} + +func assertRoute(t *testing.T, list RoutesInfo, method, path, handler, filename string) { + for _, r := range list { + if r.Method == method && r.Path == path && r.Handler == handler && filepath.Base(r.File) == filename { + return + } + } + t.Fatalf("Routes info doesn't include route information: [%s] %s: %s at %s\n%v", method, path, handler, filename, list) } func handler_test1(c *Context) {} diff --git a/utils.go b/utils.go index 533888d1..e7a3b382 100644 --- a/utils.go +++ b/utils.go @@ -10,7 +10,6 @@ import ( "os" "path" "reflect" - "runtime" "strings" ) @@ -114,10 +113,6 @@ func lastChar(str string) uint8 { return str[size-1] } -func nameOfFunction(f interface{}) string { - return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() -} - func joinPaths(absolutePath, relativePath string) string { if len(relativePath) == 0 { return absolutePath diff --git a/utils_test.go b/utils_test.go index 11a5b684..b2fc5fd4 100644 --- a/utils_test.go +++ b/utils_test.go @@ -77,14 +77,6 @@ func TestFilterFlags(t *testing.T) { assert.Equal(t, result, "text/html") } -func TestFunctionName(t *testing.T) { - assert.Equal(t, nameOfFunction(somefunction), "github.com/gin-gonic/gin.somefunction") -} - -func somefunction() { - // this empty function is used by TestFunctionName() -} - func TestJoinPaths(t *testing.T) { assert.Equal(t, joinPaths("", ""), "") assert.Equal(t, joinPaths("", "/"), "/")