Merge 5417a27f9e76394ff3768148814a8b862e13703c into a5ae88722b552adf00b87c7ae5f2fb84d56ebe6f

This commit is contained in:
Wael M. Nasreddine 2014-08-24 21:55:56 +00:00
commit cb872a054c
11 changed files with 163 additions and 30 deletions

View File

@ -5,7 +5,7 @@ List of all the awesome people working to make Gin the best Web Framework in Go!
##gin 0.x series authors ##gin 0.x series authors
**Lead Developer:** Manu Martinez-Almeida (@manucorporat) **Lead Developer:** Manu Martinez-Almeida (@manucorporat)
**Stuff:** **Staff:**
Javier Provecho (@javierprovecho) Javier Provecho (@javierprovecho)
People and companies, who have contributed, in alphabetical order. People and companies, who have contributed, in alphabetical order.
@ -22,10 +22,13 @@ People and companies, who have contributed, in alphabetical order.
- Using template.Must to fix multiple return issue - Using template.Must to fix multiple return issue
- ★ Added support for OPTIONS verb - ★ Added support for OPTIONS verb
- ★ Setting response headers before calling WriteHeader - ★ Setting response headers before calling WriteHeader
- Improved documentation for model binding
- ★ Added Content.Redirect()
- ★ Added tons of Unit tests
**@austinheap (Austin Heap)** **@austinheap (Austin Heap)**
- Adds travis CI integration - Added travis CI integration
**@bluele (Jun Kimura)** **@bluele (Jun Kimura)**
@ -67,20 +70,23 @@ People and companies, who have contributed, in alphabetical order.
**@mdigger (Dmitry Sedykh)** **@mdigger (Dmitry Sedykh)**
- Fixes Form binding when content-type is x-www-form-urlencoded - Fixes Form binding when content-type is x-www-form-urlencoded
- No repeat call c.Writer.Status() in gin.Logger - No repeat call c.Writer.Status() in gin.Logger
- Fixed Content-Type for json render - Fixes Content-Type for json render
**@mopemope (Yutaka Matsubara)** **@mopemope (Yutaka Matsubara)**
- ★ Adds Godep support (Dependencies Manager) - ★ Adds Godep support (Dependencies Manager)
- Fix variadic parameter in the flexible render API - Fix variadic parameter in the flexible render API
- Fix Corrupted plain render - Fix Corrupted plain render
- Fix variadic parameter in new flexible render API
**@msemenistyi (Mykyta Semenistyi)** **@msemenistyi (Mykyta Semenistyi)**
- update Readme.md. Add code to String method - update Readme.md. Add code to String method
**@msoedov (Sasha Myasoedov)**
- ★ Adds tons of unit tests.
**@ngerakines (Nick Gerakines)** **@ngerakines (Nick Gerakines)**
- ★ Improves API, c.GET() doesn't panic - ★ Improves API, c.GET() doesn't panic
- Adds MustGet() method - Adds MustGet() method
@ -96,3 +102,7 @@ People and companies, who have contributed, in alphabetical order.
**@SkuliOskarsson (Skuli Oskarsson)** **@SkuliOskarsson (Skuli Oskarsson)**
- Fixes some texts in README II - Fixes some texts in README II
**@yuyabee**
- Fixed README

View File

@ -1,6 +1,12 @@
##Changelog #Changelog
###Gin 0.4 (??) ###Gin 0.4 (Aug 21, 2014)
- [NEW] Development mode
- [NEW] Unit tests
- [NEW] Add Content.Redirect()
- [FIX] Deferring WriteHeader()
- [FIX] Improved documentation for model binding
###Gin 0.3 (Jul 18, 2014) ###Gin 0.3 (Jul 18, 2014)

View File

