Browse Source

Splitting source code in different files

Manu Mtz-Almeida 11 years ago
parent
commit
dc43372610
3 changed files with 308 additions and 292 deletions
  1. 257 0
      context.go
  2. 0 292
      gin.go
  3. 51 0
      utils.go

+ 257 - 0
context.go

@@ -0,0 +1,257 @@
+package gin
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"github.com/gin-gonic/gin/binding"
+	"github.com/gin-gonic/gin/render"
+	"github.com/julienschmidt/httprouter"
+	"log"
+	"net/http"
+)
+
+const (
+	ErrorTypeInternal = 1 << iota
+	ErrorTypeExternal = 1 << iota
+	ErrorTypeAll      = 0xffffffff
+)
+
+// Used internally to collect errors that occurred during an http request.
+type errorMsg struct {
+	Err  string      `json:"error"`
+	Type uint32      `json:"-"`
+	Meta interface{} `json:"meta"`
+}
+
+type errorMsgs []errorMsg
+
+func (a errorMsgs) ByType(typ uint32) errorMsgs {
+	if len(a) == 0 {
+		return a
+	}
+	result := make(errorMsgs, 0, len(a))
+	for _, msg := range a {
+		if msg.Type&typ > 0 {
+			result = append(result, msg)
+		}
+	}
+	return result
+}
+
+func (a errorMsgs) String() string {
+	if len(a) == 0 {
+		return ""
+	}
+	var buffer bytes.Buffer
+	for i, msg := range a {
+		text := fmt.Sprintf("Error #%02d: %s \n     Meta: %v\n", (i + 1), msg.Err, msg.Meta)
+		buffer.WriteString(text)
+	}
+	return buffer.String()
+}
+
+// Context is the most important part of gin. It allows us to pass variables between middleware,
+// manage the flow, validate the JSON of a request and render a JSON response for example.
+type Context struct {
+	Request  *http.Request
+	Writer   ResponseWriter
+	Keys     map[string]interface{}
+	Errors   errorMsgs
+	Params   httprouter.Params
+	Engine   *Engine
+	handlers []HandlerFunc
+	index    int8
+}
+
+/************************************/
+/********** ROUTES GROUPING *********/
+/************************************/
+
+func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context {
+	c := engine.cache.Get().(*Context)
+	c.Writer.reset(w)
+	c.Request = req
+	c.Params = params
+	c.handlers = handlers
+	c.Keys = nil
+	c.index = -1
+	return c
+}
+
+/************************************/
+/****** FLOW AND ERROR MANAGEMENT****/
+/************************************/
+
+func (c *Context) Copy() *Context {
+	var cp Context = *c
+	cp.index = AbortIndex
+	cp.handlers = nil
+	return &cp
+}
+
+// Next should be used only in the middlewares.
+// It executes the pending handlers in the chain inside the calling handler.
+// See example in github.
+func (c *Context) Next() {
+	c.index++
+	s := int8(len(c.handlers))
+	for ; c.index < s; c.index++ {
+		c.handlers[c.index](c)
+	}
+}
+
+// Forces the system to do not continue calling the pending handlers.
+// For example, the first handler checks if the request is authorized. If it's not, context.Abort(401) should be called.
+// The rest of pending handlers would never be called for that request.
+func (c *Context) Abort(code int) {
+	if code >= 0 {
+		c.Writer.WriteHeader(code)
+	}
+	c.index = AbortIndex
+}
+
+// Fail is the same as Abort plus an error message.
+// Calling `context.Fail(500, err)` is equivalent to:
+// ```
+// context.Error("Operation aborted", err)
+// context.Abort(500)
+// ```
+func (c *Context) Fail(code int, err error) {
+	c.Error(err, "Operation aborted")
+	c.Abort(code)
+}
+
+func (c *Context) ErrorTyped(err error, typ uint32, meta interface{}) {
+	c.Errors = append(c.Errors, errorMsg{
+		Err:  err.Error(),
+		Type: typ,
+		Meta: meta,
+	})
+}
+
+// Attaches an error to the current context. The error is pushed to a list of errors.
+// It's a good idea to call Error for each error that occurred during the resolution of a request.
+// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
+func (c *Context) Error(err error, meta interface{}) {
+	c.ErrorTyped(err, ErrorTypeExternal, meta)
+}
+
+func (c *Context) LastError() error {
+	s := len(c.Errors)
+	if s > 0 {
+		return errors.New(c.Errors[s-1].Err)
+	} else {
+		return nil
+	}
+}
+
+/************************************/
+/******** METADATA MANAGEMENT********/
+/************************************/
+
+// Sets a new pair key/value just for the specified context.
+// It also lazy initializes the hashmap.
+func (c *Context) Set(key string, item interface{}) {
+	if c.Keys == nil {
+		c.Keys = make(map[string]interface{})
+	}
+	c.Keys[key] = item
+}
+
+// Get returns the value for the given key or an error if the key does not exist.
+func (c *Context) Get(key string) (interface{}, error) {
+	if c.Keys != nil {
+		item, ok := c.Keys[key]
+		if ok {
+			return item, nil
+		}
+	}
+	return nil, errors.New("Key does not exist.")
+}
+
+// MustGet returns the value for the given key or panics if the value doesn't exist.
+func (c *Context) MustGet(key string) interface{} {
+	value, err := c.Get(key)
+	if err != nil || value == nil {
+		log.Panicf("Key %s doesn't exist", key)
+	}
+	return value
+}
+
+/************************************/
+/******** ENCOGING MANAGEMENT********/
+/************************************/
+
+// This function checks the Content-Type to select a binding engine automatically,
+// Depending the "Content-Type" header different bindings are used:
+// "application/json" --> JSON binding
+// "application/xml"  --> XML binding
+// else --> returns an error
+// if Parses the request's body as JSON if Content-Type == "application/json"  using JSON or XML  as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
+func (c *Context) Bind(obj interface{}) bool {
+	var b binding.Binding
+	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
+	switch {
+	case c.Request.Method == "GET" || ctype == MIMEPOSTForm:
+		b = binding.Form
+	case ctype == MIMEJSON:
+		b = binding.JSON
+	case ctype == MIMEXML || ctype == MIMEXML2:
+		b = binding.XML
+	default:
+		c.Fail(400, errors.New("unknown content-type: "+ctype))
+		return false
+	}
+	return c.BindWith(obj, b)
+}
+
+func (c *Context) BindWith(obj interface{}, b binding.Binding) bool {
+	if err := b.Bind(c.Request, obj); err != nil {
+		c.Fail(400, err)
+		return false
+	}
+	return true
+}
+
+func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
+	if err := render.Render(c.Writer, code, obj...); err != nil {
+		c.ErrorTyped(err, ErrorTypeInternal, obj)
+		c.Abort(500)
+	}
+}
+
+// Serializes the given struct as JSON into the response body in a fast and efficient way.
+// It also sets the Content-Type as "application/json".
+func (c *Context) JSON(code int, obj interface{}) {
+	c.Render(code, render.JSON, obj)
+}
+
+// Serializes the given struct as XML into the response body in a fast and efficient way.
+// It also sets the Content-Type as "application/xml".
+func (c *Context) XML(code int, obj interface{}) {
+	c.Render(code, render.XML, obj)
+}
+
+// Renders the HTTP template specified by its file name.
+// It also updates the HTTP code and sets the Content-Type as "text/html".
+// See http://golang.org/doc/articles/wiki/
+func (c *Context) HTML(code int, name string, obj interface{}) {
+	c.Render(code, c.Engine.HTMLRender, name, obj)
+}
+
+// Writes the given string into the response body and sets the Content-Type to "text/plain".
+func (c *Context) String(code int, format string, values ...interface{}) {
+	c.Render(code, render.Plain, format, values)
+}
+
+// Writes some data into the body stream and updates the HTTP code.
+func (c *Context) Data(code int, contentType string, data []byte) {
+	if len(contentType) > 0 {
+		c.Writer.Header().Set("Content-Type", contentType)
+	}
+	if code >= 0 {
+		c.Writer.WriteHeader(code)
+	}
+	c.Writer.Write(data)
+}

