diff --git a/AUTHORS.md b/AUTHORS.md index 67535a41..c09e263f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -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 \ No newline at end of file +- Fixes some texts in README II + + +**@yuyabee** +- Fixed README \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c399078..5ec8c5ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/README.md b/README.md index a96eaeda..c97164d4 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/context.go b/context.go index 42943f2e..7f2cf919 100644 --- a/context.go +++ b/context.go @@ -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 *********/ /************************************/ diff --git a/context_test.go b/context_test.go index 3b3302e8..bf193303 100644 --- a/context_test.go +++ b/context_test.go @@ -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()) + } +} diff --git a/gin.go b/gin.go index e30e38e9..f8d35fcf 100644 --- a/gin.go +++ b/gin.go @@ -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() diff --git a/gin_test.go b/gin_test.go index 0cd5bf1b..7425cc21 100644 --- a/gin_test.go +++ b/gin_test.go @@ -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() diff --git a/mode.go b/mode.go new file mode 100644 index 00000000..85f133b9 --- /dev/null +++ b/mode.go @@ -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) + } +} diff --git a/render/render.go b/render/render.go index 207e7a52..bc982a30 100644 --- a/render/render.go +++ b/render/render.go @@ -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) +} diff --git a/response_writer.go b/response_writer.go index 2da8e336..91afe89b 100644 --- a/response_writer.go +++ b/response_writer.go @@ -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!") diff --git a/utils.go b/utils.go index 90cca1be..6417efd9 100644 --- a/utils.go +++ b/utils.go @@ -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() +}