@ -1,6 +1,6 @@
#Gin Web Framework #Gin Web Framework
[![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.png)](https://godoc.org/github.com/gin-gonic/gin) [![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin)
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin)
Gin is a web framework written in Golang. It features a martini-like API with much better performance, up to 40 times faster. If you need performance and good productivity, you will love Gin. Gin is a web framework written in Golang. It features a martini-like API with much better performance, up to 40 times faster. If you need performance and good productivity, you will love Gin.
@ -324,12 +324,13 @@ func main() {
Issuing a HTTP redirect is easy: Issuing a HTTP redirect is easy:
```r.GET("/test", func(c *gin.Context) { ```go
r.GET("/test", func(c *gin.Context) {
c.Redirect(301, "http://www.google.com/") c.Redirect(301, "http://www.google.com/")
}) })
Both internal and external locations are supported.
``` ```
Both internal and external locations are supported.
#### Custom Middlewares #### Custom Middlewares
@ -425,7 +426,7 @@ func main() {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
// note than you are using the copied context "c_cp", IMPORTANT // note than you are using the copied context "c_cp", IMPORTANT
log.Println("Done! in path " + c_cp.Req.URL.Path) log.Println("Done! in path " + c_cp.Request.URL.Path)
}() }()
}) })
@ -435,7 +436,7 @@ func main() {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
// since we are NOT using a goroutine, we do not have to copy the context // since we are NOT using a goroutine, we do not have to copy the context
log.Println("Done! in path " + c.Req.URL.Path) log.Println("Done! in path " + c.Request.URL.Path)
}) })
// Listen and server on 0.0.0.0:8080 // Listen and server on 0.0.0.0:8080

View File

@ -65,6 +65,15 @@ type Context struct {
index int8 index int8
} }
/************************************/
/*********** TEST HELPERS ***********/
/************************************/
func CreateContext(w http.ResponseWriter, req *http.Request) *Context {
engine := New()
return engine.createContext(w, req, nil, nil)
}
/************************************/ /************************************/
/********** ROUTES GROUPING *********/ /********** ROUTES GROUPING *********/
/************************************/ /************************************/

View File

@ -436,3 +436,23 @@ func TestBindingJSONMalformed(t *testing.T) {
t.Errorf("Content-Type should not be application/json, was %s", w.HeaderMap.Get("Content-Type")) t.Errorf("Content-Type should not be application/json, was %s", w.HeaderMap.Get("Content-Type"))
} }
} }
func TestCreateContext(t *testing.T) {
req, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
c := CreateContext(w, req)
c.JSON(200, H{"foo": "bar"})
if w.Code != 200 {
t.Errorf("Response code should be Ok, was: %s", w.Code)
}
if w.HeaderMap.Get("Content-Type") != "application/json" {
t.Errorf("Content-Type should be application/json, was %s", w.HeaderMap.Get("Content-Type"))
}
if w.Body.String() != "{\"foo\":\"bar\"}\n" {
t.Errorf("Response should be {\"foo\":\"bar\"}, was: %s", w.Body.String())
}
}

20
gin.go
View File