+ 0 - 292
gin.go

@@ -1,15 +1,9 @@
 package gin
 
 import (
-	"bytes"
-	"encoding/xml"
-	"errors"
-	"fmt"
-	"github.com/gin-gonic/gin/binding"
 	"github.com/gin-gonic/gin/render"
 	"github.com/julienschmidt/httprouter"
 	"html/template"
-	"log"
 	"math"
 	"net/http"
 	"path"
@@ -26,39 +20,9 @@ const (
 	MIMEPOSTForm = "application/x-www-form-urlencoded"
 )
 
-const (
-	ErrorTypeInternal = 1 << iota
-	ErrorTypeExternal = 1 << iota
-	ErrorTypeAll      = 0xffffffff
-)
-
 type (
 	HandlerFunc func(*Context)
 
-	H map[string]interface{}
-
-	// Used internally to collect errors that occurred during an http request.
-	errorMsg struct {
-		Err  string      `json:"error"`
-		Type uint32      `json:"-"`
-		Meta interface{} `json:"meta"`
-	}
-
-	errorMsgs []errorMsg
-
-	// Context is the most important part of gin. It allows us to pass variables between middleware,
-	// manage the flow, validate the JSON of a request and render a JSON response for example.
-	Context struct {
-		Request  *http.Request
-		Writer   ResponseWriter
-		Keys     map[string]interface{}
-		Errors   errorMsgs
-		Params   httprouter.Params
-		Engine   *Engine
-		handlers []HandlerFunc
-		index    int8
-	}
-
 	// Used internally to configure router, a RouterGroup is associated with a prefix
 	// and an array of handlers (middlewares)
 	RouterGroup struct {
@@ -78,55 +42,6 @@ type (
 	}
 )
 
-// Allows type H to be used with xml.Marshal
-func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
-	start.Name = xml.Name{
-		Space: "",
-		Local: "map",
-	}
-	if err := e.EncodeToken(start); err != nil {
-		return err
-	}
-	for key, value := range h {
-		elem := xml.StartElement{
-			Name: xml.Name{Space: "", Local: key},
-			Attr: []xml.Attr{},
-		}
-		if err := e.EncodeElement(value, elem); err != nil {
-			return err
-		}
-	}
-	if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
-		return err
-	}
-	return nil
-}
-
-func (a errorMsgs) ByType(typ uint32) errorMsgs {
-	if len(a) == 0 {
-		return a
-	}
-	result := make(errorMsgs, 0, len(a))
-	for _, msg := range a {
-		if msg.Type&typ > 0 {
-			result = append(result, msg)
-		}
-	}
-	return result
-}
-
-func (a errorMsgs) String() string {
-	if len(a) == 0 {
-		return ""
-	}
-	var buffer bytes.Buffer
-	for i, msg := range a {
-		text := fmt.Sprintf("Error #%02d: %s \n     Meta: %v\n", (i + 1), msg.Err, msg.Meta)
-		buffer.WriteString(text)
-	}
-	return buffer.String()
-}
-
 // Returns a new blank Engine instance without any middleware attached.
 // The most basic configuration
 func New() *Engine {
@@ -200,32 +115,11 @@ func (engine *Engine) RunTLS(addr string, cert string, key string) {
 /********** ROUTES GROUPING *********/
 /************************************/
 
-func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context {
-	c := engine.cache.Get().(*Context)
-	c.Writer.reset(w)
-	c.Request = req
-	c.Params = params
-	c.handlers = handlers
-	c.Keys = nil
-	c.index = -1
-	return c
-}
-
 // Adds middlewares to the group, see example code in github.
 func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
 	group.Handlers = append(group.Handlers, middlewares...)
 }
 
-func joinGroupPath(elems ...string) string {
-	joined := path.Join(elems...)
-	lastComponent := elems[len(elems)-1]
-	// Append a '/' if the last component had one, but only if it's not there already
-	if len(lastComponent) > 0 && lastComponent[len(lastComponent)-1] == '/' && joined[len(joined)-1] != '/' {
-		return joined + "/"
-	}
-	return joined
-}
-
 // Creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
 // For example, all the routes that use a common middlware for authorization could be grouped.
 func (group *RouterGroup) Group(component string, handlers ...HandlerFunc) *RouterGroup {
@@ -318,189 +212,3 @@ func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc
 	h = append(h, handlers...)
 	return h
 }
-
-/************************************/
-/****** FLOW AND ERROR MANAGEMENT****/
-/************************************/
-
-func (c *Context) Copy() *Context {
-	var cp Context = *c
-	cp.index = AbortIndex
-	cp.handlers = nil
-	return &cp
-}
-
-// Next should be used only in the middlewares.
-// It executes the pending handlers in the chain inside the calling handler.
-// See example in github.
-func (c *Context) Next() {
-	c.index++
-	s := int8(len(c.handlers))
-	for ; c.index < s; c.index++ {
-		c.handlers[c.index](c)
-	}
-}
-
-// Forces the system to do not continue calling the pending handlers.
-// For example, the first handler checks if the request is authorized. If it's not, context.Abort(401) should be called.
-// The rest of pending handlers would never be called for that request.
-func (c *Context) Abort(code int) {
-	if code >= 0 {
-		c.Writer.WriteHeader(code)
-	}
-	c.index = AbortIndex
-}
-
-// Fail is the same as Abort plus an error message.
-// Calling `context.Fail(500, err)` is equivalent to:
-// ```
-// context.Error("Operation aborted", err)
-// context.Abort(500)
-// ```
-func (c *Context) Fail(code int, err error) {
-	c.Error(err, "Operation aborted")
-	c.Abort(code)
-}
-
-func (c *Context) ErrorTyped(err error, typ uint32, meta interface{}) {
-	c.Errors = append(c.Errors, errorMsg{
-		Err:  err.Error(),
-		Type: typ,
-		Meta: meta,
-	})
-}
-
-// Attaches an error to the current context. The error is pushed to a list of errors.
-// It's a good idea to call Error for each error that occurred during the resolution of a request.
-// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
-func (c *Context) Error(err error, meta interface{}) {
-	c.ErrorTyped(err, ErrorTypeExternal, meta)
-}
-
-func (c *Context) LastError() error {
-	s := len(c.Errors)
-	if s > 0 {
-		return errors.New(c.Errors[s-1].Err)
-	} else {
-		return nil
-	}
-}
-
-/************************************/
-/******** METADATA MANAGEMENT********/
-/************************************/
-
-// Sets a new pair key/value just for the specified context.
-// It also lazy initializes the hashmap.
-func (c *Context) Set(key string, item interface{}) {
-	if c.Keys == nil {
-		c.Keys = make(map[string]interface{})
-	}
-	c.Keys[key] = item
-}
-
-// Get returns the value for the given key or an error if the key does not exist.
-func (c *Context) Get(key string) (interface{}, error) {
-	if c.Keys != nil {
-		item, ok := c.Keys[key]
-		if ok {
-			return item, nil
-		}
-	}
-	return nil, errors.New("Key does not exist.")
-}
-
-// MustGet returns the value for the given key or panics if the value doesn't exist.
-func (c *Context) MustGet(key string) interface{} {
-	value, err := c.Get(key)
-	if err != nil || value == nil {
-		log.Panicf("Key %s doesn't exist", key)
-	}
-	return value
-}
-
-/************************************/
-/******** ENCOGING MANAGEMENT********/
-/************************************/
-
-func filterFlags(content string) string {
-	for i, a := range content {
-		if a == ' ' || a == ';' {
-			return content[:i]
-		}
-	}
-	return content
-}
-
-// This function checks the Content-Type to select a binding engine automatically,
-// Depending the "Content-Type" header different bindings are used:
-// "application/json" --> JSON binding
-// "application/xml"  --> XML binding
-// else --> returns an error
-// if Parses the request's body as JSON if Content-Type == "application/json"  using JSON or XML  as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
-func (c *Context) Bind(obj interface{}) bool {
-	var b binding.Binding
-	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
-	switch {
-	case c.Request.Method == "GET" || ctype == MIMEPOSTForm:
-		b = binding.Form
-	case ctype == MIMEJSON:
-		b = binding.JSON
-	case ctype == MIMEXML || ctype == MIMEXML2:
-		b = binding.XML
-	default:
-		c.Fail(400, errors.New("unknown content-type: "+ctype))
-		return false
-	}
-	return c.BindWith(obj, b)
-}
-
-func (c *Context) BindWith(obj interface{}, b binding.Binding) bool {
-	if err := b.Bind(c.Request, obj); err != nil {
-		c.Fail(400, err)
-		return false
-	}
-	return true
-}
-
-func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
-	if err := render.Render(c.Writer, code, obj...); err != nil {
-		c.ErrorTyped(err, ErrorTypeInternal, obj)
-		c.Abort(500)
-	}
-}
-
-// Serializes the given struct as JSON into the response body in a fast and efficient way.
-// It also sets the Content-Type as "application/json".
-func (c *Context) JSON(code int, obj interface{}) {
-	c.Render(code, render.JSON, obj)
-}
-
-// Serializes the given struct as XML into the response body in a fast and efficient way.
-// It also sets the Content-Type as "application/xml".
-func (c *Context) XML(code int, obj interface{}) {
-	c.Render(code, render.XML, obj)
-}
-
-// Renders the HTTP template specified by its file name.
-// It also updates the HTTP code and sets the Content-Type as "text/html".
-// See http://golang.org/doc/articles/wiki/
-func (c *Context) HTML(code int, name string, obj interface{}) {
-	c.Render(code, c.Engine.HTMLRender, name, obj)
-}
-
-// Writes the given string into the response body and sets the Content-Type to "text/plain".
-func (c *Context) String(code int, format string, values ...interface{}) {
-	c.Render(code, render.Plain, format, values)
-}
-
-// Writes some data into the body stream and updates the HTTP code.
-func (c *Context) Data(code int, contentType string, data []byte) {
-	if len(contentType) > 0 {
-		c.Writer.Header().Set("Content-Type", contentType)
-	}
-	if code >= 0 {
-		c.Writer.WriteHeader(code)
-	}
-	c.Writer.Write(data)
-}

