darwini

package module
v0.0.0-...-aca6307 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2018 License: MIT Imports: 2 Imported by: 0

README

darwini -- Go web programming with strong types & strong opinions

C. darwini is the spider species with the toughest silk.

Darwini is experimental for now.

https://godoc.org/github.com/tv42/darwini

Documentation

Overview

Package darwini provides web programming conveniences.

The hierarchical URL path based request multiplexers manipulate Request.URL.Path so that only the delegated part of the path remains. The multiplexers operate on the first segment in Request.URL.Path. This allows constructing hierarchies easily.

Example
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strconv"
	"strings"

	"github.com/tv42/darwini"
)

var (
	ErrNotFound = errors.New("not found")
)

type gistStore struct {
	gists map[uint64]*gist
}

func (s *gistStore) list(w http.ResponseWriter, req *http.Request) {
	json.NewEncoder(w).Encode(s.gists)
}

func (s *gistStore) listPublic(w http.ResponseWriter, req *http.Request) {
	json.NewEncoder(w).Encode(s.gists)
}

func (s *gistStore) listStarred(w http.ResponseWriter, req *http.Request) {
	json.NewEncoder(w).Encode(s.gists)
}

func (s *gistStore) create(w http.ResponseWriter, req *http.Request) {
	i := uint64(len(s.gists)) + 1
	g := &gist{ID: uint64(i), store: s}
	s.gists[i] = g
	json.NewEncoder(w).Encode(i)
}

func (s *gistStore) get(seg string) (*gist, error) {
	id, err := strconv.ParseUint(seg, 10, 64)
	if err != nil {
		return nil, err
	}
	g := s.gists[id]
	if g == nil {
		return nil, ErrNotFound
	}
	return g, nil
}

type gist struct {
	ID    uint64
	Text  string
	store *gistStore
	Star  bool
}

func (g *gist) get(w http.ResponseWriter, req *http.Request)   {}
func (g *gist) patch(w http.ResponseWriter, req *http.Request) {}
func (g *gist) del(w http.ResponseWriter, req *http.Request)   {}
func (g *gist) isStar(w http.ResponseWriter, req *http.Request) {
	json.NewEncoder(w).Encode(g.Star)
}
func (g *gist) star(w http.ResponseWriter, req *http.Request) {
	g.Star = true
}
func (g *gist) unstar(w http.ResponseWriter, req *http.Request) {
	g.Star = false
}
func (g *gist) forks(w http.ResponseWriter, req *http.Request) {
}

func removeSlash(w http.ResponseWriter, req *http.Request) {}

/*
GET /gists
GET /gists/public
GET /gists/starred
GET /gists/:id
POST /gists
PATCH /gists/:id
PUT /gists/:id/star
DELETE /gists/:id/star
GET /gists/:id/star
POST /gists/:id/forks
DELETE /gists/:id
*/

func main() {
	gists := &gistStore{
		gists: map[uint64]*gist{},
	}
	m := darwini.Map{
		"gists": darwini.Dir{
			Parent: darwini.Method{
				GET:  gists.list,
				POST: gists.create,
			},
			Child: darwini.Var{
				Child: func(seg string) http.Handler {
					// Mixing dynamic and static segments is just bad,
					// so we won't bother to assist in that. Write code.
					switch seg {
					case "public":
						return http.HandlerFunc(gists.listPublic)
					case "starred":
						return http.HandlerFunc(gists.listStarred)
					}
					g, err := gists.get(seg)
					if err != nil {
						return nil // not found
					}
					return darwini.Dir{
						Parent: darwini.Method{
							GET:    g.get,
							PATCH:  g.patch,
							DELETE: g.del,
						},
						Child: darwini.Map{
							"": http.HandlerFunc(removeSlash),
							"star": darwini.Method{
								GET:    g.isStar,
								PUT:    g.star,
								DELETE: g.unstar,
							},
							"forks": darwini.Method{
								GET: g.forks,
							},
						},
					}
				},
			},
		},
	}
	s := httptest.NewServer(m)
	defer s.Close()

	resp, err := http.Post(s.URL+"/gists", "text/plain", strings.NewReader("hello, world"))
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		fmt.Println(resp.Status)
		return
	}
	var id uint64
	if err := json.NewDecoder(resp.Body).Decode(&id); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("created gist", id)

	resp, err = http.Get(s.URL + "/gists/" + strconv.FormatUint(id, 10) + "/star")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		fmt.Println(resp.Status)
		return
	}
	var starred bool
	if err := json.NewDecoder(resp.Body).Decode(&starred); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("gist %d is starred: %v\n", id, starred)

}
Output:

created gist 1
gist 1 is starred: false

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Dir

type Dir struct {
	Parent http.Handler
	Child  http.Handler
}

Dir multiplexes requests between a parent resource and children. For Dir at /path, Parent will serve requests to /path, while Child will serve requests to /path/anything.

func (Dir) ServeHTTP

func (d Dir) ServeHTTP(w http.ResponseWriter, req *http.Request)

type Error

type Error interface {
	error
	http.Handler
}

Error allows errors to specify their own HTTP status code and an error message that is safe to show to untrusted clients.

type Map

type Map map[string]http.Handler

Map multiplexes requests to children based on a map lookup. For Map at /path, /path will be forbidden, /path/seg and /path/seg/anything are served by the map entry for seg, or not found if nil.

As a special case, missing /path/ is forbidden instead of not found, to avoid a situation where /path/foo exists but its parent does not.

func (Map) ServeHTTP

func (c Map) ServeHTTP(w http.ResponseWriter, req *http.Request)

type Method

type Method struct {
	GET    http.HandlerFunc
	POST   http.HandlerFunc
	PUT    http.HandlerFunc
	PATCH  http.HandlerFunc
	DELETE http.HandlerFunc
	Custom map[string]http.HandlerFunc
}

Method multiplexes requests based on the HTTP method. The handler for a method is set either with the predefined fields, or for custom methods, with the Custom map.

The values here are HandlerFuncs and not Handlers, as it is common to make them be methods on the same value.

func (Method) ServeHTTP

func (m Method) ServeHTTP(w http.ResponseWriter, req *http.Request)

type Var

type Var struct {
	Index http.Handler
	Child func(seg string) http.Handler
}

Var multiplexes dynamically based on the next path segment. For Var at /path, /path will be forbidden, /path/ is served by Index or forbidden if nil, /path/seg and /path/seg/anything are served by the handler Child returns for seg, or not found if Child is nil or returns nil.

func (Var) ServeHTTP

func (v Var) ServeHTTP(w http.ResponseWriter, req *http.Request)

Jump to

Keyboard shortcuts

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