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 }