+ 51 - 0
utils.go

@@ -0,0 +1,51 @@
+package gin
+
+import (
+	"encoding/xml"
+	"path"
+)
+
+type H map[string]interface{}
+
+// Allows type H to be used with xml.Marshal
+func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	start.Name = xml.Name{
+		Space: "",
+		Local: "map",
+	}
+	if err := e.EncodeToken(start); err != nil {
+		return err
+	}
+	for key, value := range h {
+		elem := xml.StartElement{
+			Name: xml.Name{Space: "", Local: key},
+			Attr: []xml.Attr{},
+		}
+		if err := e.EncodeElement(value, elem); err != nil {
+			return err
+		}
+	}
+	if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
+		return err
+	}
+	return nil
+}
+
+func joinGroupPath(elems ...string) string {
+	joined := path.Join(elems...)
+	lastComponent := elems[len(elems)-1]
+	// Append a '/' if the last component had one, but only if it's not there already
+	if len(lastComponent) > 0 && lastComponent[len(lastComponent)-1] == '/' && joined[len(joined)-1] != '/' {
+		return joined + "/"
+	}
+	return joined
+}
+
+func filterFlags(content string) string {
+	for i, a := range content {
+		if a == ' ' || a == ';' {
+			return content[:i]
+		}
+	}
+	return content
+}