github.com/bketelsen/buffalo@v0.9.5/router.go (about)

     1  package buffalo
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"path"
     7  	"reflect"
     8  	"sort"
     9  	"strings"
    10  
    11  	"github.com/markbates/inflect"
    12  )
    13  
    14  // GET maps an HTTP "GET" request to the path and the specified handler.
    15  func (a *App) GET(p string, h Handler) *RouteInfo {
    16  	return a.addRoute("GET", p, h)
    17  }
    18  
    19  // POST maps an HTTP "POST" request to the path and the specified handler.
    20  func (a *App) POST(p string, h Handler) *RouteInfo {
    21  	return a.addRoute("POST", p, h)
    22  }
    23  
    24  // PUT maps an HTTP "PUT" request to the path and the specified handler.
    25  func (a *App) PUT(p string, h Handler) *RouteInfo {
    26  	return a.addRoute("PUT", p, h)
    27  }
    28  
    29  // DELETE maps an HTTP "DELETE" request to the path and the specified handler.
    30  func (a *App) DELETE(p string, h Handler) *RouteInfo {
    31  	return a.addRoute("DELETE", p, h)
    32  }
    33  
    34  // HEAD maps an HTTP "HEAD" request to the path and the specified handler.
    35  func (a *App) HEAD(p string, h Handler) *RouteInfo {
    36  	return a.addRoute("HEAD", p, h)
    37  }
    38  
    39  // OPTIONS maps an HTTP "OPTIONS" request to the path and the specified handler.
    40  func (a *App) OPTIONS(p string, h Handler) *RouteInfo {
    41  	return a.addRoute("OPTIONS", p, h)
    42  }
    43  
    44  // PATCH maps an HTTP "PATCH" request to the path and the specified handler.
    45  func (a *App) PATCH(p string, h Handler) *RouteInfo {
    46  	return a.addRoute("PATCH", p, h)
    47  }
    48  
    49  // Redirect from one URL to another URL. Only works for "GET" requests.
    50  func (a *App) Redirect(status int, from, to string) *RouteInfo {
    51  	return a.GET(from, func(c Context) error {
    52  		return c.Redirect(status, to)
    53  	})
    54  }
    55  
    56  // Mount mounts a http.Handler (or Buffalo app) and passes through all requests to it.
    57  /*
    58  func muxer() http.Handler {
    59  	f := func(res http.ResponseWriter, req *http.Request) {
    60  		fmt.Fprintf(res, "%s - %s", req.Method, req.URL.String())
    61  	}
    62  	mux := mux.NewRouter()
    63  	mux.HandleFunc("/foo", f).Methods("GET")
    64  	mux.HandleFunc("/bar", f).Methods("POST")
    65  	mux.HandleFunc("/baz/baz", f).Methods("DELETE")
    66  	return mux
    67  }
    68  
    69  a.Mount("/admin", muxer())
    70  
    71  $ curl -X DELETE http://localhost:3000/admin/baz/baz
    72  */
    73  func (a *App) Mount(p string, h http.Handler) {
    74  	prefix := path.Join(a.Prefix, p)
    75  	path := path.Join(p, "{path:.+}")
    76  	a.ANY(path, WrapHandler(http.StripPrefix(prefix, h)))
    77  }
    78  
    79  // ServeFiles maps an path to a directory on disk to serve static files.
    80  // Useful for JavaScript, images, CSS, etc...
    81  /*
    82  	a.ServeFiles("/assets", http.Dir("path/to/assets"))
    83  */
    84  func (a *App) ServeFiles(p string, root http.FileSystem) {
    85  	path := path.Join(a.Prefix, p)
    86  	a.router.PathPrefix(path).Handler(http.StripPrefix(path, http.FileServer(root)))
    87  }
    88  
    89  // Resource maps an implementation of the Resource interface
    90  // to the appropriate RESTful mappings. Resource returns the *App
    91  // associated with this group of mappings so you can set middleware, etc...
    92  // on that group, just as if you had used the a.Group functionality.
    93  /*
    94  	a.Resource("/users", &UsersResource{})
    95  
    96  	// Is equal to this:
    97  
    98  	ur := &UsersResource{}
    99  	g := a.Group("/users")
   100  	g.GET("/", ur.List) // GET /users => ur.List
   101  	g.GET("/new", ur.New) // GET /users/new => ur.New
   102  	g.GET("/{user_id}", ur.Show) // GET /users/{user_id} => ur.Show
   103  	g.GET("/{user_id}/edit", ur.Edit) // GET /users/{user_id}/edit => ur.Edit
   104  	g.POST("/", ur.Create) // POST /users => ur.Create
   105  	g.PUT("/{user_id}", ur.Update) PUT /users/{user_id} => ur.Update
   106  	g.DELETE("/{user_id}", ur.Destroy) DELETE /users/{user_id} => ur.Destroy
   107  */
   108  func (a *App) Resource(p string, r Resource) *App {
   109  	g := a.Group(p)
   110  	p = "/"
   111  
   112  	rv := reflect.ValueOf(r)
   113  	if rv.Kind() == reflect.Ptr {
   114  		rv = rv.Elem()
   115  	}
   116  
   117  	rt := rv.Type()
   118  	rname := fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()) + ".%s"
   119  
   120  	name := strings.Replace(rt.Name(), "Resource", "", 1)
   121  	paramName := inflect.Singularize(inflect.Underscore(name))
   122  
   123  	spath := path.Join(p, fmt.Sprintf("{%s_id}", paramName))
   124  	setFuncKey(r.List, fmt.Sprintf(rname, "List"))
   125  	g.GET(p, r.List)
   126  	setFuncKey(r.New, fmt.Sprintf(rname, "New"))
   127  	g.GET(path.Join(p, "new"), r.New)
   128  	setFuncKey(r.Show, fmt.Sprintf(rname, "Show"))
   129  	g.GET(path.Join(spath), r.Show)
   130  	setFuncKey(r.Edit, fmt.Sprintf(rname, "Edit"))
   131  	g.GET(path.Join(spath, "edit"), r.Edit)
   132  	setFuncKey(r.Create, fmt.Sprintf(rname, "Create"))
   133  	g.POST(p, r.Create)
   134  	setFuncKey(r.Update, fmt.Sprintf(rname, "Update"))
   135  	g.PUT(path.Join(spath), r.Update)
   136  	setFuncKey(r.Destroy, fmt.Sprintf(rname, "Destroy"))
   137  	g.DELETE(path.Join(spath), r.Destroy)
   138  	g.Prefix = path.Join(g.Prefix, spath)
   139  	return g
   140  }
   141  
   142  // ANY accepts a request across any HTTP method for the specified path
   143  // and routes it to the specified Handler.
   144  func (a *App) ANY(p string, h Handler) {
   145  	a.GET(p, h)
   146  	a.POST(p, h)
   147  	a.PUT(p, h)
   148  	a.PATCH(p, h)
   149  	a.HEAD(p, h)
   150  	a.OPTIONS(p, h)
   151  	a.DELETE(p, h)
   152  }
   153  
   154  // Group creates a new `*App` that inherits from it's parent `*App`.
   155  // This is useful for creating groups of end-points that need to share
   156  // common functionality, like middleware.
   157  /*
   158  	g := a.Group("/api/v1")
   159  	g.Use(AuthorizeAPIMiddleware)
   160  	g.GET("/users, APIUsersHandler)
   161  	g.GET("/users/:user_id, APIUserShowHandler)
   162  */
   163  func (a *App) Group(groupPath string) *App {
   164  	g := New(a.Options)
   165  	g.Prefix = path.Join(a.Prefix, groupPath)
   166  	g.Name = g.Prefix
   167  
   168  	g.router = a.router
   169  	g.Middleware = a.Middleware.clone()
   170  	g.ErrorHandlers = a.ErrorHandlers
   171  	g.root = a
   172  	if a.root != nil {
   173  		g.root = a.root
   174  	}
   175  	a.children = append(a.children, g)
   176  	return g
   177  }
   178  
   179  func (a *App) addRoute(method string, url string, h Handler) *RouteInfo {
   180  	a.moot.Lock()
   181  	defer a.moot.Unlock()
   182  
   183  	url = path.Join(a.Prefix, url)
   184  	name := a.buildRouteName(url)
   185  
   186  	hs := funcKey(h)
   187  	r := &RouteInfo{
   188  		Method:      method,
   189  		Path:        url,
   190  		HandlerName: hs,
   191  		Handler:     h,
   192  		App:         a,
   193  		Aliases:     []string{},
   194  	}
   195  
   196  	r.MuxRoute = a.router.Handle(url, r).Methods(method)
   197  	r.Name(name)
   198  
   199  	routes := a.Routes()
   200  	routes = append(routes, r)
   201  	sort.Sort(routes)
   202  
   203  	if a.root != nil {
   204  		a.root.routes = routes
   205  	} else {
   206  		a.routes = routes
   207  	}
   208  
   209  	return r
   210  }
   211  
   212  //buildRouteName builds a route based on the path passed.
   213  func (a *App) buildRouteName(p string) string {
   214  	if p == "/" || p == "" {
   215  		return "root"
   216  	}
   217  
   218  	resultParts := []string{}
   219  	parts := strings.Split(p, "/")
   220  
   221  	for index, part := range parts {
   222  
   223  		if strings.Contains(part, "{") || part == "" {
   224  			continue
   225  		}
   226  
   227  		shouldSingularize := (len(parts) > index+1) && strings.Contains(parts[index+1], "{")
   228  		if shouldSingularize {
   229  			part = inflect.Singularize(part)
   230  		}
   231  
   232  		if parts[index] == "new" || parts[index] == "edit" {
   233  			resultParts = append([]string{part}, resultParts...)
   234  			continue
   235  		}
   236  
   237  		if index > 0 && strings.Contains(parts[index-1], "}") {
   238  			resultParts = append(resultParts, part)
   239  			continue
   240  		}
   241  
   242  		resultParts = append(resultParts, part)
   243  	}
   244  
   245  	if len(resultParts) == 0 {
   246  		return "unnamed"
   247  	}
   248  
   249  	underscore := strings.TrimSpace(strings.Join(resultParts, "_"))
   250  	return inflect.CamelizeDownFirst(underscore)
   251  }