release v1.20

This commit is contained in:
deepzz0
2017-06-14 21:25:18 +08:00
parent 1634418a13
commit 54f5289d6b
134 changed files with 26141 additions and 18531 deletions

View File

@@ -19,34 +19,34 @@ Available on [godoc.org](http://www.godoc.org/gopkg.in/boj/redistore.v1).
See http://www.gorillatoolkit.org/pkg/sessions for full documentation on underlying interface.
### Example
``` go
// Fetch new store.
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {
panic(err)
}
defer store.Close()
// Fetch new store.
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {
panic(err)
}
defer store.Close()
// Get a session.
session, err = store.Get(req, "session-key")
if err != nil {
log.Error(err.Error())
}
// Get a session.
session, err = store.Get(req, "session-key")
if err != nil {
log.Error(err.Error())
}
// Add a value.
session.Values["foo"] = "bar"
// Add a value.
session.Values["foo"] = "bar"
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Save.
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Delete session.
session.Options.MaxAge = -1
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Change session storage configuration for MaxAge = 10 days.
store.SetMaxAge(10*24*3600)
// Delete session.
session.Options.MaxAge = -1
if err = sessions.Save(req, rsp); err != nil {
t.Fatalf("Error saving session: %v", err)
}
// Change session storage configuration for MaxAge = 10 days.
store.SetMaxAge(10 * 24 * 3600)
```

View File

@@ -181,7 +181,7 @@ func (p *Pool) Get() Conn {
return &pooledConnection{p: p, c: c}
}
// ActiveCount returns the number of active connections in the pool.
// ActiveCount returns the number of connections in the pool. The count includes idle connections and connections in use.
func (p *Pool) ActiveCount() int {
p.mu.Lock()
active := p.active
@@ -189,6 +189,14 @@ func (p *Pool) ActiveCount() int {
return active
}
// IdleCount returns the number of idle connections in the pool.
func (p *Pool) IdleCount() int {
p.mu.Lock()
idle := p.idle.Len()
p.mu.Unlock()
return idle
}
// Close releases the resources used by the pool.
func (p *Pool) Close() error {
p.mu.Lock()

View File

@@ -83,7 +83,7 @@ func (d *poolDialer) dial() (redis.Conn, error) {
return &poolTestConn{d: d, Conn: c}, nil
}
func (d *poolDialer) check(message string, p *redis.Pool, dialed, open int) {
func (d *poolDialer) check(message string, p *redis.Pool, dialed, open, inuse int) {
d.mu.Lock()
if d.dialed != dialed {
d.t.Errorf("%s: dialed=%d, want %d", message, d.dialed, dialed)
@@ -91,9 +91,13 @@ func (d *poolDialer) check(message string, p *redis.Pool, dialed, open int) {
if d.open != open {
d.t.Errorf("%s: open=%d, want %d", message, d.open, open)
}
if active := p.ActiveCount(); active != open {
d.t.Errorf("%s: active=%d, want %d", message, active, open)
}
if idle := p.IdleCount(); idle != open-inuse {
d.t.Errorf("%s: idle=%d, want %d", message, idle, open-inuse)
}
d.mu.Unlock()
}
@@ -113,9 +117,9 @@ func TestPoolReuse(t *testing.T) {
c2.Close()
}
d.check("before close", p, 2, 2)
d.check("before close", p, 2, 2, 0)
p.Close()
d.check("after close", p, 2, 0)
d.check("after close", p, 2, 0, 0)
}
func TestPoolMaxIdle(t *testing.T) {
@@ -137,9 +141,9 @@ func TestPoolMaxIdle(t *testing.T) {
c2.Close()
c3.Close()
}
d.check("before close", p, 12, 2)
d.check("before close", p, 12, 2, 0)
p.Close()
d.check("after close", p, 12, 0)
d.check("after close", p, 12, 0, 0)
}
func TestPoolError(t *testing.T) {
@@ -161,7 +165,7 @@ func TestPoolError(t *testing.T) {
c.Do("ERR", io.EOF)
c.Close()
d.check(".", p, 2, 0)
d.check(".", p, 2, 0, 0)
}
func TestPoolClose(t *testing.T) {
@@ -189,7 +193,7 @@ func TestPoolClose(t *testing.T) {
p.Close()
d.check("after pool close", p, 3, 1)
d.check("after pool close", p, 3, 1, 1)
if _, err := c1.Do("PING"); err == nil {
t.Errorf("expected error after connection and pool closed")
@@ -197,7 +201,7 @@ func TestPoolClose(t *testing.T) {
c3.Close()
d.check("after conn close", p, 3, 0)
d.check("after conn close", p, 3, 0, 0)
c1 = p.Get()
if _, err := c1.Do("PING"); err == nil {
@@ -222,7 +226,7 @@ func TestPoolTimeout(t *testing.T) {
c.Do("PING")
c.Close()
d.check("1", p, 1, 1)
d.check("1", p, 1, 1, 0)
now = now.Add(p.IdleTimeout)
@@ -230,7 +234,7 @@ func TestPoolTimeout(t *testing.T) {
c.Do("PING")
c.Close()
d.check("2", p, 2, 1)
d.check("2", p, 2, 1, 0)
}
func TestPoolConcurrenSendReceive(t *testing.T) {
@@ -272,7 +276,7 @@ func TestPoolBorrowCheck(t *testing.T) {
c.Do("PING")
c.Close()
}
d.check("1", p, 10, 1)
d.check("1", p, 10, 1, 0)
}
func TestPoolMaxActive(t *testing.T) {
@@ -289,7 +293,7 @@ func TestPoolMaxActive(t *testing.T) {
c2 := p.Get()
c2.Do("PING")
d.check("1", p, 2, 2)
d.check("1", p, 2, 2, 2)
c3 := p.Get()
if _, err := c3.Do("PING"); err != redis.ErrPoolExhausted {
@@ -297,9 +301,9 @@ func TestPoolMaxActive(t *testing.T) {
}
c3.Close()
d.check("2", p, 2, 2)
d.check("2", p, 2, 2, 2)
c2.Close()
d.check("3", p, 2, 2)
d.check("3", p, 2, 2, 1)
c3 = p.Get()
if _, err := c3.Do("PING"); err != nil {
@@ -307,7 +311,7 @@ func TestPoolMaxActive(t *testing.T) {
}
c3.Close()
d.check("4", p, 2, 2)
d.check("4", p, 2, 2, 1)
}
func TestPoolMonitorCleanup(t *testing.T) {
@@ -323,7 +327,7 @@ func TestPoolMonitorCleanup(t *testing.T) {
c.Send("MONITOR")
c.Close()
d.check("", p, 1, 0)
d.check("", p, 1, 0, 0)
}
func TestPoolPubSubCleanup(t *testing.T) {
@@ -456,7 +460,7 @@ func TestWaitPool(t *testing.T) {
c := p.Get()
errs := startGoroutines(p, "PING")
d.check("before close", p, 1, 1)
d.check("before close", p, 1, 1, 1)
c.Close()
timeout := time.After(2 * time.Second)
for i := 0; i < cap(errs); i++ {
@@ -469,7 +473,7 @@ func TestWaitPool(t *testing.T) {
t.Fatalf("timeout waiting for blocked goroutine %d", i)
}
}
d.check("done", p, 1, 1)
d.check("done", p, 1, 1, 0)
}
func TestWaitPoolClose(t *testing.T) {
@@ -487,7 +491,7 @@ func TestWaitPoolClose(t *testing.T) {
t.Fatal(err)
}
errs := startGoroutines(p, "PING")
d.check("before close", p, 1, 1)
d.check("before close", p, 1, 1, 1)
p.Close()
timeout := time.After(2 * time.Second)
for i := 0; i < cap(errs); i++ {
@@ -504,7 +508,7 @@ func TestWaitPoolClose(t *testing.T) {
}
}
c.Close()
d.check("done", p, 1, 0)
d.check("done", p, 1, 0, 0)
}
func TestWaitPoolCommandError(t *testing.T) {
@@ -520,7 +524,7 @@ func TestWaitPoolCommandError(t *testing.T) {
c := p.Get()
errs := startGoroutines(p, "ERR", testErr)
d.check("before close", p, 1, 1)
d.check("before close", p, 1, 1, 1)
c.Close()
timeout := time.After(2 * time.Second)
for i := 0; i < cap(errs); i++ {
@@ -533,7 +537,7 @@ func TestWaitPoolCommandError(t *testing.T) {
t.Fatalf("timeout waiting for blocked goroutine %d", i)
}
}
d.check("done", p, cap(errs), 0)
d.check("done", p, cap(errs), 0, 0)
}
func TestWaitPoolDialError(t *testing.T) {
@@ -549,7 +553,7 @@ func TestWaitPoolDialError(t *testing.T) {
c := p.Get()
errs := startGoroutines(p, "ERR", testErr)
d.check("before close", p, 1, 1)
d.check("before close", p, 1, 1, 1)
d.dialErr = errors.New("dial")
c.Close()
@@ -578,7 +582,7 @@ func TestWaitPoolDialError(t *testing.T) {
if errCount != cap(errs)-1 {
t.Errorf("expected %d dial errors, got %d", cap(errs)-1, errCount)
}
d.check("done", p, cap(errs), 0)
d.check("done", p, cap(errs), 0, 0)
}
// Borrowing requires us to iterate over the idle connections, unlock the pool,

View File

@@ -391,3 +391,35 @@ func Int64Map(result interface{}, err error) (map[string]int64, error) {
}
return m, nil
}
// Positions is a helper that converts an array of positions (lat, long)
// into a [][2]float64. The GEOPOS command returns replies in this format.
func Positions(result interface{}, err error) ([]*[2]float64, error) {
values, err := Values(result, err)
if err != nil {
return nil, err
}
positions := make([]*[2]float64, len(values))
for i := range values {
if values[i] == nil {
continue
}
p, ok := values[i].([]interface{})
if !ok {
return nil, fmt.Errorf("redigo: unexpected element type for interface slice, got type %T", values[i])
}
if len(p) != 2 {
return nil, fmt.Errorf("redigo: unexpected number of values for a member position, got %d", len(p))
}
lat, err := Float64(p[0], nil)
if err != nil {
return nil, err
}
long, err := Float64(p[1], nil)
if err != nil {
return nil, err
}
positions[i] = &[2]float64{lat, long}
}
return positions, nil
}

View File

@@ -96,6 +96,11 @@ var replyTests = []struct {
ve(redis.Uint64(int64(-1), nil)),
ve(uint64(0), redis.ErrNegativeInt),
},
{
"positions([[1, 2], nil, [3, 4]])",
ve(redis.Positions([]interface{}{[]interface{}{[]byte("1"), []byte("2")}, nil, []interface{}{[]byte("3"), []byte("4")}}, nil)),
ve([]*[2]float64{{1.0, 2.0}, nil, {3.0, 4.0}}, nil),
},
}
func TestReply(t *testing.T) {

View File

@@ -186,7 +186,11 @@ func convertAssign(d interface{}, s interface{}) (err error) {
case string:
switch d := d.(type) {
case *string:
*d = string(s)
*d = s
case *interface{}:
*d = s
case nil:
// skip value
default:
err = cannotConvert(reflect.ValueOf(d), s)
}

View File

@@ -55,6 +55,11 @@ func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} {
return args
}
// Hash returns the script hash.
func (s *Script) Hash() string {
return s.hash
}
// Do evaluates the script. Under the covers, Do optimistically evaluates the
// script using the EVALSHA command. If the command fails because the script is
// not loaded, then Do evaluates the script using the EVAL command (thus

View File

@@ -16,6 +16,7 @@ package redis_test
import (
"fmt"
"github.com/garyburd/redigo/redis"
)

View File

@@ -1,9 +1,8 @@
language: go
go:
- 1.4.3
- 1.5.4
- 1.6.4
- 1.7.4
- 1.6.x
- 1.7.x
- 1.8.x
- tip
services:

View File

@@ -29,8 +29,7 @@ Let's start with an example that shows the sessions API in a nutshell:
var store = sessions.NewCookieStore([]byte("something-very-secret"))
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Get a session. We're ignoring the error resulted from decoding an
// existing session: Get() always returns a session, even if empty.
// Get a session. Get() always returns a session, even if empty.
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@@ -1,4 +1,4 @@
package main
package isatty_test
import (
"fmt"
@@ -7,7 +7,7 @@ import (
"github.com/mattn/go-isatty"
)
func main() {
func Example() {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println("Is Terminal")
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {

View File

@@ -1,11 +1,11 @@
sudo: false
language: go
go:
- 1.7
- tip
- 1.x
- master
matrix:
allow_failures:
- go: tip
- go: master
fast_finish: true
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).

View File

@@ -1,3 +1,5 @@
MIT License
Copyright (c) 2015 Dmitri Shuralyov
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -7,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -5,9 +5,11 @@ sanitized_anchor_name
Package sanitized_anchor_name provides a func to create sanitized anchor names.
Its logic can be reused by multiple packages to create interoperable anchor names and links to those anchors.
Its logic can be reused by multiple packages to create interoperable anchor names
and links to those anchors.
At this time, it does not try to ensure that generated anchor names are unique, that responsibility falls on the caller.
At this time, it does not try to ensure that generated anchor names
are unique, that responsibility falls on the caller.
Installation
------------