Add File and line location to RouteInfo.

These two fields are very helpful to generate documentation with
swagger.

Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
David Calavera 2015-09-07 17:02:58 -04:00
parent 704d690ac0
commit 7010af371d
6 changed files with 71 additions and 39 deletions

27
function.go Normal file
View File

@ -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
}

23
function_test.go Normal file
View File

@ -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")
}

7
gin.go
View File

@ -38,6 +38,8 @@ type (
Method string Method string
Path string Path string
Handler string Handler string
File string
Line int
} }
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings. // 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 { func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
path += root.path path += root.path
if len(root.handlers) > 0 { if len(root.handlers) > 0 {
funcInfo := parseFunction(root.handlers.Last())
routes = append(routes, RouteInfo{ routes = append(routes, RouteInfo{
Method: method, Method: method,
Path: path, Path: path,
Handler: nameOfFunction(root.handlers.Last()), Handler: funcInfo.Name,
File: funcInfo.File,
Line: funcInfo.Line,
}) })
} }
for _, child := range root.children { for _, child := range root.children {

View File

@ -5,6 +5,7 @@
package gin package gin
import ( import (
"path/filepath"
"reflect" "reflect"
"testing" "testing"
@ -214,31 +215,20 @@ func TestListOfRoutes(t *testing.T) {
list := router.Routes() list := router.Routes()
assert.Len(t, list, 7) assert.Len(t, list, 7)
assert.Contains(t, list, RouteInfo{ assertRoute(t, list, "GET", "/favicon.ico", "github.com/gin-gonic/gin.handler_test1", "gin_test.go")
Method: "GET", assertRoute(t, list, "GET", "/", "github.com/gin-gonic/gin.handler_test1", "gin_test.go")
Path: "/favicon.ico", assertRoute(t, list, "GET", "/users/", "github.com/gin-gonic/gin.handler_test2", "gin_test.go")
Handler: "github.com/gin-gonic/gin.handler_test1", 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")
assert.Contains(t, list, RouteInfo{ }
Method: "GET",
Path: "/", func assertRoute(t *testing.T, list RoutesInfo, method, path, handler, filename string) {
Handler: "github.com/gin-gonic/gin.handler_test1", for _, r := range list {
}) if r.Method == method && r.Path == path && r.Handler == handler && filepath.Base(r.File) == filename {
assert.Contains(t, list, RouteInfo{ return
Method: "GET", }
Path: "/users/", }
Handler: "github.com/gin-gonic/gin.handler_test2", t.Fatalf("Routes info doesn't include route information: [%s] %s: %s at %s\n%v", method, path, handler, filename, list)
})
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",
})
} }
func handler_test1(c *Context) {} func handler_test1(c *Context) {}

View File

@ -10,7 +10,6 @@ import (
"os" "os"
"path" "path"
"reflect" "reflect"
"runtime"
"strings" "strings"
) )
@ -114,10 +113,6 @@ func lastChar(str string) uint8 {
return str[size-1] return str[size-1]
} }
func nameOfFunction(f interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
}
func joinPaths(absolutePath, relativePath string) string { func joinPaths(absolutePath, relativePath string) string {
if len(relativePath) == 0 { if len(relativePath) == 0 {
return absolutePath return absolutePath

View File

@ -77,14 +77,6 @@ func TestFilterFlags(t *testing.T) {
assert.Equal(t, result, "text/html") 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) { func TestJoinPaths(t *testing.T) {
assert.Equal(t, joinPaths("", ""), "") assert.Equal(t, joinPaths("", ""), "")
assert.Equal(t, joinPaths("", "/"), "/") assert.Equal(t, joinPaths("", "/"), "/")