Skip to content

Commit

Permalink
Fix group logic (go-playground#18)
Browse files Browse the repository at this point in the history
* Correct Group logic
* break up Group function

Group handled 3 separate pieces of logic, which have now been broken out for clarity and ease of use into:
Group - groups retaining existing middleware
GroupWithMore - groups retaining middleware and adding additional
GroupWithNone - groups but retains no middleware

* Update Group documentation
* Updated examples DIR to _examples

renamed to ensure example dependencies, if any, aren't fetched during go get
  • Loading branch information
Dean Karn authored Jun 25, 2017
1 parent 5684150 commit e8bf16b
Show file tree
Hide file tree
Showing 15 changed files with 110 additions and 43 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
## LARS
<img align="right" src="https://raw.githubusercontent.com/go-playground/lars/master/examples/README/test.gif">![Project status](https://img.shields.io/badge/version-3.7.0-green.svg)
<img align="right" src="https://raw.githubusercontent.com/go-playground/lars/master/_examples/README/test.gif">![Project status](https://img.shields.io/badge/version-4.0.0-green.svg)
[![Build Status](https://semaphoreci.com/api/v1/projects/4351aa2d-2f94-40be-a6ef-85c248490378/679708/badge.svg)](https://semaphoreci.com/joeybloggs/lars)
[![Coverage Status](https://coveralls.io/repos/github/go-playground/lars/badge.svg?branch=master)](https://coveralls.io/github/go-playground/lars?branch=master)
[![Go Report Card](https://goreportcard.com/badge/go-playground/lars)](https://goreportcard.com/report/go-playground/lars)
[![GoDoc](https://godoc.org/github.com/go-playground/lars?status.svg)](https://godoc.org/github.com/go-playground/lars)
![License](https://img.shields.io/dub/l/vibe-d.svg)
[![Gitter](https://badges.gitter.im/go-playground/lars.svg)](https://gitter.im/go-playground/lars?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

LARS is a fast radix-tree based, zero allocation, HTTP router for Go. [ view examples](https://github.com/go-playground/lars/tree/master/examples). If looking for a more pure Go solution, be sure to check out [pure](https://github.com/go-playground/pure) which is essentially a pure version of lars
LARS is a fast radix-tree based, zero allocation, HTTP router for Go. [ view examples](https://github.com/go-playground/lars/tree/master/_examples). If looking for a more pure Go solution, be sure to check out [pure](https://github.com/go-playground/pure) which is essentially a pure version of lars

Why Another HTTP Router?
------------------------
Have you ever been painted into a corner by a framework, **ya me too!** and I've noticed that allot of routers out there, IMHO, are adding so much functionality that they are turning into Web Frameworks, (which is fine, frameworks are important) however, not at the expense of flexibility and configurability. So with no further ado, introducing LARS an HTTP router that can be your launching pad in creating a framework for your needs. How? Context is an interface [see example here](https://github.com/go-playground/lars/blob/master/examples/all-in-one/main.go), where you can add as little or much as you want or need and most importantly...**under your control**.
Have you ever been painted into a corner by a framework, **ya me too!** and I've noticed that allot of routers out there, IMHO, are adding so much functionality that they are turning into Web Frameworks, (which is fine, frameworks are important) however, not at the expense of flexibility and configurability. So with no further ado, introducing LARS an HTTP router that can be your launching pad in creating a framework for your needs. How? Context is an interface [see example here](https://github.com/go-playground/lars/blob/master/_examples/all-in-one/main.go), where you can add as little or much as you want or need and most importantly...**under your control**.

Key & Unique Features
--------------
- [x] **Context is an interface** - this allows passing of framework/globals/application specific variables. [example](https://github.com/go-playground/lars/blob/master/examples/all-in-one/main.go)
- [x] **Context is an interface** - this allows passing of framework/globals/application specific variables. [example](https://github.com/go-playground/lars/blob/master/_examples/all-in-one/main.go)
- [x] **Smart Route Logic** - helpful logic to help prevent adding bad routes, keeping your url's consistent. i.e. /user/:id and /user/:user_id - the second one will fail to add letting you know that :user_id should be :id
- [x] **Uber simple middleware + handlers** - middleware and handlers actually have the exact same definition!
- [x] **Custom Handlers** - can register custom handlers for making other middleware + handler patterns usable with this router; the best part about this is can register one for your custom context and not have to do type casting everywhere [see here](https://github.com/go-playground/lars/blob/master/examples/custom-handler/main.go)
- [x] **Diverse handler support** - Full support for standard/native http Handler + HandlerFunc + some others [see here](https://github.com/go-playground/lars/blob/master/examples/native/main.go)
- [x] **Diverse handler support** - Full support for standard/native http Handler + HandlerFunc + some others [see here](https://github.com/go-playground/lars/blob/master/_examples/native/main.go)
* When Parsing a form call Context's ParseForm amd ParseMulipartForm functions and the URL params will be added into the Form object, just like query parameters are, so no extra work
- [x] **Fast & Efficient** - lars uses a custom version of [httprouter](https://github.com/julienschmidt/httprouter) so incredibly fast and efficient.

Expand All @@ -32,7 +32,7 @@ go get -u github.com/go-playground/lars

Usage
------
Below is a simple example, for a full example [see here](https://github.com/go-playground/lars/blob/master/examples/all-in-one/main.go)
Below is a simple example, for a full example [see here](https://github.com/go-playground/lars/blob/master/_examples/all-in-one/main.go)
```go
package main

Expand All @@ -41,7 +41,7 @@ import (
"net/http"

"github.com/go-playground/lars"
mw "github.com/go-playground/lars/examples/middleware/logging-recovery"
mw "github.com/go-playground/lars/_examples/middleware/logging-recovery"
)

func main() {
Expand Down Expand Up @@ -100,10 +100,10 @@ contactinfo.Delete("/delete", ...)

// creates a group for others + inherits all middleware registered using l.Use() + adds
// OtherHandler to middleware
others := l.Group("/others", OtherHandler)
others := l.GroupWithMore("/others", OtherHandler)

// creates a group for admin WITH NO MIDDLEWARE... more can be added using admin.Use()
admin := l.Group("/admin",nil)
admin := l.GroupWithNone("/admin")
admin.Use(SomeAdminSecurityMiddleware)
...
```
Expand Down Expand Up @@ -174,7 +174,7 @@ func Home(c *MyContext) {

Decoding Body
-------------
For full example see [here](https://github.com/go-playground/lars/blob/master/examples/decode/main.go).
For full example see [here](https://github.com/go-playground/lars/blob/master/_examples/decode/main.go).
currently JSON, XML, FORM + Multipart Form's are support out of the box.
```go
// first argument denotes yes or no I would like URL query parameter fields
Expand Down Expand Up @@ -253,8 +253,8 @@ comply with the following rule(s):

* Are completely reusable by the community without modification

Other middleware will be listed under the examples/middleware/... folder for a quick copy/paste modify. as an example a logging or
recovery middleware are very application dependent and therefore will be listed under the examples/middleware/...
Other middleware will be listed under the _examples/middleware/... folder for a quick copy/paste modify. as an example a logging or
recovery middleware are very application dependent and therefore will be listed under the _examples/middleware/...

Benchmarks
-----------
Expand Down
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"net/http"

"github.com/go-playground/lars"
mw "github.com/go-playground/lars/examples/middleware/logging-recovery"
mw "github.com/go-playground/lars/_examples/middleware/logging-recovery"
)

func main() {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"time"

"github.com/go-playground/lars"
"github.com/go-playground/lars/examples/middleware/logging-recovery"
"github.com/go-playground/lars/_examples/middleware/logging-recovery"
"github.com/gorilla/websocket"
)

Expand Down
10 changes: 5 additions & 5 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ Package lars - Library Access/Retrieval System, is a fast radix-tree based, zero
Usage
Below is a simple example, for a full example see here https://github.com/go-playground/lars/blob/master/examples/all-in-one/main.go
Below is a simple example, for a full example see here https://github.com/go-playground/lars/blob/master/_examples/all-in-one/main.go
package main
import (
"net/http"
"github.com/go-playground/lars"
mw "github.com/go-playground/lars/examples/middleware/logging-recovery"
mw "github.com/go-playground/lars/_examples/middleware/logging-recovery"
)
func main() {
Expand Down Expand Up @@ -73,10 +73,10 @@ example group definitions
// creates a group for others + inherits all middleware registered using l.Use() +
// adds OtherHandler to middleware
others := l.Group("/others", OtherHandler)
others := l.GroupWithMore("/others", OtherHandler)
// creates a group for admin WITH NO MIDDLEWARE... more can be added using admin.Use()
admin := l.Group("/admin",nil)
admin := l.GroupWithNone("/admin")
admin.Use(SomeAdminSecurityMiddleware)
...
Expand Down Expand Up @@ -154,7 +154,7 @@ example context + custom handlers
Decoding Body
For full example see https://github.com/go-playground/lars/blob/master/examples/decode/main.go
For full example see https://github.com/go-playground/lars/blob/master/_examples/decode/main.go
currently JSON, XML, FORM + Multipart Form's are support out of the box.
// first argument denotes yes or no I would like URL query parameter fields
Expand Down
49 changes: 27 additions & 22 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
// IRouteGroup interface for router group
type IRouteGroup interface {
IRoutes
Group(prefix string, middleware ...Handler) IRouteGroup
GroupWithNone(prefix string) IRouteGroup
GroupWithMore(prefix string, middleware ...Handler) IRouteGroup
Group(prefix string) IRouteGroup
}

// IRoutes interface for routes
Expand Down Expand Up @@ -176,31 +178,34 @@ func (g *routeGroup) WebSocket(upgrader websocket.Upgrader, path string, h Handl
}, handler)
}

// Group creates a new sub router with prefix. It inherits all properties from
// the parent. Passing middleware overrides parent middleware but still keeps
// the root level middleware intact.
func (g *routeGroup) Group(prefix string, middleware ...Handler) IRouteGroup {

rg := &routeGroup{
prefix: g.prefix + prefix,
lars: g.lars,
}

if len(middleware) == 0 {
rg.middleware = make(HandlersChain, len(g.middleware)+len(middleware))
copy(rg.middleware, g.middleware)

return rg
// GroupWithNone creates a new sub router with specified prefix and no middleware attached.
func (g *routeGroup) GroupWithNone(prefix string) IRouteGroup {
return &routeGroup{
prefix: g.prefix + prefix,
lars: g.lars,
middleware: make(HandlersChain, 0),
}
}

if middleware[0] == nil {
rg.middleware = make(HandlersChain, 0)
return rg
// GroupWithMore creates a new sub router with specified prefix, retains existing middleware and adds new middleware.
func (g *routeGroup) GroupWithMore(prefix string, middleware ...Handler) IRouteGroup {
rg := &routeGroup{
prefix: g.prefix + prefix,
lars: g.lars,
middleware: make(HandlersChain, len(g.middleware)),
}

rg.middleware = make(HandlersChain, len(g.lars.middleware))
copy(rg.middleware, g.lars.middleware)
copy(rg.middleware, g.middleware)
rg.Use(middleware...)
return rg
}

// Group creates a new sub router with specified prefix and retains existing middleware.
func (g *routeGroup) Group(prefix string) IRouteGroup {
rg := &routeGroup{
prefix: g.prefix + prefix,
lars: g.lars,
middleware: make(HandlersChain, len(g.middleware)),
}
copy(rg.middleware, g.middleware)
return rg
}
62 changes: 62 additions & 0 deletions group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,65 @@ func TestWebsockets(t *testing.T) {
Equal(t, wsBad, nil)
Equal(t, res.StatusCode, http.StatusForbidden)
}

func TestGrouplogic(t *testing.T) {

var aa, bb, cc, tl int

aM := func(c Context) {
aa++
c.Next()
}

bM := func(c Context) {
bb++
c.Next()
}

cM := func(c Context) {
cc++
c.Next()
}

l := New()
l.Use(func(c Context) {
tl++
c.Next()
})

a := l.GroupWithMore("/a", aM)
a.Get("/test", func(c Context) {
c.JSON(http.StatusOK, "a-ok")
})

b := a.GroupWithMore("/b", bM)
b.Get("/test", func(c Context) {
c.JSON(http.StatusOK, "b-ok")
})

c := b.GroupWithMore("/c", cM)
c.Get("/test", func(c Context) {
c.JSON(http.StatusOK, "c-ok")
})

code, body := request(GET, "/a/test", l)
Equal(t, code, http.StatusOK)
Equal(t, body, "\"a-ok\"")
Equal(t, tl, 1)
Equal(t, aa, 1)

code, body = request(GET, "/a/b/test", l)
Equal(t, code, http.StatusOK)
Equal(t, body, "\"b-ok\"")
Equal(t, tl, 2)
Equal(t, aa, 2)
Equal(t, bb, 1)

code, body = request(GET, "/a/b/c/test", l)
Equal(t, code, http.StatusOK)
Equal(t, body, "\"c-ok\"")
Equal(t, tl, 3)
Equal(t, aa, 3)
Equal(t, bb, 2)
Equal(t, cc, 1)
}
4 changes: 2 additions & 2 deletions lars_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func TestUseAndGroup(t *testing.T) {
c.Next()
}

sh := l.Group("/superheros", logger2)
sh := l.GroupWithMore("/superheros", logger2)
sh.Get("/", fn)
sh.Get("/list/", fn)

Expand Down Expand Up @@ -381,7 +381,7 @@ func TestUseAndGroup(t *testing.T) {

log = ""

g2 := l.Group("/admins", nil)
g2 := l.GroupWithNone("/admins")
g2.Get("/", fn)
g2.Get("/list/", fn)

Expand Down

0 comments on commit e8bf16b

Please sign in to comment.