Repository: gobuffalo/buffalo
Pull request #1798 (2019-10-08 14:04:45)

Add ResourceName to RouteInfo struct
Hello I am doing a small feature that add the resource name to the RouteInfo struct as asked in the issue #1762. 

### Files changed

routes_info.go
route_mappings.go
route_mappings_test.go

### How I have implemented that

In the `route_mapping.go` file the way to register a new resource is to call app.Resource right ? 
To be simple as possible I am just counting the number of routes before all resources handlers are added. Then after all handlers are added I am looping over all routes after the index saved and set the field `resourceName` to `rt.Name()`.
The Resource method look like this: 

```go
func (a *App) Resource(p string, r Resource) *App {
	[...]
	rt := rv.Type()
	resourceName := rt.Name()

	[...]

	indexBeforeResourceAdded := a.routes.Len()

	setFuncKey(r.List, fmt.Sprintf(handlerName, "List"))
	g.GET(p, r.List)

	setFuncKey(r.Show, fmt.Sprintf(handlerName, "Show"))
	g.GET(path.Join(spath), r.Show)

        [...]

	if n, ok := r.(newable); ok {
		setFuncKey(n.New, fmt.Sprintf(handlerName, "New"))
		g.GET(path.Join(p, "new"), n.New)
	}

        [...]

	// set route resource names for each routes added
	// no length check because cause at least 5 routes have been added so there cannot be an out of range
	for i := indexBeforeResourceAdded; i < a.routes.Len(); i++ {
		a.routes[i].ResourceName = resourceName
	}

	return g
}
```
I have also reordered all the way routes are added to be more concise. Now first there is non optional routes (the Resource interface methods) and then `newable` and `editable`. Edit: seems to have an order dependance with Show and New in the `Test_ResourceOnResource` test function so the order is now identical as before.

The `resourceName` is a field that will be set *only* if the handler is part of a resource. 
The RouteInfo struct now look like this: 
```go
// RouteInfo provides information about the underlying route that
// was built.
type RouteInfo struct {
	Method       string     `json:"method"`
	Path         string     `json:"path"`
	HandlerName  string     `json:"handler"`
	ResourceName string     `json:"resourceName,omitempty"`
	PathName     string     `json:"pathName"`
	Aliases      []string   `json:"aliases"`
	MuxRoute     *mux.Route `json:"-"`
	Handler      Handler    `json:"-"`
	App          *App       `json:"-"`
}
```

To not polluate the Stringer implementation I have set a jsontag to omitempty. 

### Test add

A new test named `Test_App_Routes_Resource` check the resourceName on each routes of the resource. 

```go
func Test_App_Routes_Resource(t *testing.T) {
	r := require.New(t)

	a := New(Options{})
	r.Nil(a.root)

	a.GET("/foo", voidHandler)
	a.Resource("/r", resourceHandler{})

	routes := a.Routes()
	r.Len(routes, 6)
	route := routes[0]
	r.Equal("GET", route.Method)
	r.Equal("/foo/", route.Path)
	r.NotZero(route.HandlerName)

	for k, v := range routes {
		if k > 0 {
			r.Equal("resourceHandler", v.ResourceName)
		}
	}
}
```




Patch:
diff --git a/go.sum b/go.sum
index 99df24b9..9682b064 100644
--- a/go.sum
+++ b/go.sum
@@ -1043,8 +1043,8 @@ golang.org/x/tools v0.0.0-20190905035308-adb45749da8e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20190906203814-12febf440ab1/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191016194801-f0068bd333b2 h1:FY4XZvYWVYktjuISUgAXWD4c1jmb+M8L2wo4iWH/hb0=
-golang.org/x/tools v0.0.0-20191016194801-f0068bd333b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191017101817-846f856e7d71 h1:gKvBHiwvrwjVESSCHvRLeyg2OLVRH6UwgwczU5CXvcg=
+golang.org/x/tools v0.0.0-20191017101817-846f856e7d71/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
diff --git a/route_info.go b/route_info.go
index 00840a9a..b0d7f8fe 100644
--- a/route_info.go
+++ b/route_info.go
@@ -17,14 +17,15 @@ import (
 // RouteInfo provides information about the underlying route that
 // was built.
 type RouteInfo struct {
-	Method      string     `json:"method"`
-	Path        string     `json:"path"`
-	HandlerName string     `json:"handler"`
-	PathName    string     `json:"pathName"`
-	Aliases     []string   `json:"aliases"`
-	MuxRoute    *mux.Route `json:"-"`
-	Handler     Handler    `json:"-"`
-	App         *App       `json:"-"`
+	Method       string     `json:"method"`
+	Path         string     `json:"path"`
+	HandlerName  string     `json:"handler"`
+	ResourceName string     `json:"resourceName,omitempty"`
+	PathName     string     `json:"pathName"`
+	Aliases      []string   `json:"aliases"`
+	MuxRoute     *mux.Route `json:"-"`
+	Handler      Handler    `json:"-"`
+	App          *App       `json:"-"`
 }
 
 // String returns a JSON representation of the RouteInfo
diff --git a/route_mappings.go b/route_mappings.go
index 0e686020..52f66a73 100644
--- a/route_mappings.go
+++ b/route_mappings.go
@@ -157,7 +157,8 @@ func (a *App) Resource(p string, r Resource) *App {
 	}
 
 	rt := rv.Type()
-	rname := fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()) + ".%s"
+	resourceName := rt.Name()
+	handlerName := fmt.Sprintf("%s.%s", rt.PkgPath(), resourceName) + ".%s"
 
 	n := strings.TrimSuffix(rt.Name(), "Resource")
 	paramName := name.New(n).ParamID().String()
