Skip to content

Commit d9c47b6

Browse files
committed
Added a Print function on router that prints out a stylized list of
routes, aka rake routes from Ruby on Rails
1 parent a451812 commit d9c47b6

File tree

2 files changed

+140
-1
lines changed

2 files changed

+140
-1
lines changed

router.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package echo
22

3-
import "net/http"
3+
import (
4+
"fmt"
5+
"io"
6+
"net/http"
7+
)
48

59
type (
610
router struct {
@@ -315,3 +319,61 @@ func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
315319
h(c)
316320
r.echo.pool.Put(c)
317321
}
322+
323+
/*
324+
Print writes out a formatted listing of paths corresponding to each
325+
HTTP method that has been added to the router.
326+
327+
Example output:
328+
GET /foo
329+
/foo/:id
330+
/bar/baz/:id
331+
/:controller/:action/:id
332+
/api/v1/users/:id
333+
/api/v1/users/:id/edit
334+
POST /api/v1/users/:id
335+
PUT /foo
336+
*/
337+
func (r *router) Print(out io.Writer) {
338+
for verb, node := range r.trees {
339+
x := node.Paths()
340+
if len(x) > 0 {
341+
fmt.Fprintf(out, "%s\t%s\n", verb, x[0])
342+
for _, n := range x[1:] {
343+
fmt.Fprintf(out, "\t%s\n", n)
344+
}
345+
}
346+
}
347+
}
348+
349+
/*
350+
Paths generates a list of all of the paths, and subpaths, for a particular node.
351+
352+
Example output:
353+
[]string{"/:controller/:action/:id", "/api/v1/users/:id", "/api/v1/users/:id/edit", "/bar/baz/:id", "/foo", "/foo/:id"}
354+
*/
355+
func (n *node) Paths() []string {
356+
prefix := n.prefix
357+
x := []string{}
358+
if prefix != "" {
359+
pl := len(n.pnames)
360+
if pl > 0 && prefix == ":" {
361+
prefix = prefix + n.pnames[pl-1]
362+
}
363+
children := n.children
364+
if len(children) > 0 {
365+
if n.handler != nil {
366+
x = append(x, prefix)
367+
}
368+
for _, c := range children {
369+
y := c.Paths()
370+
for _, s := range y {
371+
x = append(x, prefix+s)
372+
}
373+
}
374+
} else {
375+
x = append(x, prefix)
376+
}
377+
}
378+
return x
379+
}

router_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"fmt"
66
"net/http"
77
"net/http/httptest"
8+
"sort"
9+
"strings"
810
"testing"
911
)
1012

@@ -634,6 +636,81 @@ func TestRouterServeHTTP(t *testing.T) {
634636
r.ServeHTTP(w, req)
635637
}
636638

639+
func TestPaths(t *testing.T) {
640+
e := New()
641+
642+
e.Get("/foo", func(res http.ResponseWriter, req *http.Request) {})
643+
e.Put("/foo", func(res http.ResponseWriter, req *http.Request) {})
644+
e.Get("/foo/:id", func(res http.ResponseWriter, req *http.Request) {})
645+
e.Get("/bar/baz/:id", func(res http.ResponseWriter, req *http.Request) {})
646+
e.Get("/:controller/:action/:id", func(res http.ResponseWriter, req *http.Request) {})
647+
648+
g := e.Group("/api/v1")
649+
g.Get("/users/:id", func(res http.ResponseWriter, req *http.Request) {})
650+
g.Get("/users/:id/edit", func(res http.ResponseWriter, req *http.Request) {})
651+
g.Post("/users/:id", func(res http.ResponseWriter, req *http.Request) {})
652+
653+
trees := e.Router.trees
654+
655+
exm := map[string][]string{
656+
"GET": []string{"/:controller/:action/:id", "/api/v1/users/:id", "/api/v1/users/:id/edit", "/bar/baz/:id", "/foo", "/foo/:id"},
657+
"PUT": []string{"/foo"},
658+
"POST": []string{"/api/v1/users/:id"},
659+
}
660+
661+
for verb, exp := range exm {
662+
paths := trees[verb].Paths()
663+
sort.Strings(paths)
664+
665+
for _, e := range exp {
666+
found := false
667+
for _, p := range paths {
668+
if p == e {
669+
found = true
670+
break
671+
}
672+
}
673+
if !found {
674+
t.Errorf("expected %q to contain %s", paths, e)
675+
}
676+
}
677+
}
678+
679+
}
680+
681+
func TestPrint(t *testing.T) {
682+
e := New()
683+
684+
e.Get("/foo", func(res http.ResponseWriter, req *http.Request) {})
685+
e.Put("/foo", func(res http.ResponseWriter, req *http.Request) {})
686+
e.Get("/foo/:id", func(res http.ResponseWriter, req *http.Request) {})
687+
e.Get("/bar/baz/:id", func(res http.ResponseWriter, req *http.Request) {})
688+
e.Get("/:controller/:action/:id", func(res http.ResponseWriter, req *http.Request) {})
689+
690+
g := e.Group("/api/v1")
691+
g.Get("/users/:id", func(res http.ResponseWriter, req *http.Request) {})
692+
g.Get("/users/:id/edit", func(res http.ResponseWriter, req *http.Request) {})
693+
g.Post("/users/:id", func(res http.ResponseWriter, req *http.Request) {})
694+
695+
var b bytes.Buffer
696+
e.Router.Print(&b)
697+
get := `GET /foo
698+
/foo/:id
699+
/bar/baz/:id
700+
/:controller/:action/:id
701+
/api/v1/users/:id
702+
/api/v1/users/:id/edit`
703+
post := `POST /api/v1/users/:id`
704+
put := `PUT /foo`
705+
706+
res := b.String()
707+
for _, exp := range []string{get, post, put} {
708+
if !strings.Contains(res, exp) {
709+
t.Errorf("expected %q to contain %q", res, exp)
710+
}
711+
}
712+
}
713+
637714
func (n *node) printTree(pfx string, tail bool) {
638715
p := prefix(tail, pfx, "└── ", "├── ")
639716
fmt.Printf("%s%s, %p: type=%d, parent=%p, handler=%v\n", p, n.prefix, n, n.typ, n.parent, n.handler)

0 commit comments

Comments
 (0)