Explorar el Código

v1.1 (#751)

* Implement QueryArray and PostArray methods

* Refactor GetQuery and GetPostForm

* Removed additional Iota

I think assigning iota to each constant is not required

* Add 1.7 test.

* Add codecov.io

* corrected a typo in README

* remove coveralls services.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* Update TravisCI to Gitter webhook

* Add codecov.yml Gitter webhook

* Changed imports to gopkg instead of github in README (#733)

* Add contribution guide

* Update go get for stable version

In the future, github default branch will be develop so running `go get github.com/gin-gonic/gin` will pull latest code from develop.

* Changed imports to gopkg instead of github in README

* Update README.md

* Logger: skip ANSI color commands if output is not a tty
Javier Provecho Fernandez hace 9 años
padre
commit
32cab500ec
Se han modificado 7 ficheros con 104 adiciones y 27 borrados
  1. 2 4
      .travis.yml
  2. 12 14
      README.md
  3. 5 0
      codecov.yml
  4. 38 5
      context.go
  5. 32 0
      context_test.go
  6. 13 2
      logger.go
  7. 2 2
      mode.go

+ 2 - 4
.travis.yml

@@ -8,17 +8,15 @@ go:
   - tip
 
 script:
-  - go get golang.org/x/tools/cmd/cover
-  - go get github.com/mattn/goveralls
   - go test -v -covermode=count -coverprofile=coverage.out
 
 after_success:
-  - goveralls -coverprofile=coverage.out -service=travis-ci -repotoken yFj7FrCeddvBzUaaCyG33jCLfWXeb93eA
+  - bash <(curl -s https://codecov.io/bash)
 
 notifications:
   webhooks:
     urls:
-      - https://webhooks.gitter.im/e/acc2c57482e94b44f557
+      - https://webhooks.gitter.im/e/7f95bf605c4d356372f4
     on_success: change  # options: [always|never|change] default: always
     on_failure: always  # options: [always|never|change] default: always
     on_start: false     # default: false

+ 12 - 14
README.md

@@ -1,16 +1,15 @@
 
 #Gin Web Framework
+
 <img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
 [![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin)
-[![Coverage Status](https://coveralls.io/repos/gin-gonic/gin/badge.svg?branch=master)](https://coveralls.io/r/gin-gonic/gin?branch=master)
+[![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin)
 [![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin)
 [![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin)
 [![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 
 Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
 
-
-
 ![Gin console logger](https://gin-gonic.github.io/gin/other/console.png)
 
 ```sh
@@ -19,7 +18,7 @@ $ cat test.go
 ```go
 package main
 
-import "github.com/gin-gonic/gin"
+import "gopkg.in/gin-gonic/gin.v1"
 
 func main() {
 	r := gin.Default()
@@ -28,7 +27,7 @@ func main() {
 			"message": "pong",
 		})
 	})
-	r.Run() // listen and server on 0.0.0.0:8080
+	r.Run() // listen and serve on 0.0.0.0:8080
 }
 ```
 
@@ -317,7 +316,7 @@ func main() {
 		testing.GET("/analytics", analyticsEndpoint)
 	}
 
-	// Listen and server on 0.0.0.0:8080
+	// Listen and serve on 0.0.0.0:8080
 	r.Run(":8080")
 }
 ```
@@ -367,7 +366,7 @@ func main() {
 		}
 	})
 
-	// Listen and server on 0.0.0.0:8080
+	// Listen and serve on 0.0.0.0:8080
 	router.Run(":8080")
 }
 ```
@@ -378,8 +377,7 @@ func main() {
 package main
 
 import (
-	"github.com/gin-gonic/gin"
-	"github.com/gin-gonic/gin/binding"
+	"gopkg.in/gin-gonic/gin.v1"
 )
 
 type LoginForm struct {
@@ -447,7 +445,7 @@ func main() {
 		c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
 	})
 
-	// Listen and server on 0.0.0.0:8080
+	// Listen and serve on 0.0.0.0:8080
 	r.Run(":8080")
 }
 ```
@@ -461,7 +459,7 @@ func main() {
 	router.StaticFS("/more_static", http.Dir("my_file_system"))
 	router.StaticFile("/favicon.ico", "./resources/favicon.ico")
 
-	// Listen and server on 0.0.0.0:8080
+	// Listen and serve on 0.0.0.0:8080
 	router.Run(":8080")
 }
 ```
@@ -593,7 +591,7 @@ func main() {
 		log.Println(example)
 	})
 
-	// Listen and server on 0.0.0.0:8080
+	// Listen and serve on 0.0.0.0:8080
 	r.Run(":8080")
 }
 ```
@@ -631,7 +629,7 @@ func main() {
 		}
 	})
 
-	// Listen and server on 0.0.0.0:8080
+	// Listen and serve on 0.0.0.0:8080
 	r.Run(":8080")
 }
 ```
@@ -664,7 +662,7 @@ func main() {
 		log.Println("Done! in path " + c.Request.URL.Path)
 	})
 
-	// Listen and server on 0.0.0.0:8080
+	// Listen and serve on 0.0.0.0:8080
 	r.Run(":8080")
 }
 ```

+ 5 - 0
codecov.yml

@@ -0,0 +1,5 @@
+coverage:
+  notify:
+    gitter:
+      default:
+        url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165

+ 38 - 5
context.go

@@ -230,11 +230,27 @@ func (c *Context) DefaultQuery(key, defaultValue string) string {
 // 		("", false) == c.GetQuery("id")
 // 		("", true) == c.GetQuery("lastname")
 func (c *Context) GetQuery(key string) (string, bool) {
+	if values, ok := c.GetQueryArray(key); ok {
+		return values[0], ok
+	}
+	return "", false
+}
+
+// QueryArray returns a slice of strings for a given query key.
+// The length of the slice depends on the number of params with the given key.
+func (c *Context) QueryArray(key string) []string {
+	values, _ := c.GetQueryArray(key)
+	return values
+}
+
+// GetQueryArray returns a slice of strings for a given query key, plus
+// a boolean value whether at least one value exists for the given key.
+func (c *Context) GetQueryArray(key string) ([]string, bool) {
 	req := c.Request
 	if values, ok := req.URL.Query()[key]; ok && len(values) > 0 {
-		return values[0], true
+		return values, true
 	}
-	return "", false
+	return []string{}, false
 }
 
 // PostForm returns the specified key from a POST urlencoded form or multipart form
@@ -262,17 +278,34 @@ func (c *Context) DefaultPostForm(key, defaultValue string) string {
 // 		email=  			  	-->  ("", true) := GetPostForm("email") // set email to ""
 //							 	-->  ("", false) := GetPostForm("email") // do nothing with email
 func (c *Context) GetPostForm(key string) (string, bool) {
+	if values, ok := c.GetPostFormArray(key); ok {
+		return values[0], ok
+	}
+	return "", false
+}
+
+// PostFormArray returns a slice of strings for a given form key.
+// The length of the slice depends on the number of params with the given key.
+func (c *Context) PostFormArray(key string) []string {
+	values, _ := c.GetPostFormArray(key)
+	return values
+}
+
+// GetPostFormArray returns a slice of strings for a given form key, plus
+// a boolean value whether at least one value exists for the given key.
+func (c *Context) GetPostFormArray(key string) ([]string, bool) {
 	req := c.Request
+	req.ParseForm()
 	req.ParseMultipartForm(32 << 20) // 32 MB
 	if values := req.PostForm[key]; len(values) > 0 {
-		return values[0], true
+		return values, true
 	}
 	if req.MultipartForm != nil && req.MultipartForm.File != nil {
 		if values := req.MultipartForm.Value[key]; len(values) > 0 {
-			return values[0], true
+			return values, true
 		}
 	}
-	return "", false
+	return []string{}, false
 }
 
 // Bind checks the Content-Type to select a binding engine automatically,

+ 32 - 0
context_test.go

@@ -251,6 +251,22 @@ func TestContextQueryAndPostForm(t *testing.T) {
 	assert.Equal(t, obj.Page, 11)
 	assert.Equal(t, obj.Both, "")
 	assert.Equal(t, obj.Array, []string{"first", "second"})
+
+	values, ok := c.GetQueryArray("array[]")
+	assert.True(t, ok)
+	assert.Equal(t, "first", values[0])
+	assert.Equal(t, "second", values[1])
+
+	values = c.QueryArray("array[]")
+	assert.Equal(t, "first", values[0])
+	assert.Equal(t, "second", values[1])
+
+	values = c.QueryArray("nokey")
+	assert.Equal(t, 0, len(values))
+
+	values = c.QueryArray("both")
+	assert.Equal(t, 1, len(values))
+	assert.Equal(t, "GET", values[0])
 }
 
 func TestContextPostFormMultipart(t *testing.T) {
@@ -299,6 +315,22 @@ func TestContextPostFormMultipart(t *testing.T) {
 	assert.False(t, ok)
 	assert.Empty(t, value)
 	assert.Equal(t, c.DefaultPostForm("nokey", "nothing"), "nothing")
+
+	values, ok := c.GetPostFormArray("array")
+	assert.True(t, ok)
+	assert.Equal(t, "first", values[0])
+	assert.Equal(t, "second", values[1])
+
+	values = c.PostFormArray("array")
+	assert.Equal(t, "first", values[0])
+	assert.Equal(t, "second", values[1])
+
+	values = c.PostFormArray("nokey")
+	assert.Equal(t, 0, len(values))
+
+	values = c.PostFormArray("foo")
+	assert.Equal(t, 1, len(values))
+	assert.Equal(t, "bar", values[0])
 }
 
 func TestContextSetCookie(t *testing.T) {

+ 13 - 2
logger.go

@@ -7,7 +7,10 @@ package gin
 import (
 	"fmt"
 	"io"
+	"os"
 	"time"
+
+	"golang.org/x/crypto/ssh/terminal"
 )
 
 var (
@@ -44,6 +47,11 @@ func Logger() HandlerFunc {
 // LoggerWithWriter instance a Logger middleware with the specified writter buffer.
 // Example: os.Stdout, a file opened in write mode, a socket...
 func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
+	isTerm := true
+	if outFile, ok := out.(*os.File); ok {
+		isTerm = terminal.IsTerminal(int(outFile.Fd()))
+	}
+
 	var skip map[string]struct{}
 
 	if length := len(notlogged); length > 0 {
@@ -71,8 +79,11 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
 			clientIP := c.ClientIP()
 			method := c.Request.Method
 			statusCode := c.Writer.Status()
-			statusColor := colorForStatus(statusCode)
-			methodColor := colorForMethod(method)
+			var statusColor, methodColor string
+			if isTerm {
+				statusColor = colorForStatus(statusCode)
+				methodColor = colorForMethod(method)
+			}
 			comment := c.Errors.ByType(ErrorTypePrivate).String()
 
 			fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s  %s %-7s %s\n%s",

+ 2 - 2
mode.go

@@ -20,8 +20,8 @@ const (
 )
 const (
 	debugCode   = iota
-	releaseCode = iota
-	testCode    = iota
+	releaseCode 
+	testCode  
 )
 
 // DefaultWriter is the default io.Writer used the Gin for debug output and