@@ -171,29 +172,34 @@ func (a *App) Resource(p string, r Resource) *App {
 	}
 
 	spath := path.Join(p, "{"+paramName+"}")
-	setFuncKey(r.List, fmt.Sprintf(rname, "List"))
-	g.GET(p, r.List)
+
+	setFuncKey(r.List, fmt.Sprintf(handlerName, "List"))
+	g.GET(p, r.List).ResourceName = resourceName
 
 	if n, ok := r.(newable); ok {
-		setFuncKey(n.New, fmt.Sprintf(rname, "New"))
-		g.GET(path.Join(p, "new"), n.New)
+		setFuncKey(n.New, fmt.Sprintf(handlerName, "New"))
+		g.GET(path.Join(p, "new"), n.New).ResourceName = resourceName
 	}
 
-	setFuncKey(r.Show, fmt.Sprintf(rname, "Show"))
-	g.GET(path.Join(spath), r.Show)
+	setFuncKey(r.Show, fmt.Sprintf(handlerName, "Show"))
+	g.GET(path.Join(spath), r.Show).ResourceName = resourceName
 
 	if n, ok := r.(editable); ok {
-		setFuncKey(n.Edit, fmt.Sprintf(rname, "Edit"))
-		g.GET(path.Join(spath, "edit"), n.Edit)
+		setFuncKey(n.Edit, fmt.Sprintf(handlerName, "Edit"))
+		g.GET(path.Join(spath, "edit"), n.Edit).ResourceName = resourceName
 	}
 
-	setFuncKey(r.Create, fmt.Sprintf(rname, "Create"))
-	g.POST(p, r.Create)
-	setFuncKey(r.Update, fmt.Sprintf(rname, "Update"))
-	g.PUT(path.Join(spath), r.Update)
-	setFuncKey(r.Destroy, fmt.Sprintf(rname, "Destroy"))
-	g.DELETE(path.Join(spath), r.Destroy)
+	setFuncKey(r.Create, fmt.Sprintf(handlerName, "Create"))
+	g.POST(p, r.Create).ResourceName = resourceName
+
+	setFuncKey(r.Update, fmt.Sprintf(handlerName, "Update"))
+	g.PUT(path.Join(spath), r.Update).ResourceName = resourceName
+
+	setFuncKey(r.Destroy, fmt.Sprintf(handlerName, "Destroy"))
+	g.DELETE(path.Join(spath), r.Destroy).ResourceName = resourceName
+
 	g.Prefix = path.Join(g.Prefix, spath)
+
 	return g
 }
 


Test patch:
diff --git a/route_mappings_test.go b/route_mappings_test.go
index 940e626e..5ab171bb 100644
--- a/route_mappings_test.go
+++ b/route_mappings_test.go
@@ -109,3 +109,48 @@ func Test_App_RouteHelpers(t *testing.T) {
 	r.NoError(err)
 	r.Equal("/test/1/", string(x))
 }
+
+type resourceHandler struct{}
+
+func (r resourceHandler) List(Context) error {
+	return nil
+}
+
+func (r resourceHandler) Show(Context) error {
+	return nil
+}
+
+func (r resourceHandler) Create(Context) error {
+	return nil
+}
+
+func (r resourceHandler) Update(Context) error {
+	return nil
+}
+
+func (r resourceHandler) Destroy(Context) error {
+	return nil
+}
+
+func Test_App_Routes_Resource(t *testing.T) {
+	r := require.New(t)
+
+	a := New(Options{})
+	r.Nil(a.root)
+
+	a.GET("/foo", voidHandler)
+	a.Resource("/r", resourceHandler{})
+
+	routes := a.Routes()
+	r.Len(routes, 6)
+	route := routes[0]
+	r.Equal("GET", route.Method)
+	r.Equal("/foo/", route.Path)
+	r.NotZero(route.HandlerName)
+
+	for k, v := range routes {
+		if k > 0 {
+			r.Equal("resourceHandler", v.ResourceName)
+		}
+	}
+}