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
**Lead Developer:** Manu Martinez-Almeida (@manucorporat)
**Stuff:**
**Staff:**
Javier Provecho (@javierprovecho)
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
- ★ Added support for OPTIONS verb
- ★ Setting response headers before calling WriteHeader
- Improved documentation for model binding
- ★ Added Content.Redirect()
- ★ Added tons of Unit tests
**@austinheap (Austin Heap)**
- Adds travis CI integration
- Added travis CI integration
**@bluele (Jun Kimura)**
@ -67,20 +70,23 @@ People and companies, who have contributed, in alphabetical order.
**@mdigger (Dmitry Sedykh)**
- Fixes Form binding when content-type is x-www-form-urlencoded
- No repeat call c.Writer.Status() in gin.Logger
- Fixed Content-Type for json render
- Fixes Content-Type for json render
**@mopemope (Yutaka Matsubara)**
- ★ Adds Godep support (Dependencies Manager)
- Fix variadic parameter in the flexible render API
- Fix Corrupted plain render
- Fix variadic parameter in new flexible render API
**@msemenistyi (Mykyta Semenistyi)**
- update Readme.md. Add code to String method
**@msoedov (Sasha Myasoedov)**
- ★ Adds tons of unit tests.
**@ngerakines (Nick Gerakines)**
- ★ Improves API, c.GET() doesn't panic
- Adds MustGet() method
@ -95,4 +101,8 @@ People and companies, who have contributed, in alphabetical order.
**@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)

View File

@ -1,6 +1,6 @@
#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)
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:
```r.GET("/test", func(c *gin.Context) {
```go
r.GET("/test", func(c *gin.Context) {
c.Redirect(301, "http://www.google.com/")
})
Both internal and external locations are supported.
```
Both internal and external locations are supported.
#### Custom Middlewares
@ -425,7 +426,7 @@ func main() {
time.Sleep(5 * time.Second)
// 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)
// 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

View File

@ -65,6 +65,15 @@ type Context struct {
index int8
}
/************************************/
/*********** TEST HELPERS ***********/
/************************************/
func CreateContext(w http.ResponseWriter, req *http.Request) *Context {
engine := New()
return engine.createContext(w, req, nil, nil)
}
/************************************/
/********** 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"))
}
}
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())
}
}

28
gin.go
View File

@ -1,6 +1,7 @@
package gin
import (
"fmt"
"github.com/gin-gonic/gin/render"
"github.com/julienschmidt/httprouter"
"html/template"
@ -73,13 +74,21 @@ func Default() *Engine {
}
func (engine *Engine) LoadHTMLGlob(pattern string) {
templ := template.Must(template.ParseGlob(pattern))
engine.SetHTMLTemplate(templ)
if gin_mode == debugCode {
engine.HTMLRender = render.HTMLDebug
} else {
templ := template.Must(template.ParseGlob(pattern))
engine.SetHTMLTemplate(templ)
}
}
func (engine *Engine) LoadHTMLFiles(files ...string) {
templ := template.Must(template.ParseFiles(files...))
engine.SetHTMLTemplate(templ)
if gin_mode == debugCode {
engine.HTMLRender = render.HTMLDebug
} else {
templ := template.Must(template.ParseFiles(files...))
engine.SetHTMLTemplate(templ)
}
}
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
@ -105,12 +114,18 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
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 {
panic(err)
}
}
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 {
panic(err)
}
@ -160,6 +175,11 @@ func (group *RouterGroup) pathFor(p string) string {
func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) {
p = group.pathFor(p)
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) {
c := group.engine.createContext(w, req, params, handlers)
c.Next()

View File

@ -10,6 +10,10 @@ import (
"testing"
)
func init() {
SetMode(TestMode)
}
func PerformRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
req, _ := http.NewRequest(method, path, nil)
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
redirectRender struct{}
// Redirects
htmlDebugRender struct{}
// form binding
HTMLRender struct {
Template *template.Template
@ -32,10 +35,11 @@ type (
)
var (
JSON = jsonRender{}
XML = xmlRender{}
Plain = plainRender{}
Redirect = redirectRender{}
JSON = jsonRender{}
XML = xmlRender{}
Plain = plainRender{}
Redirect = redirectRender{}
HTMLDebug = htmlDebugRender{}
)
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])
}
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 {
writeHeader(w, code, "text/plain")
format := data[0].(string)
@ -80,3 +77,21 @@ func (_ plainRender) Render(w http.ResponseWriter, code int, data ...interface{}
}
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) {
if code != 0 {
if code > 0 {
w.status = code
if w.written {
log.Println("[GIN] WARNING. Headers were already written!")

View File

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