@ -1,6 +1,7 @@
package gin package gin
import ( import (
"fmt"
"github.com/gin-gonic/gin/render" "github.com/gin-gonic/gin/render"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"html/template" "html/template"
@ -73,14 +74,22 @@ func Default() *Engine {
} }
func (engine *Engine) LoadHTMLGlob(pattern string) { func (engine *Engine) LoadHTMLGlob(pattern string) {
if gin_mode == debugCode {
engine.HTMLRender = render.HTMLDebug
} else {
templ := template.Must(template.ParseGlob(pattern)) templ := template.Must(template.ParseGlob(pattern))
engine.SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
}
func (engine *Engine) LoadHTMLFiles(files ...string) { func (engine *Engine) LoadHTMLFiles(files ...string) {
if gin_mode == debugCode {
engine.HTMLRender = render.HTMLDebug
} else {
templ := template.Must(template.ParseFiles(files...)) templ := template.Must(template.ParseFiles(files...))
engine.SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
}
func (engine *Engine) SetHTMLTemplate(templ *template.Template) { func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
engine.HTMLRender = render.HTMLRender{ engine.HTMLRender = render.HTMLRender{
@ -105,12 +114,18 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} }
func (engine *Engine) Run(addr string) { func (engine *Engine) Run(addr string) {
if gin_mode == debugCode {
fmt.Println("[GIN-debug] Listening and serving HTTP on " + addr)
}
if err := http.ListenAndServe(addr, engine); err != nil { if err := http.ListenAndServe(addr, engine); err != nil {
panic(err) panic(err)
} }
} }
func (engine *Engine) RunTLS(addr string, cert string, key string) { func (engine *Engine) RunTLS(addr string, cert string, key string) {
if gin_mode == debugCode {
fmt.Println("[GIN-debug] Listening and serving HTTPS on " + addr)
}
if err := http.ListenAndServeTLS(addr, cert, key, engine); err != nil { if err := http.ListenAndServeTLS(addr, cert, key, engine); err != nil {
panic(err) panic(err)
} }
@ -160,6 +175,11 @@ func (group *RouterGroup) pathFor(p string) string {
func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) { func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) {
p = group.pathFor(p) p = group.pathFor(p)
handlers = group.combineHandlers(handlers) handlers = group.combineHandlers(handlers)
if gin_mode == debugCode {
nuHandlers := len(handlers)
name := funcName(handlers[nuHandlers-1])
fmt.Printf("[GIN-debug] %-5s %-25s --> %s (%d handlers)\n", method, p, name, nuHandlers)
}
group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
c := group.engine.createContext(w, req, params, handlers) c := group.engine.createContext(w, req, params, handlers)
c.Next() c.Next()

View File

@ -10,6 +10,10 @@ import (
"testing" "testing"
) )
func init() {
SetMode(TestMode)
}
func PerformRequest(r http.Handler, method, path string) *httptest.ResponseRecorder { func PerformRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
req, _ := http.NewRequest(method, path, nil) req, _ := http.NewRequest(method, path, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()

42
mode.go Normal file
View File

@ -0,0 +1,42 @@
package gin
import (
"os"
)
const GIN_MODE = "GIN_MODE"
const (
DebugMode string = "debug"
ReleaseMode string = "release"
TestMode string = "test"
)
const (
debugCode = iota
releaseCode = iota
testCode = iota
)
var gin_mode int = debugCode
func SetMode(value string) {
switch value {
case DebugMode:
gin_mode = debugCode
case ReleaseMode:
gin_mode = releaseCode
case TestMode:
gin_mode = testCode
default:
panic("gin mode unknown, the allowed modes are: " + DebugMode + " and " + ReleaseMode)
}
}
func init() {
value := os.Getenv(GIN_MODE)
if len(value) == 0 {
SetMode(DebugMode)
} else {
SetMode(value)
}
}

View File

@ -25,6 +25,9 @@ type (
// Redirects // Redirects
redirectRender struct{} redirectRender struct{}
// Redirects
htmlDebugRender struct{}
// form binding // form binding
HTMLRender struct { HTMLRender struct {
Template *template.Template Template *template.Template
@ -36,6 +39,7 @@ var (
XML = xmlRender{} XML = xmlRender{}
Plain = plainRender{} Plain = plainRender{}
Redirect = redirectRender{} Redirect = redirectRender{}
HTMLDebug = htmlDebugRender{}
) )
func writeHeader(w http.ResponseWriter, code int, contentType string) { func writeHeader(w http.ResponseWriter, code int, contentType string) {
@ -61,13 +65,6 @@ func (_ xmlRender) Render(w http.ResponseWriter, code int, data ...interface{})
return encoder.Encode(data[0]) return encoder.Encode(data[0])
} }
func (html HTMLRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
writeHeader(w, code, "text/html")
file := data[0].(string)
obj := data[1]
return html.Template.ExecuteTemplate(w, file, obj)
}
func (_ plainRender) Render(w http.ResponseWriter, code int, data ...interface{}) error { func (_ plainRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
writeHeader(w, code, "text/plain") writeHeader(w, code, "text/plain")
format := data[0].(string) format := data[0].(string)
@ -80,3 +77,21 @@ func (_ plainRender) Render(w http.ResponseWriter, code int, data ...interface{}
} }
return err return err
} }
func (_ htmlDebugRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
writeHeader(w, code, "text/html")
file := data[0].(string)
obj := data[1]
t, err := template.ParseFiles(file)
if err != nil {
return err
}
return t.ExecuteTemplate(w, file, obj)
}
func (html HTMLRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
writeHeader(w, code, "text/html")
file := data[0].(string)
obj := data[1]
return html.Template.ExecuteTemplate(w, file, obj)
}

View File

@ -27,7 +27,7 @@ func (w *responseWriter) reset(writer http.ResponseWriter) {
} }
func (w *responseWriter) WriteHeader(code int) { func (w *responseWriter) WriteHeader(code int) {
if code != 0 { if code > 0 {
w.status = code w.status = code
if w.written { if w.written {
log.Println("[GIN] WARNING. Headers were already written!") log.Println("[GIN] WARNING. Headers were already written!")

View File

@ -2,6 +2,8 @@ package gin
import ( import (
"encoding/xml" "encoding/xml"
"reflect"
"runtime"
) )
type H map[string]interface{} type H map[string]interface{}
@ -38,3 +40,7 @@ func filterFlags(content string) string {
} }
return content return content
} }
func funcName(f interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
}