rata

package
v0.0.0-...-6e5da2a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 16, 2016 License: MIT, Apache-2.0 Imports: 7 Imported by: 0

README

Rata: It's a smat rata. Wicked smat.

Rata is a router with Pat-style path patterns, plus more.

API Docs: https://godoc.org/github.com/tedsuo/rata

Package rata provides three things: Routes, a Router, and a RequestGenerator.

Routes are structs that define which Method and Path each associated http handler should respond to. Unlike many router implementations, the routes and the handlers are defined separately. This allows for the routes to be reused in multiple contexts. For example, a proxy server and a backend server can be created by having one set of Routes, but two sets of Handlers (one handler that proxies, another that serves the request). Likewise, your client code can use the routes with the RequestGenerator to create requests that use the same routes. Then, if the routes change, unit tests in the client and proxy service will warn you of the problem. This contract helps components stay in sync while relying less on integration tests.

For example, let's imagine that you want to implement a "pet" resource that allows you to view, create, update, and delete which pets people own. Also, you would like to include the owner_id and pet_id as part of the URL path.

First off, the routes might look like this:

  petRoutes := rata.Routes{
    {Name: "get_pet",    Method: "GET",    Path: "/people/:owner_id/pets/:pet_id"},
    {Name: "create_pet", Method: "POST",   Path: "/people/:owner_id/pets"},
    {Name: "update_pet", Method: "PUT",    Path: "/people/:owner_id/pets/:pet_id"},
    {Name: "delete_pet", Method: "DELETE", Path: "/people/:owner_id/pets/:pet_id"},
  }

On the server, create a matching set of http handlers, one for each route:

  petHandlers := rata.Handlers{
    "get_pet":    newGetPetHandler(),
    "create_pet": newCreatePetHandler(),
    "update_pet": newUpdatePetHandler(),
    "delete_pet": newDeletePetHandler()
  }

You can create a router by mixing the routes and handlers together:

  router, err := rata.NewRouter(petRoutes, petHandlers)
  if err != nil {
    panic(err)
  }

  // The router is just an http.Handler, so it can be used to create a server in the usual fashion:
  server := httptest.NewServer(router)

Meanwhile, on the client side, you can create a request generator:

  requestGenerator := rata.NewRequestGenerator(server.URL, petRoutes)

  // You can use the request generator to ensure you are creating a valid request:
  req, err := requestGenerator.CreateRequest("get_pet", rata.Params{"owner_id": "123", "pet_id": "5"}, nil)

  // The generated request can be used like any other http.Request object:
  res, err := http.DefaultClient.Do(req)

Documentation

Overview

Package rata provides three things: Routes, a Router, and a RequestGenerator.

Routes are structs that define which Method and Path each associated http handler should respond to. Unlike many router implementations, the routes and the handlers are defined separately. This allows for the routes to be reused in multiple contexts. For example, a proxy server and a backend server can be created by having one set of Routes, but two sets of Handlers (one handler that proxies, another that serves the request). Likewise, your client code can use the routes with the RequestGenerator to create requests that use the same routes. Then, if the routes change, unit tests in the client and proxy service will warn you of the problem. This contract helps components stay in sync while relying less on integration tests.

For example, let's imagine that you want to implement a "pet" resource that allows you to view, create, update, and delete which pets people own. Also, you would like to include the owner_id and pet_id as part of the URL path.

First off, the routes might look like this:

petRoutes := rata.Routes{
  {Name: "get_pet",    Method: "GET",    Path: "/people/:owner_id/pets/:pet_id"},
  {Name: "create_pet", Method: "POST",   Path: "/people/:owner_id/pets"},
  {Name: "update_pet", Method: "PUT",    Path: "/people/:owner_id/pets/:pet_id"},
  {Name: "delete_pet", Method: "DELETE", Path: "/people/:owner_id/pets/:pet_id"},
}

On the server, create a matching set of http handlers, one for each route:

handlers := rata.Handlers{
  "get_pet":    newGetPetHandler(),
  "create_pet": newCreatePetHandler(),
  "update_pet": newUpdatePetHandler(),
  "delete_pet": newDeletePetHandler()
}

