github.com/mayra-cabrera/buffalo@v0.9.4-0.20170814145312-66d2e7772f11/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  // ServeFiles maps an path to a directory on disk to serve static files.
    57  // Useful for JavaScript, images, CSS, etc...
    58  /*
    59  	a.ServeFiles("/assets", http.Dir("path/to/assets"))
    60  */
    61  func (a *App) ServeFiles(p string, root http.FileSystem) {
    62  	path := path.Join(a.Prefix, p)
    63  	a.router.PathPrefix(path).Handler(http.StripPrefix(path, http.FileServer(root)))
    64  }
    65  
    66  // Resource maps an implementation of the Resource interface
    67  // to the appropriate RESTful mappings. Resource returns the *App
    68  // associated with this group of mappings so you can set middleware, etc...
    69  // on that group, just as if you had used the a.Group functionality.
    70  /*
    71  	a.Resource("/users", &UsersResource{})
    72  
    73  	// Is equal to this:
    74  
    75  	ur := &UsersResource{}
    76  	g := a.Group("/users")
    77  	g.GET("/", ur.List) // GET /users => ur.List
    78  	g.GET("/new", ur.New) // GET /users/new => ur.New
    79  	g.GET("/{user_id}", ur.Show) // GET /users/{user_id} => ur.Show
    80  	g.GET("/{user_id}/edit", ur.Edit) // GET /users/{user_id}/edit => ur.Edit
    81  	g.POST("/", ur.Create) // POST /users => ur.Create
    82  	g.PUT("/{user_id}", ur.Update) PUT /users/{user_id} => ur.Update
    83  	g.DELETE("/{user_id}", ur.Destroy) DELETE /users/{user_id} => ur.Destroy
    84  */
    85  func (a *App) Resource(p string, r Resource) *App {
    86  	g := a.Group(p)
    87  	p = "/"
    88  
    89  	rv := reflect.ValueOf(r)
    90  	if rv.Kind() == reflect.Ptr {
    91  		rv = rv.Elem()
    92  	}
    93  
    94  	rt := rv.Type()
    95  	rname := fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()) + ".%s"
    96  
    97  	name := strings.Replace(rt.Name(), "Resource", "", 1)
    98  	paramName := inflect.Singularize(inflect.Underscore(name))
    99  
   100  	spath := path.Join(p, fmt.Sprintf("{%s_id}", paramName))
   101  	setFuncKey(r.List, fmt.Sprintf(rname, "List"))
   102  	g.GET(p, r.List)
   103  	setFuncKey(r.New, fmt.Sprintf(rname, "New"))
   104  	g.GET(path.Join(p, "new"), r.New)
   105  	setFuncKey(r.Show, fmt.Sprintf(rname, "Show"))
   106  	g.GET(path.Join(spath), r.Show)
   107  	setFuncKey(r.Edit, fmt.Sprintf(rname, "Edit"))
   108  	g.GET(path.Join(spath, "edit"), r.Edit)
   109  	setFuncKey(r.Create, fmt.Sprintf(rname, "Create"))
   110  	g.POST(p, r.Create)
   111  	setFuncKey(r.Update, fmt.Sprintf(rname, "Update"))
   112  	g.PUT(path.Join(spath), r.Update)
   113  	setFuncKey(r.Destroy, fmt.Sprintf(rname, "Destroy"))
   114  	g.DELETE(path.Join(spath), r.Destroy)
   115  	return g
   116  }
   117  
   118  // ANY accepts a request across any HTTP method for the specified path
   119  // and routes it to the specified Handler.
   120  func (a *App) ANY(p string, h Handler) {
   121  	a.GET(p, h)
   122  	a.POST(p, h)
   123  	a.PUT(p, h)
   124  	a.PATCH(p, h)
   125  	a.HEAD(p, h)
   126  	a.OPTIONS(p, h)
   127  	a.DELETE(p, h)
   128  }
   129  
   130  // Group creates a new `*App` that inherits from it's parent `*App`.
   131  // This is useful for creating groups of end-points that need to share
   132  // common functionality, like middleware.
   133  /*
   134  	g := a.Group("/api/v1")
   135  	g.Use(AuthorizeAPIMiddleware)
   136  	g.GET("/users, APIUsersHandler)
   137  	g.GET("/users/:user_id, APIUserShowHandler)
   138  */
   139  func (a *App) Group(groupPath string) *App {
   140  	g := New(a.Options)
   141  	g.Prefix = path.Join(a.Prefix, groupPath)
   142  	g.Name = g.Prefix
   143  
   144  	g.router = a.router
   145  	g.Middleware = a.Middleware.clone()
   146  	g.ErrorHandlers = a.ErrorHandlers
   147  	g.root = a
   148  	if a.root != nil {
   149  		g.root = a.root
   150  	}
   151  	a.children = append(a.children, g)
   152  	return g
   153  }
   154  
   155  func (a *App) addRoute(method string, url string, h Handler) *RouteInfo {
   156  	a.moot.Lock()
   157  	defer a.moot.Unlock()
   158  
   159  	url = path.Join(a.Prefix, url)
   160  	name := a.buildRouteName(url)
   161  
   162  	hs := funcKey(h)
   163  	r := &RouteInfo{
   164  		Method:      method,
   165  		Path:        url,
   166  		HandlerName: hs,
   167  		Handler:     h,
   168  		App:         a,
   169  		Aliases:     []string{},
   170  	}
   171  
   172  	r.MuxRoute = a.router.Handle(url, r).Methods(method)
   173  	r.Name(name)
   174  
   175  	routes := a.Routes()
   176  	routes = append(routes, r)
   177  	sort.Sort(routes)
   178  
   179  	if a.root != nil {
   180  		a.root.routes = routes
   181  	} else {
   182  		a.routes = routes
   183  	}
   184  
   185  	return r
   186  }
   187  
   188  //buildRouteName builds a route based on the path passed.
   189  func (a *App) buildRouteName(p string) string {
   190  	if p == "/" || p == "" {
   191  		return "root"
   192  	}
   193  
   194  	resultParts := []string{}
   195  	parts := strings.Split(p, "/")
   196  
   197  	for index, part := range parts {
   198  
   199  		if strings.Contains(part, "{") || part == "" {
   200  			continue
   201  		}
   202  
   203  		shouldSingularize := (len(parts) > index+1) && strings.Contains(parts[index+1], "{")
   204  		if shouldSingularize {
   205  			part = inflect.Singularize(part)
   206  		}
   207  
   208  		if parts[index] == "new" || parts[index] == "edit" {
   209  			resultParts = append([]string{part}, resultParts...)
   210  			continue
   211  		}
   212  
   213  		if index > 0 && strings.Contains(parts[index-1], "}") {
   214  			resultParts = append(resultParts, part)
   215  			continue
   216  		}
   217  
   218  		resultParts = append(resultParts, part)
   219  	}
   220  
   221  	if len(resultParts) == 0 {
   222  		return "unnamed"
   223  	}
   224  
   225  	underscore := strings.TrimSpace(strings.Join(resultParts, "_"))
   226  	return inflect.CamelizeDownFirst(underscore)
   227  }