浏览代码

Updated libs

Torkel Ödegaard 11 年之前
父节点
当前提交
c4a48b35c0

+ 1 - 0
.gitignore

@@ -18,3 +18,4 @@ data/sessions
 data/*.db
 data/log
 /bin/*
+/grafana-pro

+ 2 - 2
Godeps/Godeps.json

@@ -4,7 +4,7 @@
 	"Deps": [
 		{
 			"ImportPath": "github.com/Unknwon/com",
-			"Rev": "6c41aba9600e474c1a9e0521402c996b52944c4b"
+			"Rev": "d9bcf409c8a368d06c9b347705c381e7c12d54df"
 		},
 		{
 			"ImportPath": "github.com/Unknwon/goconfig",
@@ -12,7 +12,7 @@
 		},
 		{
 			"ImportPath": "github.com/Unknwon/macaron",
-			"Rev": "634a5d99660f7104f290a54e95ada2f8e34e46b2"
+			"Rev": "0f55900b417c019233ec27f86950bfb9feda8379"
 		},
 		{
 			"ImportPath": "github.com/codegangsta/cli",

+ 31 - 0
Godeps/_workspace/src/github.com/Unknwon/com/dir.go

@@ -16,6 +16,7 @@ package com
 
 import (
 	"errors"
+	"fmt"
 	"os"
 	"path"
 	"strings"
@@ -95,6 +96,36 @@ func GetAllSubDirs(rootPath string) ([]string, error) {
 	return statDir(rootPath, "", true, true)
 }
 
+// GetFileListBySuffix returns an ordered list of file paths.
+// It recognize if given path is a file, and don't do recursive find.
+func GetFileListBySuffix(dirPath, suffix string) ([]string, error) {
+	if !IsExist(dirPath) {
+		return nil, fmt.Errorf("given path does not exist: %s", dirPath)
+	} else if IsFile(dirPath) {
+		return []string{dirPath}, nil
+	}
+
+	// Given path is a directory.
+	dir, err := os.Open(dirPath)
+	if err != nil {
+		return nil, err
+	}
+
+	fis, err := dir.Readdir(0)
+	if err != nil {
+		return nil, err
+	}
+
+	files := make([]string, 0, len(fis))
+	for _, fi := range fis {
+		if strings.HasSuffix(fi.Name(), suffix) {
+			files = append(files, path.Join(dirPath, fi.Name()))
+		}
+	}
+
+	return files, nil
+}
+
 // CopyDir copy files recursively from source to target directory.
 //
 // The filter accepts a function that process the path info.

+ 37 - 5
Godeps/_workspace/src/github.com/Unknwon/com/http.go

@@ -15,6 +15,7 @@
 package com
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -43,10 +44,9 @@ func (e *RemoteError) Error() string {
 
 var UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36"
 
-// HttpGet gets the specified resource. ErrNotFound is returned if the
-// server responds with status 404.
-func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) {
-	req, err := http.NewRequest("GET", url, nil)
+// HttpCall makes HTTP method call.
+func HttpCall(client *http.Client, method, url string, header http.Header, body io.Reader) (io.ReadCloser, error) {
+	req, err := http.NewRequest(method, url, body)
 	if err != nil {
 		return nil, err
 	}
@@ -65,11 +65,23 @@ func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser
 	if resp.StatusCode == 404 { // 403 can be rate limit error.  || resp.StatusCode == 403 {
 		err = fmt.Errorf("resource not found: %s", url)
 	} else {
-		err = fmt.Errorf("get %s -> %d", url, resp.StatusCode)
+		err = fmt.Errorf("%s %s -> %d", method, url, resp.StatusCode)
 	}
 	return nil, err
 }
 
+// HttpGet gets the specified resource.
+// ErrNotFound is returned if the server responds with status 404.
+func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) {
+	return HttpCall(client, "GET", url, header, nil)
+}
+
+// HttpPost posts the specified resource.
+// ErrNotFound is returned if the server responds with status 404.
+func HttpPost(client *http.Client, url string, header http.Header, body []byte) (io.ReadCloser, error) {
+	return HttpCall(client, "POST", url, header, bytes.NewBuffer(body))
+}
+
 // HttpGetToFile gets the specified resource and writes to file.
 // ErrNotFound is returned if the server responds with status 404.
 func HttpGetToFile(client *http.Client, url string, header http.Header, fileName string) error {
@@ -115,6 +127,26 @@ func HttpGetJSON(client *http.Client, url string, v interface{}) error {
 	return nil
 }
 
+// HttpPostJSON posts the specified resource with struct values,
+// and maps results to struct.
+// ErrNotFound is returned if the server responds with status 404.
+func HttpPostJSON(client *http.Client, url string, body, v interface{}) error {
+	data, err := json.Marshal(body)
+	if err != nil {
+		return err
+	}
+	rc, err := HttpPost(client, url, http.Header{"content-type": []string{"application/json"}}, data)
+	if err != nil {
+		return err
+	}
+	defer rc.Close()
+	err = json.NewDecoder(rc).Decode(v)
+	if _, ok := err.(*json.SyntaxError); ok {
+		return fmt.Errorf("JSON syntax error at %s", url)
+	}
+	return nil
+}
+
 // A RawFile describes a file that can be downloaded.
 type RawFile interface {
 	Name() string

+ 37 - 31
Godeps/_workspace/src/github.com/Unknwon/com/string.go

@@ -15,47 +15,53 @@
 package com
 
 import (
+	"crypto/aes"
+	"crypto/cipher"
 	"crypto/rand"
-	"crypto/sha1"
-	"crypto/sha256"
-	"fmt"
-	"hash"
+	"encoding/base64"
+	"errors"
 	"io"
 	r "math/rand"
 	"strconv"
 	"strings"
 	"time"
-	"unicode"
 )
 
-func sha(m hash.Hash, str string) string {
-	io.WriteString(m, str)
-	return fmt.Sprintf("%x", m.Sum(nil))
-}
-
-// sha1 hash string
-func Sha1(str string) string {
-	return sha(sha1.New(), str)
-}
-
-// sha256 hash string
-func Sha256(str string) string {
-	return sha(sha256.New(), str)
-}
-
-// trim space on left
-func Ltrim(str string) string {
-	return strings.TrimLeftFunc(str, unicode.IsSpace)
-}
-
-// trim space on right
-func Rtrim(str string) string {
-	return strings.TrimRightFunc(str, unicode.IsSpace)
+// AESEncrypt encrypts text and given key with AES.
+func AESEncrypt(key, text []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	b := base64.StdEncoding.EncodeToString(text)
+	ciphertext := make([]byte, aes.BlockSize+len(b))
+	iv := ciphertext[:aes.BlockSize]
+	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+		return nil, err
+	}
+	cfb := cipher.NewCFBEncrypter(block, iv)
+	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
+	return ciphertext, nil
 }
 
-// replace find all occurs to string
-func StrReplace(str string, find string, to string) string {
-	return strings.Replace(str, find, to, -1)
+// AESDecrypt decrypts text and given key with AES.
+func AESDecrypt(key, text []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	if len(text) < aes.BlockSize {
+		return nil, errors.New("ciphertext too short")
+	}
+	iv := text[:aes.BlockSize]
+	text = text[aes.BlockSize:]
+	cfb := cipher.NewCFBDecrypter(block, iv)
+	cfb.XORKeyStream(text, text)
+	data, err := base64.StdEncoding.DecodeString(string(text))
+	if err != nil {
+		return nil, err
+	}
+	return data, nil
 }
 
 // IsLetter returns true if the 'l' is an English letter.

+ 9 - 1
Godeps/_workspace/src/github.com/Unknwon/macaron/README.md

@@ -5,7 +5,7 @@ Macaron [![Build Status](https://drone.io/github.com/Unknwon/macaron/status.png)
 
 Package macaron is a high productive and modular design web framework in Go.
 
-##### Current version: 0.4.5
+##### Current version: 0.4.9
 
 ## Getting Started
 
@@ -36,6 +36,7 @@ func main() {
 - Unlimited nested group routers.
 - Directly integrate with existing services.
 - Dynamically change template files at runtime.
+- Allow to use in-memory template and static files.
 - Easy to plugin/unplugin features with modular design.
 - Handy dependency injection powered by [inject](https://github.com/codegangsta/inject).
 - Better router layer and less reflection make faster speed.
@@ -46,6 +47,9 @@ Middlewares allow you easily plugin/unplugin features for your Macaron applicati
 
 There are already many [middlewares](https://github.com/macaron-contrib) to simplify your work:
 
+- gzip - Gzip compression to all requests
+- render - Go template engine
+- static - Serves static files
 - [binding](https://github.com/macaron-contrib/binding) - Request data binding and validation
 - [i18n](https://github.com/macaron-contrib/i18n) - Internationalization and Localization
 - [cache](https://github.com/macaron-contrib/cache) - Cache manager
@@ -54,8 +58,12 @@ There are already many [middlewares](https://github.com/macaron-contrib) to simp
 - [captcha](https://github.com/macaron-contrib/captcha) - Captcha service
 - [pongo2](https://github.com/macaron-contrib/pongo2) - Pongo2 template engine support
 - [sockets](https://github.com/macaron-contrib/sockets) - WebSockets channels binding
+- [bindata](https://github.com/macaron-contrib/bindata) - Embed binary data as static and template files
 - [toolbox](https://github.com/macaron-contrib/toolbox) - Health check, pprof, profile and statistic services
+- [oauth2](https://github.com/macaron-contrib/oauth2) - OAuth 2.0 backend
 - [switcher](https://github.com/macaron-contrib/switcher) - Multiple-site support
+- [method](https://github.com/macaron-contrib/method) - HTTP method override
+- [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions
 - [renders](https://github.com/macaron-contrib/renders) - Beego-like render engine(Macaron has built-in template engine, this is another option)
 
 ## Use Cases

+ 26 - 33
Godeps/_workspace/src/github.com/Unknwon/macaron/context.go

@@ -15,19 +15,17 @@
 package macaron
 
 import (
-	"crypto/hmac"
-	"crypto/sha1"
-	"encoding/base64"
-	"fmt"
+	"crypto/md5"
+	"encoding/hex"
 	"html/template"
 	"io"
 	"io/ioutil"
 	"mime/multipart"
 	"net/http"
+	"net/url"
 	"path"
 	"path/filepath"
 	"reflect"
-	"strconv"
 	"strings"
 	"time"
 
@@ -42,25 +40,28 @@ type Locale interface {
 	Tr(string, ...interface{}) string
 }
 
-// Body is the request's body.
+// RequestBody represents a request body.
 type RequestBody struct {
 	reader io.ReadCloser
 }
 
+// Bytes reads and returns content of request body in bytes.
 func (rb *RequestBody) Bytes() ([]byte, error) {
 	return ioutil.ReadAll(rb.reader)
 }
 
+// String reads and returns content of request body in string.
 func (rb *RequestBody) String() (string, error) {
 	data, err := rb.Bytes()
 	return string(data), err
 }
 
+// ReadCloser returns a ReadCloser for request body.
 func (rb *RequestBody) ReadCloser() io.ReadCloser {
 	return rb.reader
 }
 
-// A Request represents an HTTP request received by a server or to be sent by a client.
+// Request represents an HTTP request received by a server or to be sent by a client.
 type Request struct {
 	*http.Request
 }
@@ -239,7 +240,7 @@ func (ctx *Context) GetFile(name string) (multipart.File, *multipart.FileHeader,
 func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
 	cookie := http.Cookie{}
 	cookie.Name = name
-	cookie.Value = value
+	cookie.Value = url.QueryEscape(value)
 
 	if len(others) > 0 {
 		switch v := others[0].(type) {
@@ -296,7 +297,8 @@ func (ctx *Context) GetCookie(name string) string {
 	if err != nil {
 		return ""
 	}
-	return cookie.Value
+	val, _ := url.QueryUnescape(cookie.Value)
+	return val
 }
 
 // GetCookieInt returns cookie result in int type.
@@ -327,41 +329,32 @@ func (ctx *Context) GetSecureCookie(key string) (string, bool) {
 }
 
 // SetSuperSecureCookie sets given cookie value to response header with secret string.
-func (ctx *Context) SetSuperSecureCookie(Secret, name, value string, others ...interface{}) {
-	vs := base64.URLEncoding.EncodeToString([]byte(value))
-	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
-	h := hmac.New(sha1.New, []byte(Secret))
-	fmt.Fprintf(h, "%s%s", vs, timestamp)
-	sig := fmt.Sprintf("%02x", h.Sum(nil))
-	cookie := strings.Join([]string{vs, timestamp, sig}, "|")
-	ctx.SetCookie(name, cookie, others...)
+func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) {
+	m := md5.Sum([]byte(secret))
+	secret = hex.EncodeToString(m[:])
+	text, err := com.AESEncrypt([]byte(secret), []byte(value))
+	if err != nil {
+		panic("error encrypting cookie: " + err.Error())
+	}
+	ctx.SetCookie(name, hex.EncodeToString(text), others...)
 }
 
 // GetSuperSecureCookie returns given cookie value from request header with secret string.
-func (ctx *Context) GetSuperSecureCookie(Secret, key string) (string, bool) {
+func (ctx *Context) GetSuperSecureCookie(secret, key string) (string, bool) {
 	val := ctx.GetCookie(key)
 	if val == "" {
 		return "", false
 	}
 
-	parts := strings.SplitN(val, "|", 3)
-
-	if len(parts) != 3 {
+	data, err := hex.DecodeString(val)
+	if err != nil {
 		return "", false
 	}
 
-	vs := parts[0]
-	timestamp := parts[1]
-	sig := parts[2]
-
-	h := hmac.New(sha1.New, []byte(Secret))
-	fmt.Fprintf(h, "%s%s", vs, timestamp)
-
-	if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
-		return "", false
-	}
-	res, _ := base64.URLEncoding.DecodeString(vs)
-	return string(res), true
+	m := md5.Sum([]byte(secret))
+	secret = hex.EncodeToString(m[:])
+	text, err := com.AESDecrypt([]byte(secret), data)
+	return string(text), err == nil
 }
 
 // ServeContent serves given content to response.

+ 3 - 2
Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go

@@ -191,7 +191,8 @@ func Test_Context(t *testing.T) {
 			req, err := http.NewRequest("GET", "/set", nil)
 			So(err, ShouldBeNil)
 			m.ServeHTTP(resp, req)
-			So(strings.HasPrefix(resp.Header().Get("Set-Cookie"), "user=VW5rbndvbg==|"), ShouldBeTrue)
+
+			cookie := resp.Header().Get("Set-Cookie")
 
 			m.Get("/get", func(ctx *Context) string {
 				name, ok := ctx.GetSecureCookie("user")
@@ -202,7 +203,7 @@ func Test_Context(t *testing.T) {
 			resp = httptest.NewRecorder()
 			req, err = http.NewRequest("GET", "/get", nil)
 			So(err, ShouldBeNil)
-			req.Header.Set("Cookie", "user=VW5rbndvbg==|1409244667158399419|6097781707f68d9940ba1ef0e78cc84aaeebc48f; Path=/; Max-Age=1")
+			req.Header.Set("Cookie", cookie)
 			m.ServeHTTP(resp, req)
 			So(resp.Body.String(), ShouldEqual, "Unknwon")
 		})

+ 0 - 19
Godeps/_workspace/src/github.com/Unknwon/macaron/docs/zh-CN/README.md

@@ -1,19 +0,0 @@
-## 多站点支持
-
-如果您想要运行 2 或 2 个以上的实例在一个程序里,[HostSwitcher](https://gowalker.org/github.com/Unknwon/macaron#HostSwitcher) 就是您需要的特性:
-
-```go
-func main() {
-	m1 := macaron.Classic()
-	// Register m1 middlewares and routers.
-
-	m2 := macaron.Classic()
-	// Register m2 middlewares and routers.
-
-	hs := macaron.NewHostSwitcher()
-	// Set instance corresponding to host address.
-	hs.Set("gowalker.org", m1)
-	hs.Set("gogs.io", m2)
-	hs.Run()
-}
-```

+ 10 - 6
Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go

@@ -31,21 +31,25 @@ func init() {
 
 // Logger returns a middleware handler that logs the request as it goes in and the response as it goes out.
 func Logger() Handler {
-	return func(res http.ResponseWriter, req *http.Request, c *Context, log *log.Logger) {
+	return func(ctx *Context, log *log.Logger) {
 		start := time.Now()
 
-		log.Printf("Started %s %s for %s", req.Method, req.URL.Path, c.RemoteAddr())
+		log.Printf("Started %s %s for %s", ctx.Req.Method,ctx.Req.RequestURI, ctx.RemoteAddr())
 
-		rw := res.(ResponseWriter)
-		c.Next()
+		rw := ctx.Resp.(ResponseWriter)
+		ctx.Next()
 
-		content := fmt.Sprintf("Completed %s %v %s in %v", req.URL.Path, rw.Status(), http.StatusText(rw.Status()), time.Since(start))
+		content := fmt.Sprintf("Completed %s %v %s in %v",  ctx.Req.RequestURI, rw.Status(), http.StatusText(rw.Status()), time.Since(start))
 		if !isWindows {
 			switch rw.Status() {
-			case 200:
+			case 200, 201, 202:
 				content = fmt.Sprintf("\033[1;32m%s\033[0m", content)
+			case 301, 302:
+				content = fmt.Sprintf("\033[1;37m%s\033[0m", content)
 			case 304:
 				content = fmt.Sprintf("\033[1;33m%s\033[0m", content)
+			case 401, 403:
+				content = fmt.Sprintf("\033[4;31m%s\033[0m", content)
 			case 404:
 				content = fmt.Sprintf("\033[1;31m%s\033[0m", content)
 			case 500:

+ 21 - 0
Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go

@@ -22,6 +22,8 @@ import (
 	"net/http/httptest"
 	"testing"
 
+	"github.com/Unknwon/com"
+
 	. "github.com/smartystreets/goconvey/convey"
 )
 
@@ -43,4 +45,23 @@ func Test_Logger(t *testing.T) {
 		So(resp.Code, ShouldEqual, http.StatusNotFound)
 		So(len(buf.String()), ShouldBeGreaterThan, 0)
 	})
+
+	if !isWindows {
+		Convey("Color console output", t, func() {
+			m := Classic()
+			m.Get("/:code:int", func(ctx *Context) (int, string) {
+				return ctx.ParamsInt(":code"), ""
+			})
+
+			// Just for testing if logger would capture.
+			codes := []int{200, 201, 202, 301, 302, 304, 401, 403, 404, 500}
+			for _, code := range codes {
+				resp := httptest.NewRecorder()
+				req, err := http.NewRequest("GET", "http://localhost:4000/"+com.ToStr(code), nil)
+				So(err, ShouldBeNil)
+				m.ServeHTTP(resp, req)
+				So(resp.Code, ShouldEqual, code)
+			}
+		})
+	}
 }

+ 38 - 8
Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go

@@ -24,12 +24,15 @@ import (
 	"strings"
 
 	"github.com/Unknwon/com"
+	"gopkg.in/ini.v1"
 
 	"github.com/Unknwon/macaron/inject"
 )
 
+const _VERSION = "0.4.9.1229"
+
 func Version() string {
-	return "0.4.5.1122"
+	return _VERSION
 }
 
 // Handler can be any callable function.
@@ -39,9 +42,17 @@ type Handler interface{}
 
 // validateHandler makes sure a handler is a callable function,
 // and panics if it is not.
-func validateHandler(handler Handler) {
-	if reflect.TypeOf(handler).Kind() != reflect.Func {
-		panic("mocaron handler must be a callable function")
+func validateHandler(h Handler) {
+	if reflect.TypeOf(h).Kind() != reflect.Func {
+		panic("Macaron handler must be a callable function")
+	}
+}
+
+// validateHandlers makes sure handlers are callable functions,
+// and panics if any of them is not.
+func validateHandlers(handlers []Handler) {
+	for _, h := range handlers {
+		validateHandler(h)
 	}
 }
 
@@ -210,9 +221,9 @@ func (m *Macaron) SetURLPrefix(prefix string) {
 //                \/              \/    \/          \/     \/
 
 const (
-	DEV  string = "development"
-	PROD string = "production"
-	TEST string = "test"
+	DEV  = "development"
+	PROD = "production"
+	TEST = "test"
 )
 
 var (
@@ -225,6 +236,9 @@ var (
 
 	// Flash applies to current request.
 	FlashNow bool
+
+	// Configuration convention object.
+	cfg *ini.File
 )
 
 func setENV(e string) {
@@ -235,9 +249,25 @@ func setENV(e string) {
 
 func init() {
 	setENV(os.Getenv("MACARON_ENV"))
+
 	var err error
 	Root, err = os.Getwd()
 	if err != nil {
-		panic(err)
+		panic("error getting work directory: " + err.Error())
+	}
+}
+
+// SetConfig sets data sources for configuration.
+func SetConfig(source interface{}, others ...interface{}) (err error) {
+	cfg, err = ini.Load(source, others...)
+	return err
+}
+
+// Config returns configuration convention object.
+// It returns an empty object if there is no one available.
+func Config() *ini.File {
+	if cfg == nil {
+		return &ini.File{}
 	}
+	return cfg
 }

+ 15 - 4
Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go

@@ -25,6 +25,12 @@ import (
 	. "github.com/smartystreets/goconvey/convey"
 )
 
+func Test_Version(t *testing.T) {
+	Convey("Get version", t, func() {
+		So(Version(), ShouldEqual, _VERSION)
+	})
+}
+
 func Test_New(t *testing.T) {
 	Convey("Initialize a new instance", t, func() {
 		So(New(), ShouldNotBeNil)
@@ -171,11 +177,14 @@ func Test_Macaron_Written(t *testing.T) {
 func Test_Macaron_Basic_NoRace(t *testing.T) {
 	Convey("Make sure no race between requests", t, func() {
 		m := New()
+		handlers := []Handler{func() {}, func() {}}
+		// Ensure append will not realloc to trigger the race condition
+		m.handlers = handlers[:1]
 		m.Get("/", func() {})
+		req, _ := http.NewRequest("GET", "/", nil)
 		for i := 0; i < 2; i++ {
 			go func() {
 				resp := httptest.NewRecorder()
-				req, _ := http.NewRequest("GET", "/", nil)
 				m.ServeHTTP(resp, req)
 			}()
 		}
@@ -199,8 +208,10 @@ func Test_SetENV(t *testing.T) {
 	})
 }
 
-func Test_Version(t *testing.T) {
-	Convey("Get version", t, func() {
-		Version()
+func Test_Config(t *testing.T) {
+	Convey("Set and get configuration object", t, func() {
+		So(Config(), ShouldNotBeNil)
+		So(SetConfig([]byte("")), ShouldBeNil)
+		So(Config(), ShouldNotBeNil)
 	})
 }

+ 1 - 0
Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go

@@ -1,4 +1,5 @@
 // Copyright 2013 Martini Authors
+// Copyright 2014 Unknwon
 //
 // Licensed under the Apache License, Version 2.0 (the "License"): you may
 // not use this file except in compliance with the License. You may obtain

+ 0 - 3
Godeps/_workspace/src/github.com/Unknwon/macaron/render.go

@@ -307,9 +307,6 @@ func prepareOptions(options []RenderOptions) RenderOptions {
 	if len(opt.HTMLContentType) == 0 {
 		opt.HTMLContentType = ContentHTML
 	}
-	// if opt.TemplateFileSystem == nil {
-	// 	opt.TemplateFileSystem = newTemplateFileSystem(opt)
-	// }
 
 	return opt
 }

+ 20 - 30
Godeps/_workspace/src/github.com/Unknwon/macaron/router.go

@@ -147,10 +147,7 @@ func (r *Router) Handle(method string, pattern string, handlers []Handler) {
 		h = append(h, handlers...)
 		handlers = h
 	}
-	// verify handlers by cnphpbb at 20140803 23:51
-	for _, handler := range handlers {
-		validateHandler(handler)
-	}
+	validateHandlers(handlers)
 
 	r.handle(method, pattern, func(resp http.ResponseWriter, req *http.Request, params Params) {
 		c := r.m.createContext(resp, req)
@@ -217,8 +214,8 @@ func (r *Router) Route(pattern, methods string, h ...Handler) {
 }
 
 // Combo returns a combo router.
-func (r *Router) Combo(pattern string) *ComboRouter {
-	return &ComboRouter{r, pattern, map[string]bool{}}
+func (r *Router) Combo(pattern string, h ...Handler) *ComboRouter {
+	return &ComboRouter{r, pattern, h, map[string]bool{}}
 }
 
 // Configurable http.HandlerFunc which is called when no matching route is
@@ -253,9 +250,10 @@ func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 
 // ComboRouter represents a combo router.
 type ComboRouter struct {
-	router  *Router
-	pattern string
-	methods map[string]bool // Registered methods.
+	router   *Router
+	pattern  string
+	handlers []Handler
+	methods  map[string]bool // Registered methods.
 }
 
 func (cr *ComboRouter) checkMethod(name string) {
@@ -265,44 +263,36 @@ func (cr *ComboRouter) checkMethod(name string) {
 	cr.methods[name] = true
 }
 
-func (cr *ComboRouter) Get(h ...Handler) *ComboRouter {
-	cr.checkMethod("GET")
-	cr.router.Get(cr.pattern, h...)
+func (cr *ComboRouter) route(fn func(string, ...Handler), method string, h ...Handler) *ComboRouter {
+	cr.checkMethod(method)
+	fn(cr.pattern, append(cr.handlers, h...)...)
 	return cr
 }
 
+func (cr *ComboRouter) Get(h ...Handler) *ComboRouter {
+	return cr.route(cr.router.Get, "GET", h...)
+}
+
 func (cr *ComboRouter) Patch(h ...Handler) *ComboRouter {
-	cr.checkMethod("PATCH")
-	cr.router.Patch(cr.pattern, h...)
-	return cr
+	return cr.route(cr.router.Patch, "PATCH", h...)
 }
 
 func (cr *ComboRouter) Post(h ...Handler) *ComboRouter {
-	cr.checkMethod("POST")
-	cr.router.Post(cr.pattern, h...)
-	return cr
+	return cr.route(cr.router.Post, "POST", h...)
 }
 
 func (cr *ComboRouter) Put(h ...Handler) *ComboRouter {
-	cr.checkMethod("PUT")
-	cr.router.Put(cr.pattern, h...)
-	return cr
+	return cr.route(cr.router.Put, "PUT", h...)
 }
 
 func (cr *ComboRouter) Delete(h ...Handler) *ComboRouter {
-	cr.checkMethod("DELETE")
-	cr.router.Delete(cr.pattern, h...)
-	return cr
+	return cr.route(cr.router.Delete, "DELETE", h...)
 }
 
 func (cr *ComboRouter) Options(h ...Handler) *ComboRouter {
-	cr.checkMethod("OPTIONS")
-	cr.router.Options(cr.pattern, h...)
-	return cr
+	return cr.route(cr.router.Options, "OPTIONS", h...)
 }
 
 func (cr *ComboRouter) Head(h ...Handler) *ComboRouter {
-	cr.checkMethod("HEAD")
-	cr.router.Head(cr.pattern, h...)
-	return cr
+	return cr.route(cr.router.Head, "HEAD", h...)
 }

+ 12 - 9
Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go

@@ -110,21 +110,24 @@ func Test_Router_Handle(t *testing.T) {
 	Convey("Register all HTTP methods routes with combo", t, func() {
 		m := Classic()
 		m.SetURLPrefix("/prefix")
-		m.Combo("/").
-			Get(func() string { return "GET" }).
-			Patch(func() string { return "PATCH" }).
-			Post(func() string { return "POST" }).
-			Put(func() string { return "PUT" }).
-			Delete(func() string { return "DELETE" }).
-			Options(func() string { return "OPTIONS" }).
-			Head(func() string { return "HEAD" })
+		m.Use(Renderer())
+		m.Combo("/", func(ctx *Context) {
+			ctx.Data["prefix"] = "Prefix_"
+		}).
+			Get(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "GET" }).
+			Patch(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PATCH" }).
+			Post(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "POST" }).
+			Put(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PUT" }).
+			Delete(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "DELETE" }).
+			Options(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "OPTIONS" }).
+			Head(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "HEAD" })
 
 		for name := range _HTTP_METHODS {
 			resp := httptest.NewRecorder()
 			req, err := http.NewRequest(name, "/", nil)
 			So(err, ShouldBeNil)
 			m.ServeHTTP(resp, req)
-			So(resp.Body.String(), ShouldEqual, name)
+			So(resp.Body.String(), ShouldEqual, "Prefix_"+name)
 		}
 
 		defer func() {

+ 2 - 1
Godeps/_workspace/src/github.com/Unknwon/macaron/static.go

@@ -116,7 +116,7 @@ func prepareStaticOptions(dir string, options []StaticOptions) StaticOptions {
 
 func staticHandler(ctx *Context, log *log.Logger, opt StaticOptions) bool {
 	if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" {
-		return true
+		return false
 	}
 
 	file := ctx.Req.URL.Path
@@ -185,6 +185,7 @@ func Static(directory string, staticOpt ...StaticOptions) Handler {
 	}
 }
 
+// Statics registers multiple static middleware handlers all at once.
 func Statics(opt StaticOptions, dirs ...string) Handler {
 	if len(dirs) == 0 {
 		panic("no static directory is given")

+ 5 - 1
Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go

@@ -15,7 +15,7 @@
 
 package macaron
 
-// NOTE: last sync 90cff5f on Nov 2, 2014.
+// NOTE: last sync 0c93364 on Dec 19, 2014.
 
 import (
 	"path"
@@ -142,6 +142,10 @@ func NewTree() *Tree {
 // 		"/admin/" -> ["admin"]
 // 		"/admin/users" -> ["admin", "users"]
 func splitPath(pattern string) []string {
+	if len(pattern) == 0 {
+		return []string{}
+	}
+
 	elements := strings.Split(pattern, "/")
 	if elements[0] == "" {
 		elements = elements[1:]

+ 1 - 0
Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go

@@ -66,6 +66,7 @@ func Test_Tree_Match(t *testing.T) {
 		{"/:id", "/123", map[string]string{":id": "123"}},
 		{"/hello/?:id", "/hello", map[string]string{":id": ""}},
 		{"/", "/", nil},
+		{"", "", nil},
 		{"/customer/login", "/customer/login", nil},
 		{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}},
 		{"/*", "/customer/123", map[string]string{":splat": "customer/123"}},

+ 0 - 1
Godeps/_workspace/src/github.com/Unknwon/macaron/wercker.yml

@@ -1 +0,0 @@
-box: wercker/default

+ 1 - 1
Makefile

@@ -7,7 +7,7 @@ build:
 	go test ./pkg/...
 
 lint:
-	@gofmt -w . && go tool vet pkg/**/*.go && echo "$(GOLINT)"
+	@gofmt -w pkg && go tool vet pkg/**/*.go && echo "$(GOLINT)"
 
 setup:
 	go get github.com/tools/godep

二进制
grafana-pro