Repository: gobuffalo/buffalo
Pull request #2075 (2021-01-07 23:03:59)

Application Route Namer
Allows to override the application default route namer.


Patch:
diff --git a/app.go b/app.go
index 8ef8245e..a255bd21 100644
--- a/app.go
+++ b/app.go
@@ -23,6 +23,10 @@ type App struct {
 	root          *App
 	children      []*App
 	filepaths     []string
+
+	// Routenamer for the app. This field provides the ability to override the
+	// base route namer for something more specific to the app.
+	RouteNamer RouteNamer
 }
 
 // Muxer returns the underlying mux router to allow
@@ -48,6 +52,8 @@ func New(opts Options) *App {
 		moot:     &sync.RWMutex{},
 		routes:   RouteList{},
 		children: []*App{},
+
+		RouteNamer: baseRouteNamer{},
 	}
 
 	dem := a.defaultErrorMiddleware
diff --git a/route_mappings.go b/route_mappings.go
index d77adc09..e5c9429a 100644
--- a/route_mappings.go
+++ b/route_mappings.go
@@ -11,7 +11,6 @@ import (
 	"strings"
 
 	"github.com/gobuffalo/envy"
-	"github.com/gobuffalo/flect"
 	"github.com/gobuffalo/flect/name"
 	"github.com/gorilla/handlers"
 )
@@ -263,7 +262,7 @@ func (a *App) addRoute(method string, url string, h Handler) *RouteInfo {
 
 	url = path.Join(a.Prefix, url)
 	url = a.normalizePath(url)
-	name := a.buildRouteName(url)
+	name := a.RouteNamer.NameRoute(url)
 
 	hs := funcKey(h)
 	r := &RouteInfo{
@@ -291,61 +290,6 @@ func (a *App) addRoute(method string, url string, h Handler) *RouteInfo {
 	return r
 }
 
-//buildRouteName builds a route based on the path passed.
-func (a *App) buildRouteName(p string) string {
-	if p == "/" || p == "" {
-		return "root"
-	}
-
-	resultParts := []string{}
-	parts := strings.Split(p, "/")
-
-	for index, part := range parts {
-
-		originalPart := parts[index]
-
-		var previousPart string
-		if index > 0 {
-			previousPart = parts[index-1]
-		}
-
-		var nextPart string
-		if len(parts) > index+1 {
-			nextPart = parts[index+1]
-		}
-
-		isIdentifierPart := strings.Contains(part, "{") && (strings.Contains(part, flect.Singularize(previousPart)))
-		isSimplifiedID := part == `{id}`
-
-		if isIdentifierPart || isSimplifiedID || part == "" {
-			continue
-		}
-
-		if strings.Contains(nextPart, "{") {
-			part = flect.Singularize(part)
-		}
-
-		if originalPart == "new" || originalPart == "edit" {
-			resultParts = append([]string{part}, resultParts...)
-			continue
-		}
-
-		if strings.Contains(previousPart, "}") {
-			resultParts = append(resultParts, part)
-			continue
-		}
-
-		resultParts = append(resultParts, part)
-	}
-
-	if len(resultParts) == 0 {
-		return "unnamed"
-	}
-
-	underscore := strings.TrimSpace(strings.Join(resultParts, "_"))
-	return name.VarCase(underscore)
-}
-
 func stripAsset(path string, h http.Handler, a *App) http.Handler {
 	if path == "" {
 		return h
diff --git a/routenamer.go b/routenamer.go
new file mode 100644
index 00000000..b6d28058
--- /dev/null
+++ b/routenamer.go
@@ -0,0 +1,74 @@
+package buffalo
+
+import (
+	"strings"
+
+	"github.com/gobuffalo/flect"
+	"github.com/gobuffalo/flect/name"
+)
+
+// RouteNamer is in charge of naming a route from the
+// path assigned, this name typically will be used if no
+// name is assined with .Name(...).
+type RouteNamer interface {
+	// NameRoute receives the path and returns the name
+	// for the route.
+	NameRoute(string) string
+}
+
+// BaseRouteNamer is the default route namer used by apps.
+type baseRouteNamer struct{}
+
+func (drn baseRouteNamer) NameRoute(p string) string {
+	if p == "/" || p == "" {
+		return "root"
+	}
+
+	resultParts := []string{}
+	parts := strings.Split(p, "/")
+
+	for index, part := range parts {
+
+		originalPart := parts[index]
+
+		var previousPart string
+		if index > 0 {
+			previousPart = parts[index-1]
+		}
+
+		var nextPart string
+		if len(parts) > index+1 {
+			nextPart = parts[index+1]
+		}
+
+		isIdentifierPart := strings.Contains(part, "{") && (strings.Contains(part, flect.Singularize(previousPart)))
+		isSimplifiedID := part == `{id}`
+
+		if isIdentifierPart || isSimplifiedID || part == "" {
+			continue
+		}
+
+		if strings.Contains(nextPart, "{") {
+			part = flect.Singularize(part)
+		}
+
+		if originalPart == "new" || originalPart == "edit" {
+			resultParts = append([]string{part}, resultParts...)
+			continue
+		}
+
+		if strings.Contains(previousPart, "}") {
+			resultParts = append(resultParts, part)
+			continue
+		}
+
+		resultParts = append(resultParts, part)
+	}
+
+	if len(resultParts) == 0 {
+		return "unnamed"
+	}
+
+	underscore := strings.TrimSpace(strings.Join(resultParts, "_"))
+	return name.VarCase(underscore)
+}


Test patch:
diff --git a/router_test.go b/router_test.go
index 11fca3f7..b41a3389 100644
--- a/router_test.go
+++ b/router_test.go
@@ -740,7 +740,7 @@ func Test_buildRouteName(t *testing.T) {
 	a := New(Options{})
 
 	for input, result := range cases {
-		fResult := a.buildRouteName(input)
+		fResult := a.RouteNamer.NameRoute(input)
 		r.Equal(result, fResult, input)
 	}
 
@@ -751,7 +751,7 @@ func Test_buildRouteName(t *testing.T) {
 	}
 
 	for input, result := range cases {
-		fResult := a.buildRouteName(input)
+		fResult := a.RouteNamer.NameRoute(input)
 		r.Equal(result, fResult, input)
 	}
 }