You can create a router by mixing the routes and handlers together:

router, err := rata.NewRouter(petRoutes, handlers)
if err != nil {
  panic(err)
}

The router is just an http.Handler, so it can be used to create a server in the usual fashion:

server := httptest.NewServer(router)

Meanwhile, on the client side, you can create a request generator:

requestGenerator := rata.NewRequestGenerator(server.URL, petRoutes)

You can use the request generator to ensure you are creating a valid request:

req, err := requestGenerator.CreateRequest("get_pet", rata.Params{"owner_id": "123", "pet_id": "5"}, nil)

The generated request can be used like any other http.Request object:

res, err := http.DefaultClient.Do(req)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewRouter

func NewRouter(routes Routes, handlers Handlers) (http.Handler, error)

NewRouter combines a set of Routes with their corresponding Handlers to produce a http request multiplexer (AKA a "router"). If any route does not have a matching handler, an error occurs.

Types

type Handlers

type Handlers map[string]http.Handler

Handlers map route names to http.Handler objects. Each Handler key must match a route Name in the Routes collection.

type Params

type Params map[string]string

Params map path keys to values. For example, if your route has the path pattern:

/person/:person_id/pets/:pet_type

Then a correct Params map would lool like:

router.Params{
  "person_id": "123",
  "pet_type": "cats",
}

type RequestGenerator

type RequestGenerator struct {
	Header http.Header
	// contains filtered or unexported fields
}

RequestGenerator creates http.Request objects with the correct path and method pre-filled for the given route object. You can also set the the host and, optionally, any headers you would like included with every request.

func NewRequestGenerator

func NewRequestGenerator(host string, routes Routes) *RequestGenerator

NewRequestGenerator creates a RequestGenerator for a given host and route set. Host is of the form "http://example.com".

func (*RequestGenerator) CreateRequest

func (r *RequestGenerator) CreateRequest(
	name string,
	params Params,
	body io.Reader,
) (*http.Request, error)

CreateRequest creates a new http Request for the matching handler. If the request cannot be created, either because the handler does not exist or because the given params do not match the params the route requires, then CreateRequest returns an error.

type Route

type Route struct {
	// Name is a key specifying which HTTP handler the router
	// should associate with the endpoint at runtime.
	Name string
	// Method is one of the following: GET,PUT,POST,DELETE
	Method string
	// Path contains a path pattern
	Path string
}

A Route defines properties of an HTTP endpoint. At runtime, the router will associate each Route with a http.Handler object, and use the Route properties to determine which Handler should be invoked.

Currently, the properties used for matching are Method and Path.

Method can be one of the following:

GET PUT POST DELETE

Path conforms to Pat-style pattern matching. The following docs are taken from http://godoc.org/github.com/bmizerany/pat#PatternServeMux

Path Patterns may contain literals or captures. Capture names start with a colon and consist of letters A-Z, a-z, _, and 0-9. The rest of the pattern matches literally. The portion of the URL matching each name ends with an occurrence of the character in the pattern immediately following the name, or a /, whichever comes first. It is possible for a name to match the empty string.

Example pattern with one capture:

/hello/:name

Will match:

/hello/blake
/hello/keith

Will not match:

/hello/blake/
/hello/blake/foo
/foo
/foo/bar

Example 2:

/hello/:name/

Will match:

/hello/blake/
/hello/keith/foo
/hello/blake
/hello/keith

Will not match:

/foo
/foo/bar

func (Route) CreatePath

func (r Route) CreatePath(params Params) (string, error)

CreatePath combines the route's path pattern with a Params map to produce a valid path.

type Routes

type Routes []Route

Routes is a Route collection.

func (Routes) CreatePathForRoute

func (r Routes) CreatePathForRoute(name string, params Params) (string, error)

Path looks up a Route by it's Handler key and computes it's path with a given Params map.

func (Routes) FindRouteByName

func (r Routes) FindRouteByName(name string) (Route, bool)

Route looks up a Route by it's Handler key.

func (Routes) Router

func (r Routes) Router(handlers Handlers) (http.Handler, error)

Router is deprecated, please use router.NewRouter() instead

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL