├── .gitignore ├── vendor └── github.com │ ├── robfig │ └── cron │ │ ├── .travis.yml │ │ ├── README.md │ │ ├── .gitignore │ │ ├── constantdelay.go │ │ ├── LICENSE │ │ ├── constantdelay_test.go │ │ ├── spec.go │ │ └── doc.go │ ├── jinzhu │ └── gorm │ │ ├── .gitignore │ │ ├── dialects │ │ ├── mysql │ │ │ └── mysql.go │ │ ├── sqlite │ │ │ └── sqlite.go │ │ └── postgres │ │ │ └── postgres.go │ │ ├── test_all.sh │ │ ├── .codeclimate.yml │ │ ├── model.go │ │ ├── errors_test.go │ │ ├── .github │ │ ├── PULL_REQUEST_TEMPLATE.md │ │ └── ISSUE_TEMPLATE.md │ │ ├── interface.go │ │ ├── search_test.go │ │ ├── callback_row_query.go │ │ ├── License │ │ ├── utils_test.go │ │ ├── scope_test.go │ │ ├── field_test.go │ │ ├── field.go │ │ ├── wercker.yml │ │ ├── errors.go │ │ ├── README.md │ │ ├── callback_delete.go │ │ ├── embedded_struct_test.go │ │ ├── callback_query.go │ │ ├── join_table_test.go │ │ ├── delete_test.go │ │ ├── pointer_test.go │ │ ├── dialect_sqlite3.go │ │ ├── logger.go │ │ ├── callback_save.go │ │ ├── scaner_test.go │ │ ├── callback_update.go │ │ ├── dialect.go │ │ ├── dialect_postgres.go │ │ ├── search.go │ │ ├── callback_system_test.go │ │ ├── dialect_common.go │ │ └── dialect_mysql.go │ ├── urfave │ └── cli │ │ ├── .flake8 │ │ ├── .gitignore │ │ ├── altsrc │ │ ├── altsrc.go │ │ ├── helpers_test.go │ │ ├── input_source_context.go │ │ ├── yaml_file_loader.go │ │ └── toml_file_loader.go │ │ ├── autocomplete │ │ ├── zsh_autocomplete │ │ └── bash_autocomplete │ │ ├── helpers_unix_test.go │ │ ├── helpers_windows_test.go │ │ ├── appveyor.yml │ │ ├── .travis.yml │ │ ├── helpers_test.go │ │ ├── cli.go │ │ ├── LICENSE │ │ ├── category.go │ │ ├── funcs.go │ │ ├── flag-types.json │ │ ├── errors_test.go │ │ ├── errors.go │ │ └── runtests │ ├── buger │ └── jsonparser │ │ ├── .travis.yml │ │ ├── .gitignore │ │ ├── .github │ │ └── PULL_REQUEST_TEMPLATE.md │ │ ├── Dockerfile │ │ ├── bytes_safe.go │ │ ├── bytes.go │ │ ├── bytes_unsafe.go │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── bytes_test.go │ │ ├── bytes_unsafe_test.go │ │ └── benchmark │ │ └── benchmark_large_payload_test.go │ └── garyburd │ └── redigo │ ├── .github │ ├── ISSUE_TEMPLATE.md │ └── CONTRIBUTING.md │ ├── .travis.yml │ ├── internal │ ├── commandinfo_test.go │ ├── commandinfo.go │ └── redistest │ │ └── testdb.go │ ├── redisx │ ├── doc.go │ └── connmux.go │ ├── redis │ ├── pre_go17.go │ ├── go17.go │ ├── redis.go │ ├── zpop_example_test.go │ ├── script_test.go │ ├── script.go │ ├── log.go │ ├── pubsub.go │ ├── reply_test.go │ ├── pubsub_test.go │ └── test_test.go │ └── README.markdown ├── task ├── task.go ├── template_task.go └── text_custom_task.go ├── models ├── token.go ├── custom.go └── message.go ├── statics └── global.go ├── config.conf.example ├── redis ├── redis_test.go └── redis.go ├── glide.yaml ├── utils ├── crontab.go ├── access_token.go └── push.go ├── hlog ├── filelog_test.go ├── hlog.go └── filelog.go ├── enum └── task_type.go ├── glide.lock ├── main.go.example ├── config └── config.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | config.conf 2 | .idea/* 3 | main.go -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/.gitignore: -------------------------------------------------------------------------------- 1 | documents 2 | _book 3 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 1.7 3 | script: go test -v ./. 4 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialects/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import _ "github.com/go-sql-driver/mysql" 4 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialects/sqlite/sqlite.go: -------------------------------------------------------------------------------- 1 | package sqlite 2 | 3 | import _ "github.com/mattn/go-sqlite3" 4 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.test 3 | 4 | *.out 5 | 6 | *.mprof 7 | 8 | vendor/github.com/buger/goterm/ 9 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Ask questions at https://stackoverflow.com/questions/ask?tags=go+redis 2 | -------------------------------------------------------------------------------- /task/task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | type Task interface { 4 | GetTaskType() string 5 | SetTask(interface{}) 6 | GetTask() interface{} 7 | } 8 | -------------------------------------------------------------------------------- /models/token.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Token struct { 4 | AccessToken string `json:"access_token"` 5 | ExpiresIn int `json:"expires_in"` 6 | } 7 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/altsrc/altsrc.go: -------------------------------------------------------------------------------- 1 | package altsrc 2 | 3 | //go:generate python ../generate-flag-types altsrc -i ../flag-types.json -o flag_generated.go 4 | -------------------------------------------------------------------------------- /statics/global.go: -------------------------------------------------------------------------------- 1 | package statics 2 | 3 | 4 | const ( 5 | WECHAT_GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s" 6 | ) -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/test_all.sh: -------------------------------------------------------------------------------- 1 | dialects=("postgres" "mysql" "mssql" "sqlite") 2 | 3 | for dialect in "${dialects[@]}" ; do 4 | GORM_DIALECT=${dialect} go test 5 | done 6 | -------------------------------------------------------------------------------- /config.conf.example: -------------------------------------------------------------------------------- 1 | [WeChat] 2 | APPID= 3 | SECRET= 4 | TOKEN= 5 | TEMPLATE= 6 | 7 | [Redis] 8 | POOL_SIZE= 9 | TIMEOUT= 10 | HOST= 11 | PASS= 12 | DB= 13 | 14 | [Log] 15 | LOG_PATH= -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/autocomplete/zsh_autocomplete: -------------------------------------------------------------------------------- 1 | autoload -U compinit && compinit 2 | autoload -U bashcompinit && bashcompinit 3 | 4 | script_dir=$(dirname $0) 5 | source ${script_dir}/bash_autocomplete 6 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/helpers_unix_test.go: -------------------------------------------------------------------------------- 1 | // +build darwin dragonfly freebsd linux netbsd openbsd solaris 2 | 3 | package cli 4 | 5 | import "os" 6 | 7 | func clearenv() { 8 | os.Clearenv() 9 | } 10 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | gofmt: 4 | enabled: true 5 | govet: 6 | enabled: true 7 | golint: 8 | enabled: true 9 | ratings: 10 | paths: 11 | - "**.go" 12 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron) 2 | [![Build Status](https://travis-ci.org/robfig/cron.svg?branch=master)](https://travis-ci.org/robfig/cron) 3 | -------------------------------------------------------------------------------- /models/custom.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Text struct { 4 | Content string `json:"content"` 5 | } 6 | 7 | type TextCustom struct { 8 | ToUser string `json:"touser"` 9 | MsgType string `json:"msgtype"` 10 | Text Text `json:"text"` 11 | } 12 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Ask questions at 2 | [StackOverflow](https://stackoverflow.com/questions/ask?tags=go+redis). 3 | 4 | [Open an issue](https://github.com/garyburd/redigo/issues/new) to discuss your 5 | plans before doing any work on Redigo. 6 | -------------------------------------------------------------------------------- /redis/redis_test.go: -------------------------------------------------------------------------------- 1 | package redis_test 2 | 3 | import ( 4 | "testing" 5 | "github.com/hundredlee/wechat_pusher/redis" 6 | "fmt" 7 | ) 8 | 9 | func TestNewRedis(t *testing.T) { 10 | 11 | } 12 | 13 | func TestTTL(t *testing.T) { 14 | fmt.Println(redis.TTL("xxx")) 15 | } 16 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: WechatPuhser 2 | import: 3 | - package: github.com/robfig/cron 4 | repo: git@github.com:robfig/cron.git 5 | vcs: git 6 | - package: github.com/jinzhu/gorm 7 | repo: git@github.com:jinzhu/gorm.git 8 | vcs: git 9 | - package: github.com/buger/jsonparser 10 | - package: github.com/garyburd/redigo 11 | - package: github.com/urfave/cli -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description**: What this PR does 2 | 3 | **Benchmark before change**: 4 | 5 | **Benchmark after change**: 6 | 7 | 8 | For running benchmarks use: 9 | ``` 10 | go test -test.benchmem -bench JsonParser ./benchmark/ -benchtime 5s -v 11 | # OR 12 | make bench (runs inside docker) 13 | ``` -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | services: 4 | - redis-server 5 | 6 | go: 7 | - 1.4 8 | - 1.5 9 | - 1.6 10 | - 1.7 11 | - 1.8 12 | - tip 13 | 14 | script: 15 | - go get -t -v ./... 16 | - diff -u <(echo -n) <(gofmt -d .) 17 | - go vet $(go list ./... | grep -v /vendor/) 18 | - go test -v -race ./... 19 | -------------------------------------------------------------------------------- /utils/crontab.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "github.com/robfig/cron" 4 | 5 | var ( 6 | cronInstance *cron.Cron 7 | ) 8 | 9 | func getCronInstance() *cron.Cron { 10 | if cronInstance != nil { 11 | return cronInstance 12 | } 13 | cronInstance = cron.New() 14 | return cronInstance 15 | } 16 | 17 | func StartCron() { 18 | getCronInstance().Start() 19 | select {} 20 | } 21 | -------------------------------------------------------------------------------- /hlog/filelog_test.go: -------------------------------------------------------------------------------- 1 | package hlog_test 2 | 3 | import ( 4 | "github.com/hundredlee/wechat_pusher/hlog" 5 | "testing" 6 | ) 7 | 8 | func TestFileLog_LogError(t *testing.T) { 9 | filelog := hlog.LogInstance() 10 | filelog.LogError("xxxxxxxxx") 11 | } 12 | 13 | func TestFileLog_LogInfo(t *testing.T) { 14 | filelog := hlog.LogInstance() 15 | filelog.LogError("yyyyyyyyy") 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /hlog/hlog.go: -------------------------------------------------------------------------------- 1 | package hlog 2 | 3 | import "runtime" 4 | 5 | type hlog struct { 6 | isOpen bool 7 | } 8 | 9 | func (this *hlog) Open() { 10 | this.isOpen = true 11 | } 12 | 13 | func (this *hlog) Close() { 14 | this.isOpen = false 15 | } 16 | 17 | func (this *hlog) getCaller() (string, int) { 18 | _, file, line, ok := runtime.Caller(3) 19 | if !ok { 20 | file = "???" 21 | line = 0 22 | } 23 | return file, line 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/model.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import "time" 4 | 5 | // Model base model definition, including fields `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`, which could be embedded in your models 6 | // type User struct { 7 | // gorm.Model 8 | // } 9 | type Model struct { 10 | ID uint `gorm:"primary_key"` 11 | CreatedAt time.Time 12 | UpdatedAt time.Time 13 | DeletedAt *time.Time `sql:"index"` 14 | } 15 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/autocomplete/bash_autocomplete: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | : ${PROG:=$(basename ${BASH_SOURCE})} 4 | 5 | _cli_bash_autocomplete() { 6 | local cur opts base 7 | COMPREPLY=() 8 | cur="${COMP_WORDS[COMP_CWORD]}" 9 | opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) 10 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 11 | return 0 12 | } 13 | 14 | complete -F _cli_bash_autocomplete $PROG 15 | 16 | unset PROG 17 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.6 2 | 3 | RUN go get github.com/Jeffail/gabs 4 | RUN go get github.com/bitly/go-simplejson 5 | RUN go get github.com/pquerna/ffjson 6 | RUN go get github.com/antonholmquist/jason 7 | RUN go get github.com/mreiferson/go-ujson 8 | RUN go get -tags=unsafe -u github.com/ugorji/go/codec 9 | RUN go get github.com/mailru/easyjson 10 | 11 | WORKDIR /go/src/github.com/buger/jsonparser 12 | ADD . /go/src/github.com/buger/jsonparser -------------------------------------------------------------------------------- /models/message.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Message struct { 4 | ToUser string `json:"touser"` 5 | TemplateId string `json:"template_id"` 6 | Url string `json:"url"` 7 | Data Data `json:"data"` 8 | } 9 | 10 | type Data struct { 11 | First Raw `json:"first"` 12 | Subject Raw `json:"subject"` 13 | Sender Raw `json:"sender"` 14 | Remark Raw `json:"remark"` 15 | } 16 | 17 | type Raw struct { 18 | Value string `json:"value"` 19 | Color string `json:"color"` 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/helpers_windows_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | // os.Clearenv() doesn't actually unset variables on Windows 9 | // See: https://github.com/golang/go/issues/17902 10 | func clearenv() { 11 | for _, s := range os.Environ() { 12 | for j := 1; j < len(s); j++ { 13 | if s[j] == '=' { 14 | keyp, _ := syscall.UTF16PtrFromString(s[0:j]) 15 | syscall.SetEnvironmentVariable(keyp, nil) 16 | break 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/errors_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/jinzhu/gorm" 8 | ) 9 | 10 | func TestErrorsCanBeUsedOutsideGorm(t *testing.T) { 11 | errs := []error{errors.New("First"), errors.New("Second")} 12 | 13 | gErrs := gorm.Errors(errs) 14 | gErrs = gErrs.Add(errors.New("Third")) 15 | gErrs = gErrs.Add(gErrs) 16 | 17 | if gErrs.Error() != "First; Second; Third" { 18 | t.Fatalf("Gave wrong error, got %s", gErrs.Error()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /enum/task_type.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | 4 | const ( 5 | TASK_TYPE_TEMPLATE = "TYPE_TEMPLATE" 6 | TASK_TYPE_TEXT_CUSTOM = "TYPE_TEXT_CUSTOM" 7 | TASK_TYPE_IMAGE = "TYPE_IMAGE" 8 | TASK_TYPE_WORD = "TYPE_WORD" 9 | TASK_TYPE_IMAGE_WORD = "TYPE_IMAGE_WORD" 10 | ) 11 | 12 | 13 | var URL_MAP map[string]string = map[string]string { 14 | TASK_TYPE_TEMPLATE:"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s", 15 | TASK_TYPE_TEXT_CUSTOM:"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s"} 16 | 17 | 18 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/altsrc/helpers_test.go: -------------------------------------------------------------------------------- 1 | package altsrc 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func expect(t *testing.T, a interface{}, b interface{}) { 9 | if !reflect.DeepEqual(b, a) { 10 | t.Errorf("Expected %#v (type %v) - Got %#v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 11 | } 12 | } 13 | 14 | func refute(t *testing.T, a interface{}, b interface{}) { 15 | if a == b { 16 | t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/bytes_safe.go: -------------------------------------------------------------------------------- 1 | // +build appengine appenginevm 2 | 3 | package jsonparser 4 | 5 | import ( 6 | "strconv" 7 | ) 8 | 9 | // See fastbytes_unsafe.go for explanation on why *[]byte is used (signatures must be consistent with those in that file) 10 | 11 | func equalStr(b *[]byte, s string) bool { 12 | return string(*b) == s 13 | } 14 | 15 | func parseFloat(b *[]byte) (float64, error) { 16 | return strconv.ParseFloat(string(*b), 64) 17 | } 18 | 19 | func bytesToString(b *[]byte) string { 20 | return string(*b) 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | os: Windows Server 2012 R2 4 | 5 | clone_folder: c:\gopath\src\github.com\urfave\cli 6 | 7 | environment: 8 | GOPATH: C:\gopath 9 | GOVERSION: 1.6 10 | PYTHON: C:\Python27-x64 11 | PYTHON_VERSION: 2.7.x 12 | PYTHON_ARCH: 64 13 | 14 | install: 15 | - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% 16 | - go version 17 | - go env 18 | - go get github.com/urfave/gfmrun/... 19 | - go get -v -t ./... 20 | 21 | build_script: 22 | - python runtests vet 23 | - python runtests test 24 | - python runtests gfmrun 25 | -------------------------------------------------------------------------------- /task/template_task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/hundredlee/wechat_pusher/models" 5 | "github.com/hundredlee/wechat_pusher/enum" 6 | ) 7 | 8 | type TemplateTask struct { 9 | taskValue models.Message 10 | } 11 | 12 | func (self *TemplateTask) GetTaskType() string { 13 | return enum.TASK_TYPE_TEMPLATE 14 | } 15 | 16 | func (self *TemplateTask) SetTask(taskValue interface{}) { 17 | 18 | v,ok := taskValue.(models.Message) 19 | if ok { 20 | self.taskValue = v 21 | } 22 | } 23 | 24 | func (self *TemplateTask) GetTask() interface {}{ 25 | return self.taskValue 26 | } 27 | -------------------------------------------------------------------------------- /task/text_custom_task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/hundredlee/wechat_pusher/models" 5 | "github.com/hundredlee/wechat_pusher/enum" 6 | ) 7 | 8 | type TextCustomTask struct { 9 | taskValue models.TextCustom 10 | } 11 | 12 | func (self *TextCustomTask) GetTaskType() string { 13 | return enum.TASK_TYPE_TEXT_CUSTOM 14 | } 15 | 16 | func (self *TextCustomTask) SetTask(taskValue interface{}) { 17 | 18 | v,ok := taskValue.(models.TextCustom) 19 | if ok { 20 | self.taskValue = v 21 | } 22 | } 23 | 24 | func (self *TextCustomTask) GetTask() interface {}{ 25 | return self.taskValue 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Make sure these boxes checked before submitting your pull request. 2 | 3 | - [] Do only one thing 4 | - [] No API-breaking changes 5 | - [] New code/logic commented & tested 6 | - [] Write good commit message, try to squash your commits into a single one 7 | - [] Run `./build.sh` in `gh-pages` branch for document changes 8 | 9 | For significant changes like big bug fixes, new features, please open an issue to make a agreement on an implementation design/plan first before starting it. 10 | 11 | Thank you. 12 | 13 | 14 | ### What did this pull request do? 15 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/interface.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import "database/sql" 4 | 5 | // SQLCommon is the minimal database connection functionality gorm requires. Implemented by *sql.DB. 6 | type SQLCommon interface { 7 | Exec(query string, args ...interface{}) (sql.Result, error) 8 | Prepare(query string) (*sql.Stmt, error) 9 | Query(query string, args ...interface{}) (*sql.Rows, error) 10 | QueryRow(query string, args ...interface{}) *sql.Row 11 | } 12 | 13 | type sqlDb interface { 14 | Begin() (*sql.Tx, error) 15 | } 16 | 17 | type sqlTx interface { 18 | Commit() error 19 | Rollback() error 20 | } 21 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/bytes.go: -------------------------------------------------------------------------------- 1 | package jsonparser 2 | 3 | // About 3x faster then strconv.ParseInt because does not check for range error and support only base 10, which is enough for JSON 4 | func parseInt(bytes []byte) (v int64, ok bool) { 5 | if len(bytes) == 0 { 6 | return 0, false 7 | } 8 | 9 | var neg bool = false 10 | if bytes[0] == '-' { 11 | neg = true 12 | bytes = bytes[1:] 13 | } 14 | 15 | for _, c := range bytes { 16 | if c >= '0' && c <= '9' { 17 | v = (10 * v) + int64(c-'0') 18 | } else { 19 | return 0, false 20 | } 21 | } 22 | 23 | if neg { 24 | return -v, true 25 | } else { 26 | return v, true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/altsrc/input_source_context.go: -------------------------------------------------------------------------------- 1 | package altsrc 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/urfave/cli.v1" 7 | ) 8 | 9 | // InputSourceContext is an interface used to allow 10 | // other input sources to be implemented as needed. 11 | type InputSourceContext interface { 12 | Int(name string) (int, error) 13 | Duration(name string) (time.Duration, error) 14 | Float64(name string) (float64, error) 15 | String(name string) (string, error) 16 | StringSlice(name string) ([]string, error) 17 | IntSlice(name string) ([]int, error) 18 | Generic(name string) (cli.Generic, error) 19 | Bool(name string) (bool, error) 20 | BoolT(name string) (bool, error) 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | go: 10 | - 1.6.x 11 | - 1.7.x 12 | - 1.8.x 13 | - master 14 | 15 | matrix: 16 | allow_failures: 17 | - go: master 18 | include: 19 | - go: 1.6.x 20 | os: osx 21 | - go: 1.7.x 22 | os: osx 23 | - go: 1.8.x 24 | os: osx 25 | 26 | before_script: 27 | - go get github.com/urfave/gfmrun/... || true 28 | - go get golang.org/x/tools/cmd/goimports 29 | - if [ ! -f node_modules/.bin/markdown-toc ] ; then 30 | npm install markdown-toc ; 31 | fi 32 | 33 | script: 34 | - ./runtests gen 35 | - ./runtests vet 36 | - ./runtests test 37 | - ./runtests gfmrun 38 | - ./runtests toc 39 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/helpers_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "runtime" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | var ( 12 | wd, _ = os.Getwd() 13 | ) 14 | 15 | func expect(t *testing.T, a interface{}, b interface{}) { 16 | _, fn, line, _ := runtime.Caller(1) 17 | fn = strings.Replace(fn, wd+"/", "", -1) 18 | 19 | if !reflect.DeepEqual(a, b) { 20 | t.Errorf("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 21 | } 22 | } 23 | 24 | func refute(t *testing.T, a interface{}, b interface{}) { 25 | if reflect.DeepEqual(a, b) { 26 | t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 2ba71489eabf2f4e5fef1034083b88dfa6ea7663a5357119587844c83908d18b 2 | updated: 2017-06-10T14:42:27.279366421+08:00 3 | imports: 4 | - name: github.com/buger/jsonparser 5 | version: bb14bb6c38f6cf1706ef55278891d184b6a51b0e 6 | - name: github.com/garyburd/redigo 7 | version: 95d11dba2d44531bdb8022752b98912baafae03a 8 | subpackages: 9 | - internal 10 | - redis 11 | - name: github.com/jinzhu/gorm 12 | version: 9acaa33324bbcc78239a1c913d4f1292c12177b9 13 | repo: git@github.com:jinzhu/gorm.git 14 | vcs: git 15 | - name: github.com/robfig/cron 16 | version: 736158dc09e10f1911ca3a1e1b01f11b566ce5db 17 | repo: git@github.com:robfig/cron.git 18 | vcs: git 19 | - name: github.com/urfave/cli 20 | version: d70f47eeca3afd795160003bc6e28b001d60c67c 21 | testImports: [] 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) error { 14 | // println("Greetings") 15 | // } 16 | // 17 | // app.Run(os.Args) 18 | // } 19 | package cli 20 | 21 | //go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go 22 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/search_test.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestCloneSearch(t *testing.T) { 9 | s := new(search) 10 | s.Where("name = ?", "jinzhu").Order("name").Attrs("name", "jinzhu").Select("name, age") 11 | 12 | s1 := s.clone() 13 | s1.Where("age = ?", 20).Order("age").Attrs("email", "a@e.org").Select("email") 14 | 15 | if reflect.DeepEqual(s.whereConditions, s1.whereConditions) { 16 | t.Errorf("Where should be copied") 17 | } 18 | 19 | if reflect.DeepEqual(s.orders, s1.orders) { 20 | t.Errorf("Order should be copied") 21 | } 22 | 23 | if reflect.DeepEqual(s.initAttrs, s1.initAttrs) { 24 | t.Errorf("InitAttrs should be copied") 25 | } 26 | 27 | if reflect.DeepEqual(s.Select, s1.Select) { 28 | t.Errorf("selectStr should be copied") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "testing" 4 | 5 | func TestLookupCommandInfo(t *testing.T) { 6 | for _, n := range []string{"watch", "WATCH", "wAtch"} { 7 | if LookupCommandInfo(n) == (CommandInfo{}) { 8 | t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n) 9 | } 10 | } 11 | } 12 | 13 | func benchmarkLookupCommandInfo(b *testing.B, names ...string) { 14 | for i := 0; i < b.N; i++ { 15 | for _, c := range names { 16 | LookupCommandInfo(c) 17 | } 18 | } 19 | } 20 | 21 | func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) { 22 | benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR") 23 | } 24 | 25 | func BenchmarkLookupCommandInfoMixedCase(b *testing.B) { 26 | benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR") 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redisx/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Package redisx contains experimental features for Redigo. Features in this 16 | // package may be modified or deleted at any time. 17 | package redisx // import "github.com/garyburd/redigo/redisx" 18 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/callback_row_query.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import "database/sql" 4 | 5 | // Define callbacks for row query 6 | func init() { 7 | DefaultCallback.RowQuery().Register("gorm:row_query", rowQueryCallback) 8 | } 9 | 10 | type RowQueryResult struct { 11 | Row *sql.Row 12 | } 13 | 14 | type RowsQueryResult struct { 15 | Rows *sql.Rows 16 | Error error 17 | } 18 | 19 | // queryCallback used to query data from database 20 | func rowQueryCallback(scope *Scope) { 21 | if result, ok := scope.InstanceGet("row_query_result"); ok { 22 | scope.prepareQuerySQL() 23 | 24 | if rowResult, ok := result.(*RowQueryResult); ok { 25 | rowResult.Row = scope.SQLDB().QueryRow(scope.SQL, scope.SQLVars...) 26 | } else if rowsResult, ok := result.(*RowsQueryResult); ok { 27 | rowsResult.Rows, rowsResult.Error = scope.SQLDB().Query(scope.SQL, scope.SQLVars...) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/constantdelay.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "time" 4 | 5 | // ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". 6 | // It does not support jobs more frequent than once a second. 7 | type ConstantDelaySchedule struct { 8 | Delay time.Duration 9 | } 10 | 11 | // Every returns a crontab Schedule that activates once every duration. 12 | // Delays of less than a second are not supported (will round up to 1 second). 13 | // Any fields less than a Second are truncated. 14 | func Every(duration time.Duration) ConstantDelaySchedule { 15 | if duration < time.Second { 16 | duration = time.Second 17 | } 18 | return ConstantDelaySchedule{ 19 | Delay: duration - time.Duration(duration.Nanoseconds())%time.Second, 20 | } 21 | } 22 | 23 | // Next returns the next time this should be run. 24 | // This rounds so that the next activation time will be on the second. 25 | func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time { 26 | return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond) 27 | } 28 | -------------------------------------------------------------------------------- /main.go.example: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hundredlee/wechat_pusher/enum" 5 | "github.com/hundredlee/wechat_pusher/models" 6 | "github.com/hundredlee/wechat_pusher/task" 7 | "github.com/hundredlee/wechat_pusher/utils" 8 | "runtime" 9 | ) 10 | 11 | func main() { 12 | 13 | runtime.GOMAXPROCS(runtime.NumCPU()) 14 | var tasks []task.Task 15 | tasks = make([]task.Task, 100) 16 | mess := models.Message{ 17 | ToUser: "oBv9cuLU5zyI27CtzI4VhV6Xabms", 18 | TemplateId: "UXb6s5dahNC5Zt-xQIxbLJG1BdP8mP73LGLhNXl68J8", 19 | Url: "http://baidu.com", 20 | Data: models.Data{ 21 | First: models.Raw{"xxx", "#173177"}, 22 | Subject: models.Raw{"xxx", "#173177"}, 23 | Sender: models.Raw{"xxx", "#173177"}, 24 | Remark: models.Raw{"xxx", "#173177"}}} 25 | task := task.TemplateTask{} 26 | task.SetTask(mess) 27 | 28 | for i := 0; i < 100; i++ { 29 | tasks[i] = &task 30 | } 31 | 32 | utils.NewPush(&utils.Push{ 33 | Tasks:tasks, 34 | TaskType:enum.TASK_TYPE_TEMPLATE, 35 | Retries:4, 36 | BufferNum:10, 37 | }).Add("45 * * * * *") 38 | 39 | utils.StartCron() 40 | 41 | } 42 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/bytes_unsafe.go: -------------------------------------------------------------------------------- 1 | // +build !appengine,!appenginevm 2 | 3 | package jsonparser 4 | 5 | import ( 6 | "strconv" 7 | "unsafe" 8 | ) 9 | 10 | // 11 | // The reason for using *[]byte rather than []byte in parameters is an optimization. As of Go 1.6, 12 | // the compiler cannot perfectly inline the function when using a non-pointer slice. That is, 13 | // the non-pointer []byte parameter version is slower than if its function body is manually 14 | // inlined, whereas the pointer []byte version is equally fast to the manually inlined 15 | // version. Instruction count in assembly taken from "go tool compile" confirms this difference. 16 | // 17 | // TODO: Remove hack after Go 1.7 release 18 | // 19 | func equalStr(b *[]byte, s string) bool { 20 | return *(*string)(unsafe.Pointer(b)) == s 21 | } 22 | 23 | func parseFloat(b *[]byte) (float64, error) { 24 | return strconv.ParseFloat(*(*string)(unsafe.Pointer(b)), 64) 25 | } 26 | 27 | // A hack until issue golang/go#2632 is fixed. 28 | // See: https://github.com/golang/go/issues/2632 29 | func bytesToString(b *[]byte) string { 30 | return *(*string)(unsafe.Pointer(b)) 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialects/postgres/postgres.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | import ( 4 | "database/sql" 5 | "database/sql/driver" 6 | 7 | _ "github.com/lib/pq" 8 | "github.com/lib/pq/hstore" 9 | ) 10 | 11 | type Hstore map[string]*string 12 | 13 | // Value get value of Hstore 14 | func (h Hstore) Value() (driver.Value, error) { 15 | hstore := hstore.Hstore{Map: map[string]sql.NullString{}} 16 | if len(h) == 0 { 17 | return nil, nil 18 | } 19 | 20 | for key, value := range h { 21 | var s sql.NullString 22 | if value != nil { 23 | s.String = *value 24 | s.Valid = true 25 | } 26 | hstore.Map[key] = s 27 | } 28 | return hstore.Value() 29 | } 30 | 31 | // Scan scan value into Hstore 32 | func (h *Hstore) Scan(value interface{}) error { 33 | hstore := hstore.Hstore{} 34 | 35 | if err := hstore.Scan(value); err != nil { 36 | return err 37 | } 38 | 39 | if len(hstore.Map) == 0 { 40 | return nil 41 | } 42 | 43 | *h = Hstore{} 44 | for k := range hstore.Map { 45 | if hstore.Map[k].Valid { 46 | s := hstore.Map[k].String 47 | (*h)[k] = &s 48 | } else { 49 | (*h)[k] = nil 50 | } 51 | } 52 | 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Leonid Bugaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jeremy Saenz & Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/Makefile: -------------------------------------------------------------------------------- 1 | SOURCE = parser.go 2 | CONTAINER = jsonparser 3 | SOURCE_PATH = /go/src/github.com/buger/jsonparser 4 | BENCHMARK = JsonParser 5 | BENCHTIME = 5s 6 | TEST = . 7 | DRUN = docker run -v `pwd`:$(SOURCE_PATH) -i -t $(CONTAINER) 8 | 9 | build: 10 | docker build -t $(CONTAINER) . 11 | 12 | race: 13 | $(DRUN) --env GORACE="halt_on_error=1" go test ./. $(ARGS) -v -race -timeout 15s 14 | 15 | bench: 16 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -benchtime $(BENCHTIME) -v 17 | 18 | bench_local: 19 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench . $(ARGS) -benchtime $(BENCHTIME) -v 20 | 21 | profile: 22 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -memprofile mem.mprof -v 23 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -cpuprofile cpu.out -v 24 | $(DRUN) go test $(LDFLAGS) -test.benchmem -bench $(BENCHMARK) ./benchmark/ $(ARGS) -c 25 | 26 | test: 27 | $(DRUN) go test $(LDFLAGS) ./ -run $(TEST) -timeout 10s $(ARGS) -v 28 | 29 | fmt: 30 | $(DRUN) go fmt ./... 31 | 32 | vet: 33 | $(DRUN) go vet ./. 34 | 35 | bash: 36 | $(DRUN) /bin/bash -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Rob Figueiredo 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/License: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-NOW Jinzhu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/utils_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | func TestToDBNameGenerateFriendlyName(t *testing.T) { 10 | var maps = map[string]string{ 11 | "": "", 12 | "X": "x", 13 | "ThisIsATest": "this_is_a_test", 14 | "PFAndESI": "pf_and_esi", 15 | "AbcAndJkl": "abc_and_jkl", 16 | "EmployeeID": "employee_id", 17 | "SKU_ID": "sku_id", 18 | "FieldX": "field_x", 19 | "HTTPAndSMTP": "http_and_smtp", 20 | "HTTPServerHandlerForURLID": "http_server_handler_for_url_id", 21 | "UUID": "uuid", 22 | "HTTPURL": "http_url", 23 | "HTTP_URL": "http_url", 24 | "ThisIsActuallyATestSoWeMayBeAbleToUseThisCodeInGormPackageAlsoIdCanBeUsedAtTheEndAsID": "this_is_actually_a_test_so_we_may_be_able_to_use_this_code_in_gorm_package_also_id_can_be_used_at_the_end_as_id", 25 | } 26 | 27 | for key, value := range maps { 28 | if gorm.ToDBName(key) != value { 29 | t.Errorf("%v ToDBName should equal %v, but got %v", key, value, gorm.ToDBName(key)) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pre_go17.go: -------------------------------------------------------------------------------- 1 | // +build !go1.7 2 | 3 | package redis 4 | 5 | import "crypto/tls" 6 | 7 | // similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case 8 | func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config { 9 | if cfg == nil { 10 | return &tls.Config{InsecureSkipVerify: skipVerify} 11 | } 12 | return &tls.Config{ 13 | Rand: cfg.Rand, 14 | Time: cfg.Time, 15 | Certificates: cfg.Certificates, 16 | NameToCertificate: cfg.NameToCertificate, 17 | GetCertificate: cfg.GetCertificate, 18 | RootCAs: cfg.RootCAs, 19 | NextProtos: cfg.NextProtos, 20 | ServerName: cfg.ServerName, 21 | ClientAuth: cfg.ClientAuth, 22 | ClientCAs: cfg.ClientCAs, 23 | InsecureSkipVerify: cfg.InsecureSkipVerify, 24 | CipherSuites: cfg.CipherSuites, 25 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 26 | ClientSessionCache: cfg.ClientSessionCache, 27 | MinVersion: cfg.MinVersion, 28 | MaxVersion: cfg.MaxVersion, 29 | CurvePreferences: cfg.CurvePreferences, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/scope_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "testing" 6 | ) 7 | 8 | func NameIn1And2(d *gorm.DB) *gorm.DB { 9 | return d.Where("name in (?)", []string{"ScopeUser1", "ScopeUser2"}) 10 | } 11 | 12 | func NameIn2And3(d *gorm.DB) *gorm.DB { 13 | return d.Where("name in (?)", []string{"ScopeUser2", "ScopeUser3"}) 14 | } 15 | 16 | func NameIn(names []string) func(d *gorm.DB) *gorm.DB { 17 | return func(d *gorm.DB) *gorm.DB { 18 | return d.Where("name in (?)", names) 19 | } 20 | } 21 | 22 | func TestScopes(t *testing.T) { 23 | user1 := User{Name: "ScopeUser1", Age: 1} 24 | user2 := User{Name: "ScopeUser2", Age: 1} 25 | user3 := User{Name: "ScopeUser3", Age: 2} 26 | DB.Save(&user1).Save(&user2).Save(&user3) 27 | 28 | var users1, users2, users3 []User 29 | DB.Scopes(NameIn1And2).Find(&users1) 30 | if len(users1) != 2 { 31 | t.Errorf("Should found two users's name in 1, 2") 32 | } 33 | 34 | DB.Scopes(NameIn1And2, NameIn2And3).Find(&users2) 35 | if len(users2) != 1 { 36 | t.Errorf("Should found one user's name is 2") 37 | } 38 | 39 | DB.Scopes(NameIn([]string{user1.Name, user3.Name})).Find(&users3) 40 | if len(users3) != 2 { 41 | t.Errorf("Should found two users's name in 1, 3") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/category.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // CommandCategories is a slice of *CommandCategory. 4 | type CommandCategories []*CommandCategory 5 | 6 | // CommandCategory is a category containing commands. 7 | type CommandCategory struct { 8 | Name string 9 | Commands Commands 10 | } 11 | 12 | func (c CommandCategories) Less(i, j int) bool { 13 | return c[i].Name < c[j].Name 14 | } 15 | 16 | func (c CommandCategories) Len() int { 17 | return len(c) 18 | } 19 | 20 | func (c CommandCategories) Swap(i, j int) { 21 | c[i], c[j] = c[j], c[i] 22 | } 23 | 24 | // AddCommand adds a command to a category. 25 | func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { 26 | for _, commandCategory := range c { 27 | if commandCategory.Name == category { 28 | commandCategory.Commands = append(commandCategory.Commands, command) 29 | return c 30 | } 31 | } 32 | return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) 33 | } 34 | 35 | // VisibleCommands returns a slice of the Commands with Hidden=false 36 | func (c *CommandCategory) VisibleCommands() []Command { 37 | ret := []Command{} 38 | for _, command := range c.Commands { 39 | if !command.Hidden { 40 | ret = append(ret, command) 41 | } 42 | } 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/field_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | type CalculateField struct { 10 | gorm.Model 11 | Name string 12 | Children []CalculateFieldChild 13 | Category CalculateFieldCategory 14 | EmbeddedField 15 | } 16 | 17 | type EmbeddedField struct { 18 | EmbeddedName string `sql:"NOT NULL;DEFAULT:'hello'"` 19 | } 20 | 21 | type CalculateFieldChild struct { 22 | gorm.Model 23 | CalculateFieldID uint 24 | Name string 25 | } 26 | 27 | type CalculateFieldCategory struct { 28 | gorm.Model 29 | CalculateFieldID uint 30 | Name string 31 | } 32 | 33 | func TestCalculateField(t *testing.T) { 34 | var field CalculateField 35 | var scope = DB.NewScope(&field) 36 | if field, ok := scope.FieldByName("Children"); !ok || field.Relationship == nil { 37 | t.Errorf("Should calculate fields correctly for the first time") 38 | } 39 | 40 | if field, ok := scope.FieldByName("Category"); !ok || field.Relationship == nil { 41 | t.Errorf("Should calculate fields correctly for the first time") 42 | } 43 | 44 | if field, ok := scope.FieldByName("embedded_name"); !ok { 45 | t.Errorf("should find embedded field") 46 | } else if _, ok := field.TagSettings["NOT NULL"]; !ok { 47 | t.Errorf("should find embedded field's tag settings") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/funcs.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // BashCompleteFunc is an action to execute when the bash-completion flag is set 4 | type BashCompleteFunc func(*Context) 5 | 6 | // BeforeFunc is an action to execute before any subcommands are run, but after 7 | // the context is ready if a non-nil error is returned, no subcommands are run 8 | type BeforeFunc func(*Context) error 9 | 10 | // AfterFunc is an action to execute after any subcommands are run, but after the 11 | // subcommand has finished it is run even if Action() panics 12 | type AfterFunc func(*Context) error 13 | 14 | // ActionFunc is the action to execute when no subcommands are specified 15 | type ActionFunc func(*Context) error 16 | 17 | // CommandNotFoundFunc is executed if the proper command cannot be found 18 | type CommandNotFoundFunc func(*Context, string) 19 | 20 | // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying 21 | // customized usage error messages. This function is able to replace the 22 | // original error messages. If this function is not set, the "Incorrect usage" 23 | // is displayed and the execution is interrupted. 24 | type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error 25 | 26 | // FlagStringFunc is used by the help generation to display a flag, which is 27 | // expected to be a single line. 28 | type FlagStringFunc func(Flag) string 29 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/go17.go: -------------------------------------------------------------------------------- 1 | // +build go1.7 2 | 3 | package redis 4 | 5 | import "crypto/tls" 6 | 7 | // similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case 8 | func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config { 9 | if cfg == nil { 10 | return &tls.Config{InsecureSkipVerify: skipVerify} 11 | } 12 | return &tls.Config{ 13 | Rand: cfg.Rand, 14 | Time: cfg.Time, 15 | Certificates: cfg.Certificates, 16 | NameToCertificate: cfg.NameToCertificate, 17 | GetCertificate: cfg.GetCertificate, 18 | RootCAs: cfg.RootCAs, 19 | NextProtos: cfg.NextProtos, 20 | ServerName: cfg.ServerName, 21 | ClientAuth: cfg.ClientAuth, 22 | ClientCAs: cfg.ClientCAs, 23 | InsecureSkipVerify: cfg.InsecureSkipVerify, 24 | CipherSuites: cfg.CipherSuites, 25 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 26 | ClientSessionCache: cfg.ClientSessionCache, 27 | MinVersion: cfg.MinVersion, 28 | MaxVersion: cfg.MaxVersion, 29 | CurvePreferences: cfg.CurvePreferences, 30 | DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled, 31 | Renegotiation: cfg.Renegotiation, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /utils/access_token.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "github.com/buger/jsonparser" 8 | "github.com/hundredlee/wechat_pusher/config" 9 | "github.com/hundredlee/wechat_pusher/models" 10 | "github.com/hundredlee/wechat_pusher/redis" 11 | "github.com/hundredlee/wechat_pusher/statics" 12 | "io/ioutil" 13 | "net/http" 14 | ) 15 | 16 | var ( 17 | conf *config.Config = config.Instance() 18 | ) 19 | 20 | func GetAccessToken() string { 21 | 22 | appId,ok := conf.ConMap["WeChat.APPID"].(string) 23 | 24 | if !ok { 25 | panic("appid not string") 26 | } 27 | 28 | cacheKey := "access_token." + appId 29 | 30 | accessTokenCache := redis.Get(cacheKey) 31 | 32 | if len(string(accessTokenCache)) > 0 && redis.TTL(cacheKey) > 0 { 33 | return string(accessTokenCache) 34 | } 35 | 36 | url := fmt.Sprintf( 37 | statics.WECHAT_GET_ACCESS_TOKEN, 38 | conf.ConMap["WeChat.APPID"], conf.ConMap["WeChat.SECRET"]) 39 | 40 | response, _ := http.Get(url) 41 | 42 | defer response.Body.Close() 43 | body, _ := ioutil.ReadAll(response.Body) 44 | 45 | fmt.Println() 46 | 47 | if errCode, err := jsonparser.GetInt(body, "errcode"); err == nil && errCode != 0 { 48 | if errMsg, _, _, err := jsonparser.Get(body, "errmsg"); err != nil { 49 | panic(errors.New(string(errMsg))) 50 | } 51 | } 52 | 53 | var token models.Token 54 | 55 | if err := json.Unmarshal(body, &token); err != nil { 56 | panic(err) 57 | } 58 | 59 | redis.Set(cacheKey,token.AccessToken,false,token.ExpiresIn) 60 | 61 | return token.AccessToken 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/redis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | // Error represents an error returned in a command reply. 18 | type Error string 19 | 20 | func (err Error) Error() string { return string(err) } 21 | 22 | // Conn represents a connection to a Redis server. 23 | type Conn interface { 24 | // Close closes the connection. 25 | Close() error 26 | 27 | // Err returns a non-nil value when the connection is not usable. 28 | Err() error 29 | 30 | // Do sends a command to the server and returns the received reply. 31 | Do(commandName string, args ...interface{}) (reply interface{}, err error) 32 | 33 | // Send writes the command to the client's output buffer. 34 | Send(commandName string, args ...interface{}) error 35 | 36 | // Flush flushes the output buffer to the Redis server. 37 | Flush() error 38 | 39 | // Receive receives a single reply from the Redis server 40 | Receive() (reply interface{}, err error) 41 | } 42 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/field.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "fmt" 7 | "reflect" 8 | ) 9 | 10 | // Field model field definition 11 | type Field struct { 12 | *StructField 13 | IsBlank bool 14 | Field reflect.Value 15 | } 16 | 17 | // Set set a value to the field 18 | func (field *Field) Set(value interface{}) (err error) { 19 | if !field.Field.IsValid() { 20 | return errors.New("field value not valid") 21 | } 22 | 23 | if !field.Field.CanAddr() { 24 | return ErrUnaddressable 25 | } 26 | 27 | reflectValue, ok := value.(reflect.Value) 28 | if !ok { 29 | reflectValue = reflect.ValueOf(value) 30 | } 31 | 32 | fieldValue := field.Field 33 | if reflectValue.IsValid() { 34 | if reflectValue.Type().ConvertibleTo(fieldValue.Type()) { 35 | fieldValue.Set(reflectValue.Convert(fieldValue.Type())) 36 | } else { 37 | if fieldValue.Kind() == reflect.Ptr { 38 | if fieldValue.IsNil() { 39 | fieldValue.Set(reflect.New(field.Struct.Type.Elem())) 40 | } 41 | fieldValue = fieldValue.Elem() 42 | } 43 | 44 | if reflectValue.Type().ConvertibleTo(fieldValue.Type()) { 45 | fieldValue.Set(reflectValue.Convert(fieldValue.Type())) 46 | } else if scanner, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { 47 | err = scanner.Scan(reflectValue.Interface()) 48 | } else { 49 | err = fmt.Errorf("could not convert argument of field %s from %s to %s", field.Name, reflectValue.Type(), fieldValue.Type()) 50 | } 51 | } 52 | } else { 53 | field.Field.Set(reflect.Zero(field.Field.Type())) 54 | } 55 | 56 | field.IsBlank = isBlank(field.Field) 57 | return err 58 | } 59 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/wercker.yml: -------------------------------------------------------------------------------- 1 | # use the default golang container from Docker Hub 2 | box: golang 3 | 4 | services: 5 | - id: mariadb:10.0 6 | env: 7 | MYSQL_DATABASE: gorm 8 | MYSQL_USER: gorm 9 | MYSQL_PASSWORD: gorm 10 | MYSQL_RANDOM_ROOT_PASSWORD: "yes" 11 | - id: postgres 12 | env: 13 | POSTGRES_USER: gorm 14 | POSTGRES_PASSWORD: gorm 15 | POSTGRES_DB: gorm 16 | 17 | # The steps that will be executed in the build pipeline 18 | build: 19 | # The steps that will be executed on build 20 | steps: 21 | # Sets the go workspace and places you package 22 | # at the right place in the workspace tree 23 | - setup-go-workspace 24 | 25 | # Gets the dependencies 26 | - script: 27 | name: go get 28 | code: | 29 | cd $WERCKER_SOURCE_DIR 30 | go version 31 | go get -t ./... 32 | 33 | # Build the project 34 | - script: 35 | name: go build 36 | code: | 37 | go build ./... 38 | 39 | # Test the project 40 | - script: 41 | name: test sqlite 42 | code: | 43 | go test ./... 44 | 45 | - script: 46 | name: test mysql 47 | code: | 48 | GORM_DIALECT=mysql GORM_DBADDRESS=mariadb:3306 go test ./... 49 | 50 | - script: 51 | name: test postgres 52 | code: | 53 | GORM_DIALECT=postgres GORM_DBHOST=postgres go test ./... 54 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package internal // import "github.com/garyburd/redigo/internal" 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | const ( 22 | WatchState = 1 << iota 23 | MultiState 24 | SubscribeState 25 | MonitorState 26 | ) 27 | 28 | type CommandInfo struct { 29 | Set, Clear int 30 | } 31 | 32 | var commandInfos = map[string]CommandInfo{ 33 | "WATCH": {Set: WatchState}, 34 | "UNWATCH": {Clear: WatchState}, 35 | "MULTI": {Set: MultiState}, 36 | "EXEC": {Clear: WatchState | MultiState}, 37 | "DISCARD": {Clear: WatchState | MultiState}, 38 | "PSUBSCRIBE": {Set: SubscribeState}, 39 | "SUBSCRIBE": {Set: SubscribeState}, 40 | "MONITOR": {Set: MonitorState}, 41 | } 42 | 43 | func init() { 44 | for n, ci := range commandInfos { 45 | commandInfos[strings.ToLower(n)] = ci 46 | } 47 | } 48 | 49 | func LookupCommandInfo(commandName string) CommandInfo { 50 | if ci, ok := commandInfos[commandName]; ok { 51 | return ci 52 | } 53 | return commandInfos[strings.ToUpper(commandName)] 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/errors.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | var ( 9 | // ErrRecordNotFound record not found error, happens when haven't find any matched data when looking up with a struct 10 | ErrRecordNotFound = errors.New("record not found") 11 | // ErrInvalidSQL invalid SQL error, happens when you passed invalid SQL 12 | ErrInvalidSQL = errors.New("invalid SQL") 13 | // ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback` 14 | ErrInvalidTransaction = errors.New("no valid transaction") 15 | // ErrCantStartTransaction can't start transaction when you are trying to start one with `Begin` 16 | ErrCantStartTransaction = errors.New("can't start transaction") 17 | // ErrUnaddressable unaddressable value 18 | ErrUnaddressable = errors.New("using unaddressable value") 19 | ) 20 | 21 | // Errors contains all happened errors 22 | type Errors []error 23 | 24 | // GetErrors gets all happened errors 25 | func (errs Errors) GetErrors() []error { 26 | return errs 27 | } 28 | 29 | // Add adds an error 30 | func (errs Errors) Add(newErrors ...error) Errors { 31 | for _, err := range newErrors { 32 | if errors, ok := err.(Errors); ok { 33 | errs = errs.Add(errors...) 34 | } else { 35 | ok = true 36 | for _, e := range errs { 37 | if err == e { 38 | ok = false 39 | } 40 | } 41 | if ok { 42 | errs = append(errs, err) 43 | } 44 | } 45 | } 46 | return errs 47 | } 48 | 49 | // Error format happened errors 50 | func (errs Errors) Error() string { 51 | var errors = []string{} 52 | for _, e := range errs { 53 | errors = append(errors, e.Error()) 54 | } 55 | return strings.Join(errors, "; ") 56 | } 57 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Before posting a bug report about a problem, please try to verify that it is a bug and that it has not been reported already, please apply corresponding GitHub labels to the issue, for feature requests, please apply `type:feature`. 2 | 3 | DON'T post usage related questions, ask in https://gitter.im/jinzhu/gorm or http://stackoverflow.com/questions/tagged/go-gorm, 4 | 5 | Please answer these questions before submitting your issue. Thanks! 6 | 7 | 8 | 9 | ### What version of Go are you using (`go version`)? 10 | 11 | 12 | ### Which database and its version are you using? 13 | 14 | 15 | ### What did you do? 16 | 17 | Please provide a complete runnable program to reproduce your issue. 18 | 19 | ```go 20 | package main 21 | 22 | import ( 23 | "github.com/jinzhu/gorm" 24 | _ "github.com/jinzhu/gorm/dialects/mssql" 25 | _ "github.com/jinzhu/gorm/dialects/mysql" 26 | _ "github.com/jinzhu/gorm/dialects/postgres" 27 | _ "github.com/jinzhu/gorm/dialects/sqlite" 28 | ) 29 | 30 | var db *gorm.DB 31 | 32 | func init() { 33 | var err error 34 | db, err = gorm.Open("sqlite3", "test.db") 35 | // Please use below username, password as your database's account for the script. 36 | // db, err = gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable") 37 | // db, err = gorm.Open("mysql", "gorm:gorm@/dbname?charset=utf8&parseTime=True") 38 | // db, err = gorm.Open("mssql", "sqlserver://gorm:LoremIpsum86@localhost:1433?database=gorm") 39 | if err != nil { 40 | panic(err) 41 | } 42 | db.LogMode(true) 43 | } 44 | 45 | func main() { 46 | // your code here 47 | 48 | if /* failure condition */ { 49 | fmt.Println("failed") 50 | } else { 51 | fmt.Println("success") 52 | } 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/README.md: -------------------------------------------------------------------------------- 1 | # GORM 2 | 3 | The fantastic ORM library for Golang, aims to be developer friendly. 4 | 5 | [![Join the chat at https://gitter.im/jinzhu/gorm](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | [![wercker status](https://app.wercker.com/status/0cb7bb1039e21b74f8274941428e0921/s/master "wercker status")](https://app.wercker.com/project/bykey/0cb7bb1039e21b74f8274941428e0921) 7 | [![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.svg)](https://godoc.org/github.com/jinzhu/gorm) 8 | 9 | ## Overview 10 | 11 | * Full-Featured ORM (almost) 12 | * Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism) 13 | * Callbacks (Before/After Create/Save/Update/Delete/Find) 14 | * Preloading (eager loading) 15 | * Transactions 16 | * Composite Primary Key 17 | * SQL Builder 18 | * Auto Migrations 19 | * Logger 20 | * Extendable, write Plugins based on GORM callbacks 21 | * Every feature comes with tests 22 | * Developer Friendly 23 | 24 | ## Getting Started 25 | 26 | * GORM Guides [jinzhu.github.com/gorm](http://jinzhu.github.io/gorm) 27 | 28 | ## Upgrading To V1.0 29 | 30 | * [CHANGELOG](http://jinzhu.github.io/gorm/changelog.html) 31 | 32 | ## Supporting the project 33 | 34 | [![http://patreon.com/jinzhu](http://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png)](http://patreon.com/jinzhu) 35 | 36 | ## Author 37 | 38 | **jinzhu** 39 | 40 | * 41 | * 42 | * 43 | 44 | ## Contributors 45 | 46 | https://github.com/jinzhu/gorm/graphs/contributors 47 | 48 | ## License 49 | 50 | Released under the [MIT License](https://github.com/jinzhu/gorm/blob/master/License). 51 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/redistest/testdb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Package redistest contains utilities for writing Redigo tests. 16 | package redistest 17 | 18 | import ( 19 | "errors" 20 | "time" 21 | 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | type testConn struct { 26 | redis.Conn 27 | } 28 | 29 | func (t testConn) Close() error { 30 | _, err := t.Conn.Do("SELECT", "9") 31 | if err != nil { 32 | return nil 33 | } 34 | _, err = t.Conn.Do("FLUSHDB") 35 | if err != nil { 36 | return err 37 | } 38 | return t.Conn.Close() 39 | } 40 | 41 | // Dial dials the local Redis server and selects database 9. To prevent 42 | // stomping on real data, DialTestDB fails if database 9 contains data. The 43 | // returned connection flushes database 9 on close. 44 | func Dial() (redis.Conn, error) { 45 | c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | _, err = c.Do("SELECT", "9") 51 | if err != nil { 52 | c.Close() 53 | return nil, err 54 | } 55 | 56 | n, err := redis.Int(c.Do("DBSIZE")) 57 | if err != nil { 58 | c.Close() 59 | return nil, err 60 | } 61 | 62 | if n != 0 { 63 | c.Close() 64 | return nil, errors.New("database #9 is not empty, test can not continue") 65 | } 66 | 67 | return testConn{c}, nil 68 | } 69 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/bytes_test.go: -------------------------------------------------------------------------------- 1 | package jsonparser 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | "unsafe" 7 | ) 8 | 9 | type ParseIntTest struct { 10 | in string 11 | out int64 12 | isErr bool 13 | } 14 | 15 | var parseIntTests = []ParseIntTest{ 16 | { 17 | in: "0", 18 | out: 0, 19 | }, 20 | { 21 | in: "1", 22 | out: 1, 23 | }, 24 | { 25 | in: "-1", 26 | out: -1, 27 | }, 28 | { 29 | in: "12345", 30 | out: 12345, 31 | }, 32 | { 33 | in: "-12345", 34 | out: -12345, 35 | }, 36 | { 37 | in: "9223372036854775807", 38 | out: 9223372036854775807, 39 | }, 40 | { 41 | in: "-9223372036854775808", 42 | out: -9223372036854775808, 43 | }, 44 | { 45 | in: "18446744073709551616", // = 2^64; integer overflow is not detected 46 | out: 0, 47 | }, 48 | 49 | { 50 | in: "", 51 | isErr: true, 52 | }, 53 | { 54 | in: "abc", 55 | isErr: true, 56 | }, 57 | { 58 | in: "12345x", 59 | isErr: true, 60 | }, 61 | { 62 | in: "123e5", 63 | isErr: true, 64 | }, 65 | { 66 | in: "9223372036854775807x", 67 | isErr: true, 68 | }, 69 | } 70 | 71 | func TestBytesParseInt(t *testing.T) { 72 | for _, test := range parseIntTests { 73 | out, ok := parseInt([]byte(test.in)) 74 | if ok != !test.isErr { 75 | t.Errorf("Test '%s' error return did not match expectation (obtained %t, expected %t)", test.in, !ok, test.isErr) 76 | } else if ok && out != test.out { 77 | t.Errorf("Test '%s' did not return the expected value (obtained %d, expected %d)", test.in, out, test.out) 78 | } 79 | } 80 | } 81 | 82 | func BenchmarkParseInt(b *testing.B) { 83 | bytes := []byte("123") 84 | for i := 0; i < b.N; i++ { 85 | parseInt(bytes) 86 | } 87 | } 88 | 89 | // Alternative implementation using unsafe and delegating to strconv.ParseInt 90 | func BenchmarkParseIntUnsafeSlower(b *testing.B) { 91 | bytes := []byte("123") 92 | for i := 0; i < b.N; i++ { 93 | strconv.ParseInt(*(*string)(unsafe.Pointer(&bytes)), 10, 64) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/bytes_unsafe_test.go: -------------------------------------------------------------------------------- 1 | // +build !appengine,!appenginevm 2 | 3 | package jsonparser 4 | 5 | import ( 6 | "reflect" 7 | "strings" 8 | "testing" 9 | "unsafe" 10 | ) 11 | 12 | var ( 13 | // short string/[]byte sequences, as the difference between these 14 | // three methods is a constant overhead 15 | benchmarkString = "0123456789x" 16 | benchmarkBytes = []byte("0123456789y") 17 | ) 18 | 19 | func bytesEqualStrSafe(abytes []byte, bstr string) bool { 20 | return bstr == string(abytes) 21 | } 22 | 23 | func bytesEqualStrUnsafeSlower(abytes *[]byte, bstr string) bool { 24 | aslicehdr := (*reflect.SliceHeader)(unsafe.Pointer(abytes)) 25 | astrhdr := reflect.StringHeader{Data: aslicehdr.Data, Len: aslicehdr.Len} 26 | return *(*string)(unsafe.Pointer(&astrhdr)) == bstr 27 | } 28 | 29 | func TestEqual(t *testing.T) { 30 | if !equalStr(&[]byte{}, "") { 31 | t.Errorf(`equalStr("", ""): expected true, obtained false`) 32 | return 33 | } 34 | 35 | longstr := strings.Repeat("a", 1000) 36 | for i := 0; i < len(longstr); i++ { 37 | s1, s2 := longstr[:i]+"1", longstr[:i]+"2" 38 | b1 := []byte(s1) 39 | 40 | if !equalStr(&b1, s1) { 41 | t.Errorf(`equalStr("a"*%d + "1", "a"*%d + "1"): expected true, obtained false`, i, i) 42 | break 43 | } 44 | if equalStr(&b1, s2) { 45 | t.Errorf(`equalStr("a"*%d + "1", "a"*%d + "2"): expected false, obtained true`, i, i) 46 | break 47 | } 48 | } 49 | } 50 | 51 | func BenchmarkEqualStr(b *testing.B) { 52 | for i := 0; i < b.N; i++ { 53 | equalStr(&benchmarkBytes, benchmarkString) 54 | } 55 | } 56 | 57 | // Alternative implementation without using unsafe 58 | func BenchmarkBytesEqualStrSafe(b *testing.B) { 59 | for i := 0; i < b.N; i++ { 60 | bytesEqualStrSafe(benchmarkBytes, benchmarkString) 61 | } 62 | } 63 | 64 | // Alternative implementation using unsafe, but that is slower than the current implementation 65 | func BenchmarkBytesEqualStrUnsafeSlower(b *testing.B) { 66 | for i := 0; i < b.N; i++ { 67 | bytesEqualStrUnsafeSlower(&benchmarkBytes, benchmarkString) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /hlog/filelog.go: -------------------------------------------------------------------------------- 1 | package hlog 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "time" 9 | "strconv" 10 | "github.com/hundredlee/wechat_pusher/config" 11 | "fmt" 12 | ) 13 | 14 | 15 | type fileLog struct { 16 | hlog 17 | logger *log.Logger 18 | } 19 | 20 | 21 | var ( 22 | hFileLog *fileLog 23 | conf *config.Config = config.Instance() 24 | ) 25 | 26 | func LogInstance () *fileLog { 27 | if hFileLog == nil{ 28 | InitLogFile(true,"") 29 | } 30 | return hFileLog 31 | } 32 | 33 | func InitLogFile(isOpen bool,fp string) { 34 | if !isOpen { 35 | hFileLog = &fileLog{} 36 | hFileLog.logger = nil 37 | hFileLog.isOpen = isOpen 38 | return 39 | } 40 | 41 | if fp == "" { 42 | wd:= os.Getenv("GOPATH") 43 | if wd =="" { 44 | file,_ := exec.LookPath(os.Args[0]) 45 | path := filepath.Dir(file) 46 | wd = path 47 | } 48 | if wd == "" { 49 | panic("GOPATH is not setted in env or can not get exe path.") 50 | } 51 | fp = fmt.Sprintf("%s/%s/",wd,conf.ConMap["Log.LOG_PATH"]) 52 | } 53 | hFileLog = NewFileLog(isOpen,fp) 54 | } 55 | 56 | func NewFileLog(isOpen bool,logPath string) *fileLog { 57 | year,month,day := time.Now().Date() 58 | logName := "log." + strconv.Itoa(year) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(day) 59 | err := os.MkdirAll(logPath,0755) 60 | if err != nil { 61 | panic("logPath error :"+logPath+"\n") 62 | } 63 | f, err := os.OpenFile(logPath+"/"+logName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) 64 | if err != nil { 65 | panic("log file open error : " + logPath + "/" + logName + "\n") 66 | } 67 | fFileLog := &fileLog{} 68 | fFileLog.logger = log.New(f, "", log.LstdFlags) 69 | fFileLog.isOpen = isOpen 70 | 71 | return fFileLog 72 | } 73 | 74 | 75 | func (this *fileLog) log(level string, str string) { 76 | if !this.isOpen { 77 | return 78 | } 79 | file, line := this.getCaller() 80 | this.logger.Printf("%s:%d: %s %s\n", file, line, level, str) 81 | } 82 | 83 | func (this *fileLog) LogError(str string) { 84 | this.log("[ERROR]", str) 85 | } 86 | 87 | func (this *fileLog) LogInfo(str string) { 88 | this.log("[INFO]", str) 89 | } 90 | 91 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/constantdelay_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestConstantDelayNext(t *testing.T) { 9 | tests := []struct { 10 | time string 11 | delay time.Duration 12 | expected string 13 | }{ 14 | // Simple cases 15 | {"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, 16 | {"Mon Jul 9 14:59 2012", 15 * time.Minute, "Mon Jul 9 15:14 2012"}, 17 | {"Mon Jul 9 14:59:59 2012", 15 * time.Minute, "Mon Jul 9 15:14:59 2012"}, 18 | 19 | // Wrap around hours 20 | {"Mon Jul 9 15:45 2012", 35 * time.Minute, "Mon Jul 9 16:20 2012"}, 21 | 22 | // Wrap around days 23 | {"Mon Jul 9 23:46 2012", 14 * time.Minute, "Tue Jul 10 00:00 2012"}, 24 | {"Mon Jul 9 23:45 2012", 35 * time.Minute, "Tue Jul 10 00:20 2012"}, 25 | {"Mon Jul 9 23:35:51 2012", 44*time.Minute + 24*time.Second, "Tue Jul 10 00:20:15 2012"}, 26 | {"Mon Jul 9 23:35:51 2012", 25*time.Hour + 44*time.Minute + 24*time.Second, "Thu Jul 11 01:20:15 2012"}, 27 | 28 | // Wrap around months 29 | {"Mon Jul 9 23:35 2012", 91*24*time.Hour + 25*time.Minute, "Thu Oct 9 00:00 2012"}, 30 | 31 | // Wrap around minute, hour, day, month, and year 32 | {"Mon Dec 31 23:59:45 2012", 15 * time.Second, "Tue Jan 1 00:00:00 2013"}, 33 | 34 | // Round to nearest second on the delay 35 | {"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, 36 | 37 | // Round up to 1 second if the duration is less. 38 | {"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:01 2012"}, 39 | 40 | // Round to nearest second when calculating the next time. 41 | {"Mon Jul 9 14:45:00.005 2012", 15 * time.Minute, "Mon Jul 9 15:00 2012"}, 42 | 43 | // Round to nearest second for both. 44 | {"Mon Jul 9 14:45:00.005 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, 45 | } 46 | 47 | for _, c := range tests { 48 | actual := Every(c.delay).Next(getTime(c.time)) 49 | expected := getTime(c.expected) 50 | if actual != expected { 51 | t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.delay, expected, actual) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/callback_delete.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | // Define callbacks for deleting 9 | func init() { 10 | DefaultCallback.Delete().Register("gorm:begin_transaction", beginTransactionCallback) 11 | DefaultCallback.Delete().Register("gorm:before_delete", beforeDeleteCallback) 12 | DefaultCallback.Delete().Register("gorm:delete", deleteCallback) 13 | DefaultCallback.Delete().Register("gorm:after_delete", afterDeleteCallback) 14 | DefaultCallback.Delete().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback) 15 | } 16 | 17 | // beforeDeleteCallback will invoke `BeforeDelete` method before deleting 18 | func beforeDeleteCallback(scope *Scope) { 19 | if scope.DB().HasBlockGlobalUpdate() && !scope.hasConditions() { 20 | scope.Err(errors.New("Missing WHERE clause while deleting")) 21 | return 22 | } 23 | if !scope.HasError() { 24 | scope.CallMethod("BeforeDelete") 25 | } 26 | } 27 | 28 | // deleteCallback used to delete data from database or set deleted_at to current time (when using with soft delete) 29 | func deleteCallback(scope *Scope) { 30 | if !scope.HasError() { 31 | var extraOption string 32 | if str, ok := scope.Get("gorm:delete_option"); ok { 33 | extraOption = fmt.Sprint(str) 34 | } 35 | 36 | deletedAtField, hasDeletedAtField := scope.FieldByName("DeletedAt") 37 | 38 | if !scope.Search.Unscoped && hasDeletedAtField { 39 | scope.Raw(fmt.Sprintf( 40 | "UPDATE %v SET %v=%v%v%v", 41 | scope.QuotedTableName(), 42 | scope.Quote(deletedAtField.DBName), 43 | scope.AddToVars(NowFunc()), 44 | addExtraSpaceIfExist(scope.CombinedConditionSql()), 45 | addExtraSpaceIfExist(extraOption), 46 | )).Exec() 47 | } else { 48 | scope.Raw(fmt.Sprintf( 49 | "DELETE FROM %v%v%v", 50 | scope.QuotedTableName(), 51 | addExtraSpaceIfExist(scope.CombinedConditionSql()), 52 | addExtraSpaceIfExist(extraOption), 53 | )).Exec() 54 | } 55 | } 56 | } 57 | 58 | // afterDeleteCallback will invoke `AfterDelete` method after deleting 59 | func afterDeleteCallback(scope *Scope) { 60 | if !scope.HasError() { 61 | scope.CallMethod("AfterDelete") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/README.markdown: -------------------------------------------------------------------------------- 1 | Redigo 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/garyburd/redigo.svg?branch=master)](https://travis-ci.org/garyburd/redigo) 5 | [![GoDoc](https://godoc.org/github.com/garyburd/redigo/redis?status.svg)](https://godoc.org/github.com/garyburd/redigo/redis) 6 | 7 | Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database. 8 | 9 | Features 10 | ------- 11 | 12 | * A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands. 13 | * [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions. 14 | * [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe). 15 | * [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool). 16 | * [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA. 17 | * [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies. 18 | 19 | Documentation 20 | ------------- 21 | 22 | - [API Reference](http://godoc.org/github.com/garyburd/redigo/redis) 23 | - [FAQ](https://github.com/garyburd/redigo/wiki/FAQ) 24 | 25 | Installation 26 | ------------ 27 | 28 | Install Redigo using the "go get" command: 29 | 30 | go get github.com/garyburd/redigo/redis 31 | 32 | The Go distribution is Redigo's only dependency. 33 | 34 | Related Projects 35 | ---------------- 36 | 37 | - [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo. 38 | - [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation. 39 | - [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo 40 | - [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo 41 | 42 | Contributing 43 | ------------ 44 | 45 | See [CONTRIBUTING.md](https://github.com/garyburd/redigo/blob/master/.github/CONTRIBUTING.md). 46 | 47 | License 48 | ------- 49 | 50 | Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). 51 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | /** 2 | * @Description A simple conf file reader 3 | * @Author HundredLee 4 | * @Email hundred9411@gmail.com 5 | */ 6 | package config 7 | 8 | import ( 9 | "bufio" 10 | "io" 11 | "os" 12 | "path/filepath" 13 | "regexp" 14 | "strconv" 15 | "strings" 16 | ) 17 | 18 | var ( 19 | conf *Config 20 | ) 21 | 22 | type IConfig interface { 23 | Init() *Config 24 | } 25 | 26 | type Config struct { 27 | ConMap map[string]interface{} 28 | Section string 29 | } 30 | 31 | func Instance() *Config { 32 | 33 | if conf != nil { 34 | return conf 35 | } 36 | 37 | workPath, err := os.Getwd() 38 | if err != nil { 39 | panic(err) 40 | } 41 | appConfigPath := filepath.Join(workPath, "", "config.conf") 42 | 43 | conf = Init(appConfigPath) 44 | 45 | return conf 46 | } 47 | 48 | func Init(file string) *Config { 49 | 50 | f, err := os.Open(file) 51 | if err != nil { 52 | panic(err) 53 | } 54 | //方法结束,close文件指针 55 | defer f.Close() 56 | 57 | conf := new(Config) 58 | conf.ConMap = make(map[string]interface{}) 59 | 60 | read := bufio.NewReader(f) 61 | 62 | for { 63 | line, _, err := read.ReadLine() 64 | if err != nil { 65 | if err == io.EOF { 66 | break 67 | } 68 | panic(err) 69 | } 70 | 71 | lineValue := strings.TrimSpace(string(line)) 72 | 73 | //去掉注释的配置项 74 | if strings.Index(lineValue, "#") == 0 { 75 | continue 76 | } 77 | 78 | //Section 79 | 80 | exp := regexp.MustCompile(`^\[(.*)]`) 81 | matchString := exp.FindStringSubmatch(lineValue) 82 | if length := len(matchString); length > 0 { 83 | conf.Section = matchString[1] 84 | continue 85 | } 86 | 87 | //没有读取到section 88 | if len(conf.Section) <= 0 { 89 | continue 90 | } 91 | 92 | conKeyPos := strings.Index(lineValue, "=") 93 | if conKeyPos < 0 { 94 | continue 95 | } 96 | conKey := strings.TrimSpace(lineValue[0:conKeyPos]) 97 | if len(conKey) <= 0 { 98 | continue 99 | } 100 | 101 | conValue := strings.TrimSpace(lineValue[conKeyPos+1:]) 102 | 103 | if conValue == "" { 104 | conf.ConMap[conf.Section+"."+conKey] = nil 105 | } else { 106 | if value, err := strconv.ParseInt(conValue, 10, 64); err == nil { 107 | conf.ConMap[conf.Section+"."+conKey] = int(value) 108 | } else { 109 | conf.ConMap[conf.Section+"."+conKey] = conValue 110 | } 111 | } 112 | 113 | } 114 | 115 | return conf 116 | } 117 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Bool", 4 | "type": "bool", 5 | "value": false, 6 | "context_default": "false", 7 | "parser": "strconv.ParseBool(f.Value.String())" 8 | }, 9 | { 10 | "name": "BoolT", 11 | "type": "bool", 12 | "value": false, 13 | "doctail": " that is true by default", 14 | "context_default": "false", 15 | "parser": "strconv.ParseBool(f.Value.String())" 16 | }, 17 | { 18 | "name": "Duration", 19 | "type": "time.Duration", 20 | "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", 21 | "context_default": "0", 22 | "parser": "time.ParseDuration(f.Value.String())" 23 | }, 24 | { 25 | "name": "Float64", 26 | "type": "float64", 27 | "context_default": "0", 28 | "parser": "strconv.ParseFloat(f.Value.String(), 64)" 29 | }, 30 | { 31 | "name": "Generic", 32 | "type": "Generic", 33 | "dest": false, 34 | "context_default": "nil", 35 | "context_type": "interface{}" 36 | }, 37 | { 38 | "name": "Int64", 39 | "type": "int64", 40 | "context_default": "0", 41 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" 42 | }, 43 | { 44 | "name": "Int", 45 | "type": "int", 46 | "context_default": "0", 47 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", 48 | "parser_cast": "int(parsed)" 49 | }, 50 | { 51 | "name": "IntSlice", 52 | "type": "*IntSlice", 53 | "dest": false, 54 | "context_default": "nil", 55 | "context_type": "[]int", 56 | "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" 57 | }, 58 | { 59 | "name": "Int64Slice", 60 | "type": "*Int64Slice", 61 | "dest": false, 62 | "context_default": "nil", 63 | "context_type": "[]int64", 64 | "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" 65 | }, 66 | { 67 | "name": "String", 68 | "type": "string", 69 | "context_default": "\"\"", 70 | "parser": "f.Value.String(), error(nil)" 71 | }, 72 | { 73 | "name": "StringSlice", 74 | "type": "*StringSlice", 75 | "dest": false, 76 | "context_default": "nil", 77 | "context_type": "[]string", 78 | "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" 79 | }, 80 | { 81 | "name": "Uint64", 82 | "type": "uint64", 83 | "context_default": "0", 84 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" 85 | }, 86 | { 87 | "name": "Uint", 88 | "type": "uint", 89 | "context_default": "0", 90 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", 91 | "parser_cast": "uint(parsed)" 92 | } 93 | ] 94 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/embedded_struct_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import "testing" 4 | 5 | type BasePost struct { 6 | Id int64 7 | Title string 8 | URL string 9 | } 10 | 11 | type Author struct { 12 | ID string 13 | Name string 14 | Email string 15 | } 16 | 17 | type HNPost struct { 18 | BasePost 19 | Author `gorm:"embedded_prefix:user_"` // Embedded struct 20 | Upvotes int32 21 | } 22 | 23 | type EngadgetPost struct { 24 | BasePost BasePost `gorm:"embedded"` 25 | Author Author `gorm:"embedded;embedded_prefix:author_"` // Embedded struct 26 | ImageUrl string 27 | } 28 | 29 | func TestPrefixColumnNameForEmbeddedStruct(t *testing.T) { 30 | dialect := DB.NewScope(&EngadgetPost{}).Dialect() 31 | engadgetPostScope := DB.NewScope(&EngadgetPost{}) 32 | if !dialect.HasColumn(engadgetPostScope.TableName(), "author_id") || !dialect.HasColumn(engadgetPostScope.TableName(), "author_name") || !dialect.HasColumn(engadgetPostScope.TableName(), "author_email") { 33 | t.Errorf("should has prefix for embedded columns") 34 | } 35 | 36 | if len(engadgetPostScope.PrimaryFields()) != 1 { 37 | t.Errorf("should have only one primary field with embedded struct, but got %v", len(engadgetPostScope.PrimaryFields())) 38 | } 39 | 40 | hnScope := DB.NewScope(&HNPost{}) 41 | if !dialect.HasColumn(hnScope.TableName(), "user_id") || !dialect.HasColumn(hnScope.TableName(), "user_name") || !dialect.HasColumn(hnScope.TableName(), "user_email") { 42 | t.Errorf("should has prefix for embedded columns") 43 | } 44 | } 45 | 46 | func TestSaveAndQueryEmbeddedStruct(t *testing.T) { 47 | DB.Save(&HNPost{BasePost: BasePost{Title: "news"}}) 48 | DB.Save(&HNPost{BasePost: BasePost{Title: "hn_news"}}) 49 | var news HNPost 50 | if err := DB.First(&news, "title = ?", "hn_news").Error; err != nil { 51 | t.Errorf("no error should happen when query with embedded struct, but got %v", err) 52 | } else if news.Title != "hn_news" { 53 | t.Errorf("embedded struct's value should be scanned correctly") 54 | } 55 | 56 | DB.Save(&EngadgetPost{BasePost: BasePost{Title: "engadget_news"}}) 57 | var egNews EngadgetPost 58 | if err := DB.First(&egNews, "title = ?", "engadget_news").Error; err != nil { 59 | t.Errorf("no error should happen when query with embedded struct, but got %v", err) 60 | } else if egNews.BasePost.Title != "engadget_news" { 61 | t.Errorf("embedded struct's value should be scanned correctly") 62 | } 63 | 64 | if DB.NewScope(&HNPost{}).PrimaryField() == nil { 65 | t.Errorf("primary key with embedded struct should works") 66 | } 67 | 68 | for _, field := range DB.NewScope(&HNPost{}).Fields() { 69 | if field.Name == "BasePost" { 70 | t.Errorf("scope Fields should not contain embedded struct") 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/callback_query.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | // Define callbacks for querying 10 | func init() { 11 | DefaultCallback.Query().Register("gorm:query", queryCallback) 12 | DefaultCallback.Query().Register("gorm:preload", preloadCallback) 13 | DefaultCallback.Query().Register("gorm:after_query", afterQueryCallback) 14 | } 15 | 16 | // queryCallback used to query data from database 17 | func queryCallback(scope *Scope) { 18 | defer scope.trace(NowFunc()) 19 | 20 | var ( 21 | isSlice, isPtr bool 22 | resultType reflect.Type 23 | results = scope.IndirectValue() 24 | ) 25 | 26 | if orderBy, ok := scope.Get("gorm:order_by_primary_key"); ok { 27 | if primaryField := scope.PrimaryField(); primaryField != nil { 28 | scope.Search.Order(fmt.Sprintf("%v.%v %v", scope.QuotedTableName(), scope.Quote(primaryField.DBName), orderBy)) 29 | } 30 | } 31 | 32 | if value, ok := scope.Get("gorm:query_destination"); ok { 33 | results = indirect(reflect.ValueOf(value)) 34 | } 35 | 36 | if kind := results.Kind(); kind == reflect.Slice { 37 | isSlice = true 38 | resultType = results.Type().Elem() 39 | results.Set(reflect.MakeSlice(results.Type(), 0, 0)) 40 | 41 | if resultType.Kind() == reflect.Ptr { 42 | isPtr = true 43 | resultType = resultType.Elem() 44 | } 45 | } else if kind != reflect.Struct { 46 | scope.Err(errors.New("unsupported destination, should be slice or struct")) 47 | return 48 | } 49 | 50 | scope.prepareQuerySQL() 51 | 52 | if !scope.HasError() { 53 | scope.db.RowsAffected = 0 54 | if str, ok := scope.Get("gorm:query_option"); ok { 55 | scope.SQL += addExtraSpaceIfExist(fmt.Sprint(str)) 56 | } 57 | 58 | if rows, err := scope.SQLDB().Query(scope.SQL, scope.SQLVars...); scope.Err(err) == nil { 59 | defer rows.Close() 60 | 61 | columns, _ := rows.Columns() 62 | for rows.Next() { 63 | scope.db.RowsAffected++ 64 | 65 | elem := results 66 | if isSlice { 67 | elem = reflect.New(resultType).Elem() 68 | } 69 | 70 | scope.scan(rows, columns, scope.New(elem.Addr().Interface()).Fields()) 71 | 72 | if isSlice { 73 | if isPtr { 74 | results.Set(reflect.Append(results, elem.Addr())) 75 | } else { 76 | results.Set(reflect.Append(results, elem)) 77 | } 78 | } 79 | } 80 | 81 | if err := rows.Err(); err != nil { 82 | scope.Err(err) 83 | } else if scope.db.RowsAffected == 0 && !isSlice { 84 | scope.Err(ErrRecordNotFound) 85 | } 86 | } 87 | } 88 | } 89 | 90 | // afterQueryCallback will invoke `AfterFind` method after querying 91 | func afterQueryCallback(scope *Scope) { 92 | if !scope.HasError() { 93 | scope.CallMethod("AfterFind") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/join_table_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | "time" 8 | 9 | "github.com/jinzhu/gorm" 10 | ) 11 | 12 | type Person struct { 13 | Id int 14 | Name string 15 | Addresses []*Address `gorm:"many2many:person_addresses;"` 16 | } 17 | 18 | type PersonAddress struct { 19 | gorm.JoinTableHandler 20 | PersonID int 21 | AddressID int 22 | DeletedAt *time.Time 23 | CreatedAt time.Time 24 | } 25 | 26 | func (*PersonAddress) Add(handler gorm.JoinTableHandlerInterface, db *gorm.DB, foreignValue interface{}, associationValue interface{}) error { 27 | foreignPrimaryKey, _ := strconv.Atoi(fmt.Sprint(db.NewScope(foreignValue).PrimaryKeyValue())) 28 | associationPrimaryKey, _ := strconv.Atoi(fmt.Sprint(db.NewScope(associationValue).PrimaryKeyValue())) 29 | if result := db.Unscoped().Model(&PersonAddress{}).Where(map[string]interface{}{ 30 | "person_id": foreignPrimaryKey, 31 | "address_id": associationPrimaryKey, 32 | }).Update(map[string]interface{}{ 33 | "person_id": foreignPrimaryKey, 34 | "address_id": associationPrimaryKey, 35 | "deleted_at": gorm.Expr("NULL"), 36 | }).RowsAffected; result == 0 { 37 | return db.Create(&PersonAddress{ 38 | PersonID: foreignPrimaryKey, 39 | AddressID: associationPrimaryKey, 40 | }).Error 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func (*PersonAddress) Delete(handler gorm.JoinTableHandlerInterface, db *gorm.DB, sources ...interface{}) error { 47 | return db.Delete(&PersonAddress{}).Error 48 | } 49 | 50 | func (pa *PersonAddress) JoinWith(handler gorm.JoinTableHandlerInterface, db *gorm.DB, source interface{}) *gorm.DB { 51 | table := pa.Table(db) 52 | return db.Joins("INNER JOIN person_addresses ON person_addresses.address_id = addresses.id").Where(fmt.Sprintf("%v.deleted_at IS NULL OR %v.deleted_at <= '0001-01-02'", table, table)) 53 | } 54 | 55 | func TestJoinTable(t *testing.T) { 56 | DB.Exec("drop table person_addresses;") 57 | DB.AutoMigrate(&Person{}) 58 | DB.SetJoinTableHandler(&Person{}, "Addresses", &PersonAddress{}) 59 | 60 | address1 := &Address{Address1: "address 1"} 61 | address2 := &Address{Address1: "address 2"} 62 | person := &Person{Name: "person", Addresses: []*Address{address1, address2}} 63 | DB.Save(person) 64 | 65 | DB.Model(person).Association("Addresses").Delete(address1) 66 | 67 | if DB.Find(&[]PersonAddress{}, "person_id = ?", person.Id).RowsAffected != 1 { 68 | t.Errorf("Should found one address") 69 | } 70 | 71 | if DB.Model(person).Association("Addresses").Count() != 1 { 72 | t.Errorf("Should found one address") 73 | } 74 | 75 | if DB.Unscoped().Find(&[]PersonAddress{}, "person_id = ?", person.Id).RowsAffected != 2 { 76 | t.Errorf("Found two addresses with Unscoped") 77 | } 78 | 79 | if DB.Model(person).Association("Addresses").Clear(); DB.Model(person).Association("Addresses").Count() != 0 { 80 | t.Errorf("Should deleted all addresses") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/zpop_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/garyburd/redigo/redis" 21 | ) 22 | 23 | // zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. 24 | func zpop(c redis.Conn, key string) (result string, err error) { 25 | 26 | defer func() { 27 | // Return connection to normal state on error. 28 | if err != nil { 29 | c.Do("DISCARD") 30 | } 31 | }() 32 | 33 | // Loop until transaction is successful. 34 | for { 35 | if _, err := c.Do("WATCH", key); err != nil { 36 | return "", err 37 | } 38 | 39 | members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) 40 | if err != nil { 41 | return "", err 42 | } 43 | if len(members) != 1 { 44 | return "", redis.ErrNil 45 | } 46 | 47 | c.Send("MULTI") 48 | c.Send("ZREM", key, members[0]) 49 | queued, err := c.Do("EXEC") 50 | if err != nil { 51 | return "", err 52 | } 53 | 54 | if queued != nil { 55 | result = members[0] 56 | break 57 | } 58 | } 59 | 60 | return result, nil 61 | } 62 | 63 | // zpopScript pops a value from a ZSET. 64 | var zpopScript = redis.NewScript(1, ` 65 | local r = redis.call('ZRANGE', KEYS[1], 0, 0) 66 | if r ~= nil then 67 | r = r[1] 68 | redis.call('ZREM', KEYS[1], r) 69 | end 70 | return r 71 | `) 72 | 73 | // This example implements ZPOP as described at 74 | // http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting. 75 | func Example_zpop() { 76 | c, err := dial() 77 | if err != nil { 78 | fmt.Println(err) 79 | return 80 | } 81 | defer c.Close() 82 | 83 | // Add test data using a pipeline. 84 | 85 | for i, member := range []string{"red", "blue", "green"} { 86 | c.Send("ZADD", "zset", i, member) 87 | } 88 | if _, err := c.Do(""); err != nil { 89 | fmt.Println(err) 90 | return 91 | } 92 | 93 | // Pop using WATCH/MULTI/EXEC 94 | 95 | v, err := zpop(c, "zset") 96 | if err != nil { 97 | fmt.Println(err) 98 | return 99 | } 100 | fmt.Println(v) 101 | 102 | // Pop using a script. 103 | 104 | v, err = redis.String(zpopScript.Do(c, "zset")) 105 | if err != nil { 106 | fmt.Println(err) 107 | return 108 | } 109 | fmt.Println(v) 110 | 111 | // Output: 112 | // red 113 | // blue 114 | } 115 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/altsrc/yaml_file_loader.go: -------------------------------------------------------------------------------- 1 | // Disabling building of yaml support in cases where golang is 1.0 or 1.1 2 | // as the encoding library is not implemented or supported. 3 | 4 | // +build go1.2 5 | 6 | package altsrc 7 | 8 | import ( 9 | "fmt" 10 | "io/ioutil" 11 | "net/http" 12 | "net/url" 13 | "os" 14 | "runtime" 15 | "strings" 16 | 17 | "gopkg.in/urfave/cli.v1" 18 | 19 | "gopkg.in/yaml.v2" 20 | ) 21 | 22 | type yamlSourceContext struct { 23 | FilePath string 24 | } 25 | 26 | // NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. 27 | func NewYamlSourceFromFile(file string) (InputSourceContext, error) { 28 | ysc := &yamlSourceContext{FilePath: file} 29 | var results map[interface{}]interface{} 30 | err := readCommandYaml(ysc.FilePath, &results) 31 | if err != nil { 32 | return nil, fmt.Errorf("Unable to load Yaml file '%s': inner error: \n'%v'", ysc.FilePath, err.Error()) 33 | } 34 | 35 | return &MapInputSource{valueMap: results}, nil 36 | } 37 | 38 | // NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context. 39 | func NewYamlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { 40 | return func(context *cli.Context) (InputSourceContext, error) { 41 | filePath := context.String(flagFileName) 42 | return NewYamlSourceFromFile(filePath) 43 | } 44 | } 45 | 46 | func readCommandYaml(filePath string, container interface{}) (err error) { 47 | b, err := loadDataFrom(filePath) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | err = yaml.Unmarshal(b, container) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | err = nil 58 | return 59 | } 60 | 61 | func loadDataFrom(filePath string) ([]byte, error) { 62 | u, err := url.Parse(filePath) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | if u.Host != "" { // i have a host, now do i support the scheme? 68 | switch u.Scheme { 69 | case "http", "https": 70 | res, err := http.Get(filePath) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return ioutil.ReadAll(res.Body) 75 | default: 76 | return nil, fmt.Errorf("scheme of %s is unsupported", filePath) 77 | } 78 | } else if u.Path != "" { // i dont have a host, but I have a path. I am a local file. 79 | if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { 80 | return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) 81 | } 82 | return ioutil.ReadFile(filePath) 83 | } else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") { 84 | // on Windows systems u.Path is always empty, so we need to check the string directly. 85 | if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { 86 | return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) 87 | } 88 | return ioutil.ReadFile(filePath) 89 | } else { 90 | return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/errors_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestHandleExitCoder_nil(t *testing.T) { 11 | exitCode := 0 12 | called := false 13 | 14 | OsExiter = func(rc int) { 15 | if !called { 16 | exitCode = rc 17 | called = true 18 | } 19 | } 20 | 21 | defer func() { OsExiter = fakeOsExiter }() 22 | 23 | HandleExitCoder(nil) 24 | 25 | expect(t, exitCode, 0) 26 | expect(t, called, false) 27 | } 28 | 29 | func TestHandleExitCoder_ExitCoder(t *testing.T) { 30 | exitCode := 0 31 | called := false 32 | 33 | OsExiter = func(rc int) { 34 | if !called { 35 | exitCode = rc 36 | called = true 37 | } 38 | } 39 | 40 | defer func() { OsExiter = fakeOsExiter }() 41 | 42 | HandleExitCoder(NewExitError("galactic perimeter breach", 9)) 43 | 44 | expect(t, exitCode, 9) 45 | expect(t, called, true) 46 | } 47 | 48 | func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) { 49 | exitCode := 0 50 | called := false 51 | 52 | OsExiter = func(rc int) { 53 | if !called { 54 | exitCode = rc 55 | called = true 56 | } 57 | } 58 | 59 | defer func() { OsExiter = fakeOsExiter }() 60 | 61 | exitErr := NewExitError("galactic perimeter breach", 9) 62 | exitErr2 := NewExitError("last ExitCoder", 11) 63 | err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2) 64 | HandleExitCoder(err) 65 | 66 | expect(t, exitCode, 11) 67 | expect(t, called, true) 68 | } 69 | 70 | // make a stub to not import pkg/errors 71 | type ErrorWithFormat struct { 72 | error 73 | } 74 | 75 | func NewErrorWithFormat(m string) *ErrorWithFormat { 76 | return &ErrorWithFormat{error: errors.New(m)} 77 | } 78 | 79 | func (f *ErrorWithFormat) Format(s fmt.State, verb rune) { 80 | fmt.Fprintf(s, "This the format: %v", f.error) 81 | } 82 | 83 | func TestHandleExitCoder_ErrorWithFormat(t *testing.T) { 84 | called := false 85 | 86 | OsExiter = func(rc int) { 87 | if !called { 88 | called = true 89 | } 90 | } 91 | ErrWriter = &bytes.Buffer{} 92 | 93 | defer func() { 94 | OsExiter = fakeOsExiter 95 | ErrWriter = fakeErrWriter 96 | }() 97 | 98 | err := NewExitError(NewErrorWithFormat("I am formatted"), 1) 99 | HandleExitCoder(err) 100 | 101 | expect(t, called, true) 102 | expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n") 103 | } 104 | 105 | func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) { 106 | called := false 107 | 108 | OsExiter = func(rc int) { 109 | if !called { 110 | called = true 111 | } 112 | } 113 | ErrWriter = &bytes.Buffer{} 114 | 115 | defer func() { OsExiter = fakeOsExiter }() 116 | 117 | err := NewMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2")) 118 | HandleExitCoder(err) 119 | 120 | expect(t, called, true) 121 | expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n") 122 | } 123 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/delete_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestDelete(t *testing.T) { 9 | user1, user2 := User{Name: "delete1"}, User{Name: "delete2"} 10 | DB.Save(&user1) 11 | DB.Save(&user2) 12 | 13 | if err := DB.Delete(&user1).Error; err != nil { 14 | t.Errorf("No error should happen when delete a record, err=%s", err) 15 | } 16 | 17 | if !DB.Where("name = ?", user1.Name).First(&User{}).RecordNotFound() { 18 | t.Errorf("User can't be found after delete") 19 | } 20 | 21 | if DB.Where("name = ?", user2.Name).First(&User{}).RecordNotFound() { 22 | t.Errorf("Other users that not deleted should be found-able") 23 | } 24 | } 25 | 26 | func TestInlineDelete(t *testing.T) { 27 | user1, user2 := User{Name: "inline_delete1"}, User{Name: "inline_delete2"} 28 | DB.Save(&user1) 29 | DB.Save(&user2) 30 | 31 | if DB.Delete(&User{}, user1.Id).Error != nil { 32 | t.Errorf("No error should happen when delete a record") 33 | } else if !DB.Where("name = ?", user1.Name).First(&User{}).RecordNotFound() { 34 | t.Errorf("User can't be found after delete") 35 | } 36 | 37 | if err := DB.Delete(&User{}, "name = ?", user2.Name).Error; err != nil { 38 | t.Errorf("No error should happen when delete a record, err=%s", err) 39 | } else if !DB.Where("name = ?", user2.Name).First(&User{}).RecordNotFound() { 40 | t.Errorf("User can't be found after delete") 41 | } 42 | } 43 | 44 | func TestSoftDelete(t *testing.T) { 45 | type User struct { 46 | Id int64 47 | Name string 48 | DeletedAt *time.Time 49 | } 50 | DB.AutoMigrate(&User{}) 51 | 52 | user := User{Name: "soft_delete"} 53 | DB.Save(&user) 54 | DB.Delete(&user) 55 | 56 | if DB.First(&User{}, "name = ?", user.Name).Error == nil { 57 | t.Errorf("Can't find a soft deleted record") 58 | } 59 | 60 | if err := DB.Unscoped().First(&User{}, "name = ?", user.Name).Error; err != nil { 61 | t.Errorf("Should be able to find soft deleted record with Unscoped, but err=%s", err) 62 | } 63 | 64 | DB.Unscoped().Delete(&user) 65 | if !DB.Unscoped().First(&User{}, "name = ?", user.Name).RecordNotFound() { 66 | t.Errorf("Can't find permanently deleted record") 67 | } 68 | } 69 | 70 | func TestSoftDeleteWithCustomizedDeletedAtColumnName(t *testing.T) { 71 | creditCard := CreditCard{Number: "411111111234567"} 72 | DB.Save(&creditCard) 73 | DB.Delete(&creditCard) 74 | 75 | if deletedAtField, ok := DB.NewScope(&CreditCard{}).FieldByName("DeletedAt"); !ok || deletedAtField.DBName != "deleted_time" { 76 | t.Errorf("CreditCard's DeletedAt's column name should be `deleted_time`") 77 | } 78 | 79 | if DB.First(&CreditCard{}, "number = ?", creditCard.Number).Error == nil { 80 | t.Errorf("Can't find a soft deleted record") 81 | } 82 | 83 | if err := DB.Unscoped().First(&CreditCard{}, "number = ?", creditCard.Number).Error; err != nil { 84 | t.Errorf("Should be able to find soft deleted record with Unscoped, but err=%s", err) 85 | } 86 | 87 | DB.Unscoped().Delete(&creditCard) 88 | if !DB.Unscoped().First(&CreditCard{}, "number = ?", creditCard.Number).RecordNotFound() { 89 | t.Errorf("Can't find permanently deleted record") 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/pointer_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import "testing" 4 | 5 | type PointerStruct struct { 6 | ID int64 7 | Name *string 8 | Num *int 9 | } 10 | 11 | type NormalStruct struct { 12 | ID int64 13 | Name string 14 | Num int 15 | } 16 | 17 | func TestPointerFields(t *testing.T) { 18 | DB.DropTable(&PointerStruct{}) 19 | DB.AutoMigrate(&PointerStruct{}) 20 | var name = "pointer struct 1" 21 | var num = 100 22 | pointerStruct := PointerStruct{Name: &name, Num: &num} 23 | if DB.Create(&pointerStruct).Error != nil { 24 | t.Errorf("Failed to save pointer struct") 25 | } 26 | 27 | var pointerStructResult PointerStruct 28 | if err := DB.First(&pointerStructResult, "id = ?", pointerStruct.ID).Error; err != nil || *pointerStructResult.Name != name || *pointerStructResult.Num != num { 29 | t.Errorf("Failed to query saved pointer struct") 30 | } 31 | 32 | var tableName = DB.NewScope(&PointerStruct{}).TableName() 33 | 34 | var normalStruct NormalStruct 35 | DB.Table(tableName).First(&normalStruct) 36 | if normalStruct.Name != name || normalStruct.Num != num { 37 | t.Errorf("Failed to query saved Normal struct") 38 | } 39 | 40 | var nilPointerStruct = PointerStruct{} 41 | if err := DB.Create(&nilPointerStruct).Error; err != nil { 42 | t.Error("Failed to save nil pointer struct", err) 43 | } 44 | 45 | var pointerStruct2 PointerStruct 46 | if err := DB.First(&pointerStruct2, "id = ?", nilPointerStruct.ID).Error; err != nil { 47 | t.Error("Failed to query saved nil pointer struct", err) 48 | } 49 | 50 | var normalStruct2 NormalStruct 51 | if err := DB.Table(tableName).First(&normalStruct2, "id = ?", nilPointerStruct.ID).Error; err != nil { 52 | t.Error("Failed to query saved nil pointer struct", err) 53 | } 54 | 55 | var partialNilPointerStruct1 = PointerStruct{Num: &num} 56 | if err := DB.Create(&partialNilPointerStruct1).Error; err != nil { 57 | t.Error("Failed to save partial nil pointer struct", err) 58 | } 59 | 60 | var pointerStruct3 PointerStruct 61 | if err := DB.First(&pointerStruct3, "id = ?", partialNilPointerStruct1.ID).Error; err != nil || *pointerStruct3.Num != num { 62 | t.Error("Failed to query saved partial nil pointer struct", err) 63 | } 64 | 65 | var normalStruct3 NormalStruct 66 | if err := DB.Table(tableName).First(&normalStruct3, "id = ?", partialNilPointerStruct1.ID).Error; err != nil || normalStruct3.Num != num { 67 | t.Error("Failed to query saved partial pointer struct", err) 68 | } 69 | 70 | var partialNilPointerStruct2 = PointerStruct{Name: &name} 71 | if err := DB.Create(&partialNilPointerStruct2).Error; err != nil { 72 | t.Error("Failed to save partial nil pointer struct", err) 73 | } 74 | 75 | var pointerStruct4 PointerStruct 76 | if err := DB.First(&pointerStruct4, "id = ?", partialNilPointerStruct2.ID).Error; err != nil || *pointerStruct4.Name != name { 77 | t.Error("Failed to query saved partial nil pointer struct", err) 78 | } 79 | 80 | var normalStruct4 NormalStruct 81 | if err := DB.Table(tableName).First(&normalStruct4, "id = ?", partialNilPointerStruct2.ID).Error; err != nil || normalStruct4.Name != name { 82 | t.Error("Failed to query saved partial pointer struct", err) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | "time" 22 | 23 | "github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | var ( 27 | // These variables are declared at package level to remove distracting 28 | // details from the examples. 29 | c redis.Conn 30 | reply interface{} 31 | err error 32 | ) 33 | 34 | func ExampleScript() { 35 | // Initialize a package-level variable with a script. 36 | var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`) 37 | 38 | // In a function, use the script Do method to evaluate the script. The Do 39 | // method optimistically uses the EVALSHA command. If the script is not 40 | // loaded, then the Do method falls back to the EVAL command. 41 | reply, err = getScript.Do(c, "foo") 42 | } 43 | 44 | func TestScript(t *testing.T) { 45 | c, err := redis.DialDefaultServer() 46 | if err != nil { 47 | t.Fatalf("error connection to database, %v", err) 48 | } 49 | defer c.Close() 50 | 51 | // To test fall back in Do, we make script unique by adding comment with current time. 52 | script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano()) 53 | s := redis.NewScript(2, script) 54 | reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")} 55 | 56 | v, err := s.Do(c, "key1", "key2", "arg1", "arg2") 57 | if err != nil { 58 | t.Errorf("s.Do(c, ...) returned %v", err) 59 | } 60 | 61 | if !reflect.DeepEqual(v, reply) { 62 | t.Errorf("s.Do(c, ..); = %v, want %v", v, reply) 63 | } 64 | 65 | err = s.Load(c) 66 | if err != nil { 67 | t.Errorf("s.Load(c) returned %v", err) 68 | } 69 | 70 | err = s.SendHash(c, "key1", "key2", "arg1", "arg2") 71 | if err != nil { 72 | t.Errorf("s.SendHash(c, ...) returned %v", err) 73 | } 74 | 75 | err = c.Flush() 76 | if err != nil { 77 | t.Errorf("c.Flush() returned %v", err) 78 | } 79 | 80 | v, err = c.Receive() 81 | if !reflect.DeepEqual(v, reply) { 82 | t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply) 83 | } 84 | 85 | err = s.Send(c, "key1", "key2", "arg1", "arg2") 86 | if err != nil { 87 | t.Errorf("s.Send(c, ...) returned %v", err) 88 | } 89 | 90 | err = c.Flush() 91 | if err != nil { 92 | t.Errorf("c.Flush() returned %v", err) 93 | } 94 | 95 | v, err = c.Receive() 96 | if !reflect.DeepEqual(v, reply) { 97 | t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/errors.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // OsExiter is the function used when the app exits. If not set defaults to os.Exit. 11 | var OsExiter = os.Exit 12 | 13 | // ErrWriter is used to write errors to the user. This can be anything 14 | // implementing the io.Writer interface and defaults to os.Stderr. 15 | var ErrWriter io.Writer = os.Stderr 16 | 17 | // MultiError is an error that wraps multiple errors. 18 | type MultiError struct { 19 | Errors []error 20 | } 21 | 22 | // NewMultiError creates a new MultiError. Pass in one or more errors. 23 | func NewMultiError(err ...error) MultiError { 24 | return MultiError{Errors: err} 25 | } 26 | 27 | // Error implements the error interface. 28 | func (m MultiError) Error() string { 29 | errs := make([]string, len(m.Errors)) 30 | for i, err := range m.Errors { 31 | errs[i] = err.Error() 32 | } 33 | 34 | return strings.Join(errs, "\n") 35 | } 36 | 37 | type ErrorFormatter interface { 38 | Format(s fmt.State, verb rune) 39 | } 40 | 41 | // ExitCoder is the interface checked by `App` and `Command` for a custom exit 42 | // code 43 | type ExitCoder interface { 44 | error 45 | ExitCode() int 46 | } 47 | 48 | // ExitError fulfills both the builtin `error` interface and `ExitCoder` 49 | type ExitError struct { 50 | exitCode int 51 | message interface{} 52 | } 53 | 54 | // NewExitError makes a new *ExitError 55 | func NewExitError(message interface{}, exitCode int) *ExitError { 56 | return &ExitError{ 57 | exitCode: exitCode, 58 | message: message, 59 | } 60 | } 61 | 62 | // Error returns the string message, fulfilling the interface required by 63 | // `error` 64 | func (ee *ExitError) Error() string { 65 | return fmt.Sprintf("%v", ee.message) 66 | } 67 | 68 | // ExitCode returns the exit code, fulfilling the interface required by 69 | // `ExitCoder` 70 | func (ee *ExitError) ExitCode() int { 71 | return ee.exitCode 72 | } 73 | 74 | // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if 75 | // so prints the error to stderr (if it is non-empty) and calls OsExiter with the 76 | // given exit code. If the given error is a MultiError, then this func is 77 | // called on all members of the Errors slice and calls OsExiter with the last exit code. 78 | func HandleExitCoder(err error) { 79 | if err == nil { 80 | return 81 | } 82 | 83 | if exitErr, ok := err.(ExitCoder); ok { 84 | if err.Error() != "" { 85 | if _, ok := exitErr.(ErrorFormatter); ok { 86 | fmt.Fprintf(ErrWriter, "%+v\n", err) 87 | } else { 88 | fmt.Fprintln(ErrWriter, err) 89 | } 90 | } 91 | OsExiter(exitErr.ExitCode()) 92 | return 93 | } 94 | 95 | if multiErr, ok := err.(MultiError); ok { 96 | code := handleMultiError(multiErr) 97 | OsExiter(code) 98 | return 99 | } 100 | } 101 | 102 | func handleMultiError(multiErr MultiError) int { 103 | code := 1 104 | for _, merr := range multiErr.Errors { 105 | if multiErr2, ok := merr.(MultiError); ok { 106 | code = handleMultiError(multiErr2) 107 | } else { 108 | fmt.Fprintln(ErrWriter, merr) 109 | if exitErr, ok := merr.(ExitCoder); ok { 110 | code = exitErr.ExitCode() 111 | } 112 | } 113 | } 114 | return code 115 | } 116 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialect_sqlite3.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | type sqlite3 struct { 11 | commonDialect 12 | } 13 | 14 | func init() { 15 | RegisterDialect("sqlite3", &sqlite3{}) 16 | } 17 | 18 | func (sqlite3) GetName() string { 19 | return "sqlite3" 20 | } 21 | 22 | // Get Data Type for Sqlite Dialect 23 | func (s *sqlite3) DataTypeOf(field *StructField) string { 24 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s) 25 | 26 | if sqlType == "" { 27 | switch dataValue.Kind() { 28 | case reflect.Bool: 29 | sqlType = "bool" 30 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr: 31 | if field.IsPrimaryKey { 32 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 33 | sqlType = "integer primary key autoincrement" 34 | } else { 35 | sqlType = "integer" 36 | } 37 | case reflect.Int64, reflect.Uint64: 38 | if field.IsPrimaryKey { 39 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 40 | sqlType = "integer primary key autoincrement" 41 | } else { 42 | sqlType = "bigint" 43 | } 44 | case reflect.Float32, reflect.Float64: 45 | sqlType = "real" 46 | case reflect.String: 47 | if size > 0 && size < 65532 { 48 | sqlType = fmt.Sprintf("varchar(%d)", size) 49 | } else { 50 | sqlType = "text" 51 | } 52 | case reflect.Struct: 53 | if _, ok := dataValue.Interface().(time.Time); ok { 54 | sqlType = "datetime" 55 | } 56 | default: 57 | if IsByteArrayOrSlice(dataValue) { 58 | sqlType = "blob" 59 | } 60 | } 61 | } 62 | 63 | if sqlType == "" { 64 | panic(fmt.Sprintf("invalid sql type %s (%s) for sqlite3", dataValue.Type().Name(), dataValue.Kind().String())) 65 | } 66 | 67 | if strings.TrimSpace(additionalType) == "" { 68 | return sqlType 69 | } 70 | return fmt.Sprintf("%v %v", sqlType, additionalType) 71 | } 72 | 73 | func (s sqlite3) HasIndex(tableName string, indexName string) bool { 74 | var count int 75 | s.db.QueryRow(fmt.Sprintf("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND sql LIKE '%%INDEX %v ON%%'", indexName), tableName).Scan(&count) 76 | return count > 0 77 | } 78 | 79 | func (s sqlite3) HasTable(tableName string) bool { 80 | var count int 81 | s.db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", tableName).Scan(&count) 82 | return count > 0 83 | } 84 | 85 | func (s sqlite3) HasColumn(tableName string, columnName string) bool { 86 | var count int 87 | s.db.QueryRow(fmt.Sprintf("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND (sql LIKE '%%\"%v\" %%' OR sql LIKE '%%%v %%');\n", columnName, columnName), tableName).Scan(&count) 88 | return count > 0 89 | } 90 | 91 | func (s sqlite3) CurrentDatabase() (name string) { 92 | var ( 93 | ifaces = make([]interface{}, 3) 94 | pointers = make([]*string, 3) 95 | i int 96 | ) 97 | for i = 0; i < 3; i++ { 98 | ifaces[i] = &pointers[i] 99 | } 100 | if err := s.db.QueryRow("PRAGMA database_list").Scan(ifaces...); err != nil { 101 | return 102 | } 103 | if pointers[1] != nil { 104 | name = *pointers[1] 105 | } 106 | return 107 | } 108 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "crypto/sha1" 19 | "encoding/hex" 20 | "io" 21 | "strings" 22 | ) 23 | 24 | // Script encapsulates the source, hash and key count for a Lua script. See 25 | // http://redis.io/commands/eval for information on scripts in Redis. 26 | type Script struct { 27 | keyCount int 28 | src string 29 | hash string 30 | } 31 | 32 | // NewScript returns a new script object. If keyCount is greater than or equal 33 | // to zero, then the count is automatically inserted in the EVAL command 34 | // argument list. If keyCount is less than zero, then the application supplies 35 | // the count as the first value in the keysAndArgs argument to the Do, Send and 36 | // SendHash methods. 37 | func NewScript(keyCount int, src string) *Script { 38 | h := sha1.New() 39 | io.WriteString(h, src) 40 | return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))} 41 | } 42 | 43 | func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} { 44 | var args []interface{} 45 | if s.keyCount < 0 { 46 | args = make([]interface{}, 1+len(keysAndArgs)) 47 | args[0] = spec 48 | copy(args[1:], keysAndArgs) 49 | } else { 50 | args = make([]interface{}, 2+len(keysAndArgs)) 51 | args[0] = spec 52 | args[1] = s.keyCount 53 | copy(args[2:], keysAndArgs) 54 | } 55 | return args 56 | } 57 | 58 | // Hash returns the script hash. 59 | func (s *Script) Hash() string { 60 | return s.hash 61 | } 62 | 63 | // Do evaluates the script. Under the covers, Do optimistically evaluates the 64 | // script using the EVALSHA command. If the command fails because the script is 65 | // not loaded, then Do evaluates the script using the EVAL command (thus 66 | // causing the script to load). 67 | func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) { 68 | v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...) 69 | if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") { 70 | v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...) 71 | } 72 | return v, err 73 | } 74 | 75 | // SendHash evaluates the script without waiting for the reply. The script is 76 | // evaluated with the EVALSHA command. The application must ensure that the 77 | // script is loaded by a previous call to Send, Do or Load methods. 78 | func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error { 79 | return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...) 80 | } 81 | 82 | // Send evaluates the script without waiting for the reply. 83 | func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error { 84 | return c.Send("EVAL", s.args(s.src, keysAndArgs)...) 85 | } 86 | 87 | // Load loads the script without evaluating it. 88 | func (s *Script) Load(c Conn) error { 89 | _, err := c.Do("SCRIPT", "LOAD", s.src) 90 | return err 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "log" 21 | ) 22 | 23 | // NewLoggingConn returns a logging wrapper around a connection. 24 | func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn { 25 | if prefix != "" { 26 | prefix = prefix + "." 27 | } 28 | return &loggingConn{conn, logger, prefix} 29 | } 30 | 31 | type loggingConn struct { 32 | Conn 33 | logger *log.Logger 34 | prefix string 35 | } 36 | 37 | func (c *loggingConn) Close() error { 38 | err := c.Conn.Close() 39 | var buf bytes.Buffer 40 | fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err) 41 | c.logger.Output(2, buf.String()) 42 | return err 43 | } 44 | 45 | func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) { 46 | const chop = 32 47 | switch v := v.(type) { 48 | case []byte: 49 | if len(v) > chop { 50 | fmt.Fprintf(buf, "%q...", v[:chop]) 51 | } else { 52 | fmt.Fprintf(buf, "%q", v) 53 | } 54 | case string: 55 | if len(v) > chop { 56 | fmt.Fprintf(buf, "%q...", v[:chop]) 57 | } else { 58 | fmt.Fprintf(buf, "%q", v) 59 | } 60 | case []interface{}: 61 | if len(v) == 0 { 62 | buf.WriteString("[]") 63 | } else { 64 | sep := "[" 65 | fin := "]" 66 | if len(v) > chop { 67 | v = v[:chop] 68 | fin = "...]" 69 | } 70 | for _, vv := range v { 71 | buf.WriteString(sep) 72 | c.printValue(buf, vv) 73 | sep = ", " 74 | } 75 | buf.WriteString(fin) 76 | } 77 | default: 78 | fmt.Fprint(buf, v) 79 | } 80 | } 81 | 82 | func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) { 83 | var buf bytes.Buffer 84 | fmt.Fprintf(&buf, "%s%s(", c.prefix, method) 85 | if method != "Receive" { 86 | buf.WriteString(commandName) 87 | for _, arg := range args { 88 | buf.WriteString(", ") 89 | c.printValue(&buf, arg) 90 | } 91 | } 92 | buf.WriteString(") -> (") 93 | if method != "Send" { 94 | c.printValue(&buf, reply) 95 | buf.WriteString(", ") 96 | } 97 | fmt.Fprintf(&buf, "%v)", err) 98 | c.logger.Output(3, buf.String()) 99 | } 100 | 101 | func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) { 102 | reply, err := c.Conn.Do(commandName, args...) 103 | c.print("Do", commandName, args, reply, err) 104 | return reply, err 105 | } 106 | 107 | func (c *loggingConn) Send(commandName string, args ...interface{}) error { 108 | err := c.Conn.Send(commandName, args...) 109 | c.print("Send", commandName, args, nil, err) 110 | return err 111 | } 112 | 113 | func (c *loggingConn) Receive() (interface{}, error) { 114 | reply, err := c.Conn.Receive() 115 | c.print("Receive", "", nil, reply, err) 116 | return reply, err 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/altsrc/toml_file_loader.go: -------------------------------------------------------------------------------- 1 | // Disabling building of toml support in cases where golang is 1.0 or 1.1 2 | // as the encoding library is not implemented or supported. 3 | 4 | // +build go1.2 5 | 6 | package altsrc 7 | 8 | import ( 9 | "fmt" 10 | "reflect" 11 | 12 | "github.com/BurntSushi/toml" 13 | "gopkg.in/urfave/cli.v1" 14 | ) 15 | 16 | type tomlMap struct { 17 | Map map[interface{}]interface{} 18 | } 19 | 20 | func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) { 21 | ret = make(map[interface{}]interface{}) 22 | m := i.(map[string]interface{}) 23 | for key, val := range m { 24 | v := reflect.ValueOf(val) 25 | switch v.Kind() { 26 | case reflect.Bool: 27 | ret[key] = val.(bool) 28 | case reflect.String: 29 | ret[key] = val.(string) 30 | case reflect.Int: 31 | ret[key] = int(val.(int)) 32 | case reflect.Int8: 33 | ret[key] = int(val.(int8)) 34 | case reflect.Int16: 35 | ret[key] = int(val.(int16)) 36 | case reflect.Int32: 37 | ret[key] = int(val.(int32)) 38 | case reflect.Int64: 39 | ret[key] = int(val.(int64)) 40 | case reflect.Uint: 41 | ret[key] = int(val.(uint)) 42 | case reflect.Uint8: 43 | ret[key] = int(val.(uint8)) 44 | case reflect.Uint16: 45 | ret[key] = int(val.(uint16)) 46 | case reflect.Uint32: 47 | ret[key] = int(val.(uint32)) 48 | case reflect.Uint64: 49 | ret[key] = int(val.(uint64)) 50 | case reflect.Float32: 51 | ret[key] = float64(val.(float32)) 52 | case reflect.Float64: 53 | ret[key] = float64(val.(float64)) 54 | case reflect.Map: 55 | if tmp, err := unmarshalMap(val); err == nil { 56 | ret[key] = tmp 57 | } else { 58 | return nil, err 59 | } 60 | case reflect.Array, reflect.Slice: 61 | ret[key] = val.([]interface{}) 62 | default: 63 | return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind()) 64 | } 65 | } 66 | return ret, nil 67 | } 68 | 69 | func (self *tomlMap) UnmarshalTOML(i interface{}) error { 70 | if tmp, err := unmarshalMap(i); err == nil { 71 | self.Map = tmp 72 | } else { 73 | return err 74 | } 75 | return nil 76 | } 77 | 78 | type tomlSourceContext struct { 79 | FilePath string 80 | } 81 | 82 | // NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. 83 | func NewTomlSourceFromFile(file string) (InputSourceContext, error) { 84 | tsc := &tomlSourceContext{FilePath: file} 85 | var results tomlMap = tomlMap{} 86 | if err := readCommandToml(tsc.FilePath, &results); err != nil { 87 | return nil, fmt.Errorf("Unable to load TOML file '%s': inner error: \n'%v'", tsc.FilePath, err.Error()) 88 | } 89 | return &MapInputSource{valueMap: results.Map}, nil 90 | } 91 | 92 | // NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. 93 | func NewTomlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { 94 | return func(context *cli.Context) (InputSourceContext, error) { 95 | filePath := context.String(flagFileName) 96 | return NewTomlSourceFromFile(filePath) 97 | } 98 | } 99 | 100 | func readCommandToml(filePath string, container interface{}) (err error) { 101 | b, err := loadDataFrom(filePath) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | err = toml.Unmarshal(b, container) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = nil 112 | return 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import argparse 5 | import os 6 | import sys 7 | import tempfile 8 | 9 | from subprocess import check_call, check_output 10 | 11 | 12 | PACKAGE_NAME = os.environ.get( 13 | 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' 14 | ) 15 | 16 | 17 | def main(sysargs=sys.argv[:]): 18 | targets = { 19 | 'vet': _vet, 20 | 'test': _test, 21 | 'gfmrun': _gfmrun, 22 | 'toc': _toc, 23 | 'gen': _gen, 24 | } 25 | 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument( 28 | 'target', nargs='?', choices=tuple(targets.keys()), default='test' 29 | ) 30 | args = parser.parse_args(sysargs[1:]) 31 | 32 | targets[args.target]() 33 | return 0 34 | 35 | 36 | def _test(): 37 | if check_output('go version'.split()).split()[2] < 'go1.2': 38 | _run('go test -v .') 39 | return 40 | 41 | coverprofiles = [] 42 | for subpackage in ['', 'altsrc']: 43 | coverprofile = 'cli.coverprofile' 44 | if subpackage != '': 45 | coverprofile = '{}.coverprofile'.format(subpackage) 46 | 47 | coverprofiles.append(coverprofile) 48 | 49 | _run('go test -v'.split() + [ 50 | '-coverprofile={}'.format(coverprofile), 51 | ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') 52 | ]) 53 | 54 | combined_name = _combine_coverprofiles(coverprofiles) 55 | _run('go tool cover -func={}'.format(combined_name)) 56 | os.remove(combined_name) 57 | 58 | 59 | def _gfmrun(): 60 | go_version = check_output('go version'.split()).split()[2] 61 | if go_version < 'go1.3': 62 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 63 | return 64 | _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) 65 | 66 | 67 | def _vet(): 68 | _run('go vet ./...') 69 | 70 | 71 | def _toc(): 72 | _run('node_modules/.bin/markdown-toc -i README.md') 73 | _run('git diff --exit-code') 74 | 75 | 76 | def _gen(): 77 | go_version = check_output('go version'.split()).split()[2] 78 | if go_version < 'go1.5': 79 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 80 | return 81 | 82 | _run('go generate ./...') 83 | _run('git diff --exit-code') 84 | 85 | 86 | def _run(command): 87 | if hasattr(command, 'split'): 88 | command = command.split() 89 | print('runtests: {}'.format(' '.join(command)), file=sys.stderr) 90 | check_call(command) 91 | 92 | 93 | def _gfmrun_count(): 94 | with open('README.md') as infile: 95 | lines = infile.read().splitlines() 96 | return len(filter(_is_go_runnable, lines)) 97 | 98 | 99 | def _is_go_runnable(line): 100 | return line.startswith('package main') 101 | 102 | 103 | def _combine_coverprofiles(coverprofiles): 104 | combined = tempfile.NamedTemporaryFile( 105 | suffix='.coverprofile', delete=False 106 | ) 107 | combined.write('mode: set\n') 108 | 109 | for coverprofile in coverprofiles: 110 | with open(coverprofile, 'r') as infile: 111 | for line in infile.readlines(): 112 | if not line.startswith('mode: '): 113 | combined.write(line) 114 | 115 | combined.flush() 116 | name = combined.name 117 | combined.close() 118 | return name 119 | 120 | 121 | if __name__ == '__main__': 122 | sys.exit(main()) 123 | -------------------------------------------------------------------------------- /vendor/github.com/buger/jsonparser/benchmark/benchmark_large_payload_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Each test should process 24kb json record (based on Discourse API) 3 | It should read 2 arrays, and for each item in array get few fields. 4 | Basically it means processing full JSON file. 5 | */ 6 | package benchmark 7 | 8 | import ( 9 | "github.com/buger/jsonparser" 10 | "testing" 11 | // "github.com/Jeffail/gabs" 12 | // "github.com/bitly/go-simplejson" 13 | "encoding/json" 14 | "github.com/a8m/djson" 15 | jlexer "github.com/mailru/easyjson/jlexer" 16 | "github.com/pquerna/ffjson/ffjson" 17 | // "github.com/antonholmquist/jason" 18 | // "fmt" 19 | ) 20 | 21 | /* 22 | github.com/buger/jsonparser 23 | */ 24 | func BenchmarkJsonParserLarge(b *testing.B) { 25 | for i := 0; i < b.N; i++ { 26 | jsonparser.ArrayEach(largeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { 27 | jsonparser.Get(value, "username") 28 | nothing() 29 | }, "users") 30 | 31 | jsonparser.ArrayEach(largeFixture, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { 32 | jsonparser.GetInt(value, "id") 33 | jsonparser.Get(value, "slug") 34 | nothing() 35 | }, "topics", "topics") 36 | } 37 | } 38 | 39 | /* 40 | encoding/json 41 | */ 42 | func BenchmarkEncodingJsonStructLarge(b *testing.B) { 43 | for i := 0; i < b.N; i++ { 44 | var data LargePayload 45 | json.Unmarshal(largeFixture, &data) 46 | 47 | for _, u := range data.Users { 48 | nothing(u.Username) 49 | } 50 | 51 | for _, t := range data.Topics.Topics { 52 | nothing(t.Id, t.Slug) 53 | } 54 | } 55 | } 56 | 57 | func BenchmarkEncodingJsonInterfaceLarge(b *testing.B) { 58 | for i := 0; i < b.N; i++ { 59 | var data interface{} 60 | json.Unmarshal(largeFixture, &data) 61 | m := data.(map[string]interface{}) 62 | 63 | users := m["users"].([]interface{}) 64 | for _, u := range users { 65 | nothing(u.(map[string]interface{})["username"].(string)) 66 | } 67 | 68 | topics := m["topics"].(map[string]interface{})["topics"].([]interface{}) 69 | for _, t := range topics { 70 | tI := t.(map[string]interface{}) 71 | nothing(tI["id"].(float64), tI["slug"].(string)) 72 | } 73 | } 74 | } 75 | 76 | /* 77 | github.com/pquerna/ffjson 78 | */ 79 | 80 | func BenchmarkFFJsonLarge(b *testing.B) { 81 | for i := 0; i < b.N; i++ { 82 | var data LargePayload 83 | ffjson.Unmarshal(largeFixture, &data) 84 | 85 | for _, u := range data.Users { 86 | nothing(u.Username) 87 | } 88 | 89 | for _, t := range data.Topics.Topics { 90 | nothing(t.Id, t.Slug) 91 | } 92 | } 93 | } 94 | 95 | /* 96 | github.com/mailru/easyjson 97 | */ 98 | func BenchmarkEasyJsonLarge(b *testing.B) { 99 | for i := 0; i < b.N; i++ { 100 | lexer := &jlexer.Lexer{Data: largeFixture} 101 | data := new(LargePayload) 102 | data.UnmarshalEasyJSON(lexer) 103 | 104 | for _, u := range data.Users { 105 | nothing(u.Username) 106 | } 107 | 108 | for _, t := range data.Topics.Topics { 109 | nothing(t.Id, t.Slug) 110 | } 111 | } 112 | } 113 | 114 | /* 115 | github.com/a8m/djson 116 | */ 117 | func BenchmarkDjsonLarge(b *testing.B) { 118 | for i := 0; i < b.N; i++ { 119 | m, _ := djson.DecodeObject(largeFixture) 120 | users := m["users"].([]interface{}) 121 | for _, u := range users { 122 | nothing(u.(map[string]interface{})["username"].(string)) 123 | } 124 | 125 | topics := m["topics"].(map[string]interface{})["topics"].([]interface{}) 126 | for _, t := range topics { 127 | tI := t.(map[string]interface{}) 128 | nothing(tI["id"].(float64), tI["slug"].(string)) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/logger.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "database/sql/driver" 5 | "fmt" 6 | "log" 7 | "os" 8 | "reflect" 9 | "regexp" 10 | "time" 11 | "unicode" 12 | ) 13 | 14 | var ( 15 | defaultLogger = Logger{log.New(os.Stdout, "\r\n", 0)} 16 | sqlRegexp = regexp.MustCompile(`\?`) 17 | numericPlaceHolderRegexp = regexp.MustCompile(`\$\d+`) 18 | ) 19 | 20 | func isPrintable(s string) bool { 21 | for _, r := range s { 22 | if !unicode.IsPrint(r) { 23 | return false 24 | } 25 | } 26 | return true 27 | } 28 | 29 | var LogFormatter = func(values ...interface{}) (messages []interface{}) { 30 | if len(values) > 1 { 31 | var ( 32 | sql string 33 | formattedValues []string 34 | level = values[0] 35 | currentTime = "\n\033[33m[" + NowFunc().Format("2006-01-02 15:04:05") + "]\033[0m" 36 | source = fmt.Sprintf("\033[35m(%v)\033[0m", values[1]) 37 | ) 38 | 39 | messages = []interface{}{source, currentTime} 40 | 41 | if level == "sql" { 42 | // duration 43 | messages = append(messages, fmt.Sprintf(" \033[36;1m[%.2fms]\033[0m ", float64(values[2].(time.Duration).Nanoseconds()/1e4)/100.0)) 44 | // sql 45 | 46 | for _, value := range values[4].([]interface{}) { 47 | indirectValue := reflect.Indirect(reflect.ValueOf(value)) 48 | if indirectValue.IsValid() { 49 | value = indirectValue.Interface() 50 | if t, ok := value.(time.Time); ok { 51 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05"))) 52 | } else if b, ok := value.([]byte); ok { 53 | if str := string(b); isPrintable(str) { 54 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str)) 55 | } else { 56 | formattedValues = append(formattedValues, "''") 57 | } 58 | } else if r, ok := value.(driver.Valuer); ok { 59 | if value, err := r.Value(); err == nil && value != nil { 60 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value)) 61 | } else { 62 | formattedValues = append(formattedValues, "NULL") 63 | } 64 | } else { 65 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value)) 66 | } 67 | } else { 68 | formattedValues = append(formattedValues, "NULL") 69 | } 70 | } 71 | 72 | // differentiate between $n placeholders or else treat like ? 73 | if numericPlaceHolderRegexp.MatchString(values[3].(string)) { 74 | sql = values[3].(string) 75 | for index, value := range formattedValues { 76 | placeholder := fmt.Sprintf(`\$%d([^\d]|$)`, index+1) 77 | sql = regexp.MustCompile(placeholder).ReplaceAllString(sql, value+"$1") 78 | } 79 | } else { 80 | formattedValuesLength := len(formattedValues) 81 | for index, value := range sqlRegexp.Split(values[3].(string), -1) { 82 | sql += value 83 | if index < formattedValuesLength { 84 | sql += formattedValues[index] 85 | } 86 | } 87 | } 88 | 89 | messages = append(messages, sql) 90 | } else { 91 | messages = append(messages, "\033[31;1m") 92 | messages = append(messages, values[2:]...) 93 | messages = append(messages, "\033[0m") 94 | } 95 | } 96 | 97 | return 98 | } 99 | 100 | type logger interface { 101 | Print(v ...interface{}) 102 | } 103 | 104 | // LogWriter wlog writer interface 105 | type LogWriter interface { 106 | Println(v ...interface{}) 107 | } 108 | 109 | // Logger default logger 110 | type Logger struct { 111 | LogWriter 112 | } 113 | 114 | // Print format & print wlog 115 | func (logger Logger) Print(values ...interface{}) { 116 | logger.Println(LogFormatter(values...)...) 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/callback_save.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import "reflect" 4 | 5 | func beginTransactionCallback(scope *Scope) { 6 | scope.Begin() 7 | } 8 | 9 | func commitOrRollbackTransactionCallback(scope *Scope) { 10 | scope.CommitOrRollback() 11 | } 12 | 13 | func saveFieldAsAssociation(scope *Scope, field *Field) (bool, *Relationship) { 14 | if scope.changeableField(field) && !field.IsBlank && !field.IsIgnored { 15 | if value, ok := field.TagSettings["SAVE_ASSOCIATIONS"]; !ok || (value != "false" && value != "skip") { 16 | if relationship := field.Relationship; relationship != nil { 17 | return true, relationship 18 | } 19 | } 20 | } 21 | return false, nil 22 | } 23 | 24 | func saveBeforeAssociationsCallback(scope *Scope) { 25 | if !scope.shouldSaveAssociations() { 26 | return 27 | } 28 | for _, field := range scope.Fields() { 29 | if ok, relationship := saveFieldAsAssociation(scope, field); ok && relationship.Kind == "belongs_to" { 30 | fieldValue := field.Field.Addr().Interface() 31 | scope.Err(scope.NewDB().Save(fieldValue).Error) 32 | if len(relationship.ForeignFieldNames) != 0 { 33 | // set value's foreign key 34 | for idx, fieldName := range relationship.ForeignFieldNames { 35 | associationForeignName := relationship.AssociationForeignDBNames[idx] 36 | if foreignField, ok := scope.New(fieldValue).FieldByName(associationForeignName); ok { 37 | scope.Err(scope.SetColumn(fieldName, foreignField.Field.Interface())) 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | func saveAfterAssociationsCallback(scope *Scope) { 46 | if !scope.shouldSaveAssociations() { 47 | return 48 | } 49 | for _, field := range scope.Fields() { 50 | if ok, relationship := saveFieldAsAssociation(scope, field); ok && 51 | (relationship.Kind == "has_one" || relationship.Kind == "has_many" || relationship.Kind == "many_to_many") { 52 | value := field.Field 53 | 54 | switch value.Kind() { 55 | case reflect.Slice: 56 | for i := 0; i < value.Len(); i++ { 57 | newDB := scope.NewDB() 58 | elem := value.Index(i).Addr().Interface() 59 | newScope := newDB.NewScope(elem) 60 | 61 | if relationship.JoinTableHandler == nil && len(relationship.ForeignFieldNames) != 0 { 62 | for idx, fieldName := range relationship.ForeignFieldNames { 63 | associationForeignName := relationship.AssociationForeignDBNames[idx] 64 | if f, ok := scope.FieldByName(associationForeignName); ok { 65 | scope.Err(newScope.SetColumn(fieldName, f.Field.Interface())) 66 | } 67 | } 68 | } 69 | 70 | if relationship.PolymorphicType != "" { 71 | scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue)) 72 | } 73 | 74 | scope.Err(newDB.Save(elem).Error) 75 | 76 | if joinTableHandler := relationship.JoinTableHandler; joinTableHandler != nil { 77 | scope.Err(joinTableHandler.Add(joinTableHandler, newDB, scope.Value, newScope.Value)) 78 | } 79 | } 80 | default: 81 | elem := value.Addr().Interface() 82 | newScope := scope.New(elem) 83 | if len(relationship.ForeignFieldNames) != 0 { 84 | for idx, fieldName := range relationship.ForeignFieldNames { 85 | associationForeignName := relationship.AssociationForeignDBNames[idx] 86 | if f, ok := scope.FieldByName(associationForeignName); ok { 87 | scope.Err(newScope.SetColumn(fieldName, f.Field.Interface())) 88 | } 89 | } 90 | } 91 | 92 | if relationship.PolymorphicType != "" { 93 | scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue)) 94 | } 95 | scope.Err(scope.NewDB().Save(elem).Error) 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /redis/redis.go: -------------------------------------------------------------------------------- 1 | /** 2 | * @Description redis pool 3 | * @Author HundredLee 4 | * @Email hundred9411@gmail.com 5 | */ 6 | package redis 7 | 8 | import ( 9 | "github.com/garyburd/redigo/redis" 10 | "github.com/hundredlee/wechat_pusher/config" 11 | "github.com/hundredlee/wechat_pusher/hlog" 12 | "os" 13 | "time" 14 | ) 15 | 16 | var ( 17 | redisPool *redis.Pool 18 | conf *config.Config = config.Instance() 19 | ) 20 | 21 | func instance() *redis.Pool { 22 | 23 | if redisPool != nil { 24 | return redisPool 25 | } 26 | 27 | host := conf.ConMap["Redis.HOST"] 28 | pass := conf.ConMap["Redis.PASS"] 29 | db := conf.ConMap["Redis.DB"] 30 | poolsize := conf.ConMap["Redis.POOL_SIZE"] 31 | timeout := conf.ConMap["Redis.TIMEOUT"] 32 | if host == nil { 33 | panic("Redis Config not complete") 34 | } 35 | 36 | redisPool = &redis.Pool{ 37 | MaxActive: int(poolsize.(int)), 38 | IdleTimeout: time.Duration(timeout.(int)) * time.Second, 39 | Dial: func() (redis.Conn, error) { 40 | 41 | c, err := redis.Dial("tcp", host.(string)) 42 | 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | /** 48 | * maybe redis connect without or no need select database 49 | */ 50 | if pass != nil { 51 | if _, err := c.Do("AUTH", pass.(string)); err != nil { 52 | c.Close() 53 | return nil, err 54 | } 55 | } 56 | 57 | if db != nil { 58 | if _, err := c.Do("SELECT", db.(int)); err != nil { 59 | c.Close() 60 | return nil, err 61 | } 62 | } 63 | 64 | return c, nil 65 | 66 | }, 67 | } 68 | 69 | conn := redisPool.Get() 70 | defer conn.Close() 71 | 72 | r, err := redis.String(conn.Do("PING", "test")) 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | if r != "test" { 78 | hlog.LogInstance().LogInfo("redis connect failed") 79 | os.Exit(-1) 80 | } else { 81 | hlog.LogInstance().LogInfo("redis connect success") 82 | } 83 | 84 | return redisPool 85 | 86 | } 87 | 88 | func Exists(key string) bool { 89 | conn := instance().Get() 90 | defer conn.Close() 91 | if conn == nil { 92 | panic("redis init faild") 93 | } 94 | 95 | n, err := redis.Int(conn.Do("EXISTS", key)) 96 | 97 | if err != nil { 98 | return false 99 | } 100 | return n > 0 101 | } 102 | 103 | /** 104 | @param key 105 | @param value 106 | @param refresh true or false 107 | @param ttl -1 means persistence 108 | */ 109 | func Set(key string, value string, refresh bool, ttl int) bool { 110 | conn := instance().Get() 111 | defer conn.Close() 112 | 113 | exists := Exists(key) 114 | 115 | if exists && !refresh { 116 | return false 117 | } 118 | 119 | if refresh && exists { 120 | var err error 121 | 122 | if ttl != -1 { 123 | _, err = redis.String(conn.Do("set", key, value, "ex", ttl)) 124 | } else { 125 | _, err = redis.String(conn.Do("set", key, value)) 126 | } 127 | 128 | if err != nil { 129 | return false 130 | } 131 | 132 | return true 133 | } 134 | 135 | if !exists { 136 | var err error 137 | 138 | if ttl != -1 { 139 | _, err = redis.String(conn.Do("set", key, value, "ex", ttl)) 140 | } else { 141 | _, err = redis.String(conn.Do("set", key, value)) 142 | } 143 | 144 | if err != nil { 145 | return false 146 | } 147 | 148 | return true 149 | } 150 | 151 | return false 152 | } 153 | 154 | func Get(key string) []byte { 155 | 156 | conn := instance().Get() 157 | defer conn.Close() 158 | 159 | val, err := conn.Do("get", key) 160 | if err != nil { 161 | return nil 162 | } 163 | if vall, ok := val.([]byte); ok { 164 | return vall 165 | } 166 | return []byte{} 167 | } 168 | 169 | func TTL(key string) int { 170 | conn := instance().Get() 171 | 172 | defer conn.Close() 173 | 174 | ttl, err := redis.Int(conn.Do("ttl", key)) 175 | if err != nil { 176 | panic(err.Error()) 177 | } 178 | return ttl 179 | } 180 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redisx/connmux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redisx 16 | 17 | import ( 18 | "errors" 19 | "sync" 20 | 21 | "github.com/garyburd/redigo/internal" 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | // ConnMux multiplexes one or more connections to a single underlying 26 | // connection. The ConnMux connections do not support concurrency, commands 27 | // that associate server side state with the connection or commands that put 28 | // the connection in a special mode. 29 | type ConnMux struct { 30 | c redis.Conn 31 | 32 | sendMu sync.Mutex 33 | sendID uint 34 | 35 | recvMu sync.Mutex 36 | recvID uint 37 | recvWait map[uint]chan struct{} 38 | } 39 | 40 | func NewConnMux(c redis.Conn) *ConnMux { 41 | return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})} 42 | } 43 | 44 | // Get gets a connection. The application must close the returned connection. 45 | func (p *ConnMux) Get() redis.Conn { 46 | c := &muxConn{p: p} 47 | c.ids = c.buf[:0] 48 | return c 49 | } 50 | 51 | // Close closes the underlying connection. 52 | func (p *ConnMux) Close() error { 53 | return p.c.Close() 54 | } 55 | 56 | type muxConn struct { 57 | p *ConnMux 58 | ids []uint 59 | buf [8]uint 60 | } 61 | 62 | func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error { 63 | if internal.LookupCommandInfo(cmd).Set != 0 { 64 | return errors.New("command not supported by mux pool") 65 | } 66 | p := c.p 67 | p.sendMu.Lock() 68 | id := p.sendID 69 | c.ids = append(c.ids, id) 70 | p.sendID++ 71 | err := p.c.Send(cmd, args...) 72 | if flush { 73 | err = p.c.Flush() 74 | } 75 | p.sendMu.Unlock() 76 | return err 77 | } 78 | 79 | func (c *muxConn) Send(cmd string, args ...interface{}) error { 80 | return c.send(false, cmd, args...) 81 | } 82 | 83 | func (c *muxConn) Flush() error { 84 | p := c.p 85 | p.sendMu.Lock() 86 | err := p.c.Flush() 87 | p.sendMu.Unlock() 88 | return err 89 | } 90 | 91 | func (c *muxConn) Receive() (interface{}, error) { 92 | if len(c.ids) == 0 { 93 | return nil, errors.New("mux pool underflow") 94 | } 95 | 96 | id := c.ids[0] 97 | c.ids = c.ids[1:] 98 | if len(c.ids) == 0 { 99 | c.ids = c.buf[:0] 100 | } 101 | 102 | p := c.p 103 | p.recvMu.Lock() 104 | if p.recvID != id { 105 | ch := make(chan struct{}) 106 | p.recvWait[id] = ch 107 | p.recvMu.Unlock() 108 | <-ch 109 | p.recvMu.Lock() 110 | if p.recvID != id { 111 | panic("out of sync") 112 | } 113 | } 114 | 115 | v, err := p.c.Receive() 116 | 117 | id++ 118 | p.recvID = id 119 | ch, ok := p.recvWait[id] 120 | if ok { 121 | delete(p.recvWait, id) 122 | } 123 | p.recvMu.Unlock() 124 | if ok { 125 | ch <- struct{}{} 126 | } 127 | 128 | return v, err 129 | } 130 | 131 | func (c *muxConn) Close() error { 132 | var err error 133 | if len(c.ids) == 0 { 134 | return nil 135 | } 136 | c.Flush() 137 | for _ = range c.ids { 138 | _, err = c.Receive() 139 | } 140 | return err 141 | } 142 | 143 | func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) { 144 | if err := c.send(true, cmd, args...); err != nil { 145 | return nil, err 146 | } 147 | return c.Receive() 148 | } 149 | 150 | func (c *muxConn) Err() error { 151 | return c.p.c.Err() 152 | } 153 | -------------------------------------------------------------------------------- /utils/push.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/buger/jsonparser" 8 | "github.com/hundredlee/wechat_pusher/enum" 9 | "github.com/hundredlee/wechat_pusher/hlog" 10 | "github.com/hundredlee/wechat_pusher/task" 11 | "io/ioutil" 12 | "net/http" 13 | "strconv" 14 | "time" 15 | ) 16 | 17 | var fileLog = hlog.LogInstance() 18 | 19 | type Push struct { 20 | BufferNum int 21 | Retries int 22 | Tasks []task.Task 23 | TaskType string 24 | } 25 | 26 | func NewPush(push *Push) *Push { 27 | return push 28 | } 29 | 30 | func (self *Push) SetRetries(retries int) *Push { 31 | self.Retries = retries 32 | return self 33 | } 34 | 35 | func (self *Push) SetBufferNum(bufferNum int) *Push { 36 | self.BufferNum = bufferNum 37 | return self 38 | } 39 | 40 | func (self *Push) SetTaskType(taskType string) *Push { 41 | self.TaskType = taskType 42 | return self 43 | } 44 | 45 | func (self *Push) Add(schedule string) { 46 | 47 | //if tasks len equal 0 || the first object is not right taskType panic 48 | if len(self.Tasks) <= 0 { 49 | panic("task is not allow empty") 50 | } 51 | 52 | if self.Retries == 0 || self.BufferNum == 0 { 53 | panic("Please SetRetries or SetBufferNum") 54 | } 55 | 56 | if self.TaskType == "" { 57 | panic("Please Set TaskType") 58 | } 59 | 60 | firstTask := self.Tasks[0] 61 | switch self.TaskType { 62 | case enum.TASK_TYPE_TEMPLATE: 63 | if _, ok := firstTask.(*task.TemplateTask); !ok { 64 | panic("not allow other TaskType struct in this TaskType") 65 | } 66 | case enum.TASK_TYPE_TEXT_CUSTOM: 67 | if _, ok := firstTask.(*task.TextCustomTask); !ok { 68 | panic("not allow other TaskType struct in this TaskType") 69 | } 70 | } 71 | 72 | getCronInstance().AddFunc(schedule, func() { 73 | 74 | fileLog.LogInfo("Start schedule " + schedule + " TaskNumber:" + strconv.Itoa(len(self.Tasks)) + " TaskType:" + self.TaskType) 75 | 76 | var resourceChannel = make(chan bool, self.BufferNum) 77 | 78 | for _, task := range self.Tasks { 79 | 80 | resourceChannel <- true 81 | 82 | go run(task, self.Retries, resourceChannel, self.TaskType) 83 | 84 | } 85 | 86 | close(resourceChannel) 87 | 88 | }) 89 | 90 | } 91 | 92 | func (self *Push) RunRightNow(schedule string) { 93 | 94 | fileLog.LogInfo("Start schedule " + schedule + " TaskNumber:" + strconv.Itoa(len(self.Tasks)) + " TaskType:" + self.TaskType) 95 | 96 | var resourceChannel = make(chan bool, self.BufferNum) 97 | 98 | for _, task := range self.Tasks { 99 | 100 | resourceChannel <- true 101 | 102 | go run(task, self.Retries, resourceChannel, self.TaskType) 103 | 104 | } 105 | 106 | close(resourceChannel) 107 | } 108 | 109 | func run(task task.Task, retries int, resourceChannel chan bool, taskType string) { 110 | retr := 0 111 | 112 | defer func() { 113 | if recover() != nil { 114 | fileLog.LogError(fmt.Sprintf("task : %v", task)) 115 | } 116 | }() 117 | 118 | r, _ := json.Marshal(task.GetTask()) 119 | 120 | url := fmt.Sprintf(enum.URL_MAP[taskType], GetAccessToken()) 121 | 122 | LABEL: 123 | resp, _ := http.Post(url, "application/json;charset=utf-8", bytes.NewBuffer(r)) 124 | 125 | body, _ := ioutil.ReadAll(resp.Body) 126 | defer resp.Body.Close() 127 | errCode, _ := jsonparser.GetInt(body, "errcode") 128 | errmsg, _ := jsonparser.GetString(body, "errmsg") 129 | 130 | if errCode != 0 { 131 | if retr >= retries { 132 | fileLog.LogError(fmt.Sprintf("TaskInfo : %v -- ErrorCode : %d -- TryTimeOut : %d", task, errCode, retr)) 133 | } else { 134 | 135 | fileLog.LogError(fmt.Sprintf("TaskInfo : %v -- ErrorCode : %d -- errmsg : %s", task, errCode, errmsg)) 136 | 137 | time.Sleep(3 * time.Second) 138 | retr++ 139 | goto LABEL 140 | } 141 | } else { 142 | fileLog.LogInfo(fmt.Sprintf("%v -- push success", task)) 143 | } 144 | 145 | <-resourceChannel 146 | } 147 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/scaner_test.go: -------------------------------------------------------------------------------- 1 | package gorm_test 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "errors" 7 | "testing" 8 | 9 | "github.com/jinzhu/gorm" 10 | ) 11 | 12 | func TestScannableSlices(t *testing.T) { 13 | if err := DB.AutoMigrate(&RecordWithSlice{}).Error; err != nil { 14 | t.Errorf("Should create table with slice values correctly: %s", err) 15 | } 16 | 17 | r1 := RecordWithSlice{ 18 | Strings: ExampleStringSlice{"a", "b", "c"}, 19 | Structs: ExampleStructSlice{ 20 | {"name1", "value1"}, 21 | {"name2", "value2"}, 22 | }, 23 | } 24 | 25 | if err := DB.Save(&r1).Error; err != nil { 26 | t.Errorf("Should save record with slice values") 27 | } 28 | 29 | var r2 RecordWithSlice 30 | 31 | if err := DB.Find(&r2).Error; err != nil { 32 | t.Errorf("Should fetch record with slice values") 33 | } 34 | 35 | if len(r2.Strings) != 3 || r2.Strings[0] != "a" || r2.Strings[1] != "b" || r2.Strings[2] != "c" { 36 | t.Errorf("Should have serialised and deserialised a string array") 37 | } 38 | 39 | if len(r2.Structs) != 2 || r2.Structs[0].Name != "name1" || r2.Structs[0].Value != "value1" || r2.Structs[1].Name != "name2" || r2.Structs[1].Value != "value2" { 40 | t.Errorf("Should have serialised and deserialised a struct array") 41 | } 42 | } 43 | 44 | type RecordWithSlice struct { 45 | ID uint64 46 | Strings ExampleStringSlice `sql:"type:text"` 47 | Structs ExampleStructSlice `sql:"type:text"` 48 | } 49 | 50 | type ExampleStringSlice []string 51 | 52 | func (l ExampleStringSlice) Value() (driver.Value, error) { 53 | bytes, err := json.Marshal(l) 54 | return string(bytes), err 55 | } 56 | 57 | func (l *ExampleStringSlice) Scan(input interface{}) error { 58 | switch value := input.(type) { 59 | case string: 60 | return json.Unmarshal([]byte(value), l) 61 | case []byte: 62 | return json.Unmarshal(value, l) 63 | default: 64 | return errors.New("not supported") 65 | } 66 | } 67 | 68 | type ExampleStruct struct { 69 | Name string 70 | Value string 71 | } 72 | 73 | type ExampleStructSlice []ExampleStruct 74 | 75 | func (l ExampleStructSlice) Value() (driver.Value, error) { 76 | bytes, err := json.Marshal(l) 77 | return string(bytes), err 78 | } 79 | 80 | func (l *ExampleStructSlice) Scan(input interface{}) error { 81 | switch value := input.(type) { 82 | case string: 83 | return json.Unmarshal([]byte(value), l) 84 | case []byte: 85 | return json.Unmarshal(value, l) 86 | default: 87 | return errors.New("not supported") 88 | } 89 | } 90 | 91 | type ScannerDataType struct { 92 | Street string `sql:"TYPE:varchar(24)"` 93 | } 94 | 95 | func (ScannerDataType) Value() (driver.Value, error) { 96 | return nil, nil 97 | } 98 | 99 | func (*ScannerDataType) Scan(input interface{}) error { 100 | return nil 101 | } 102 | 103 | type ScannerDataTypeTestStruct struct { 104 | Field1 int 105 | ScannerDataType *ScannerDataType `sql:"TYPE:json"` 106 | } 107 | 108 | type ScannerDataType2 struct { 109 | Street string `sql:"TYPE:varchar(24)"` 110 | } 111 | 112 | func (ScannerDataType2) Value() (driver.Value, error) { 113 | return nil, nil 114 | } 115 | 116 | func (*ScannerDataType2) Scan(input interface{}) error { 117 | return nil 118 | } 119 | 120 | type ScannerDataTypeTestStruct2 struct { 121 | Field1 int 122 | ScannerDataType *ScannerDataType2 123 | } 124 | 125 | func TestScannerDataType(t *testing.T) { 126 | scope := gorm.Scope{Value: &ScannerDataTypeTestStruct{}} 127 | if field, ok := scope.FieldByName("ScannerDataType"); ok { 128 | if DB.Dialect().DataTypeOf(field.StructField) != "json" { 129 | t.Errorf("data type for scanner is wrong") 130 | } 131 | } 132 | 133 | scope = gorm.Scope{Value: &ScannerDataTypeTestStruct2{}} 134 | if field, ok := scope.FieldByName("ScannerDataType"); ok { 135 | if DB.Dialect().DataTypeOf(field.StructField) != "varchar(24)" { 136 | t.Errorf("data type for scanner is wrong") 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/callback_update.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // Define callbacks for updating 10 | func init() { 11 | DefaultCallback.Update().Register("gorm:assign_updating_attributes", assignUpdatingAttributesCallback) 12 | DefaultCallback.Update().Register("gorm:begin_transaction", beginTransactionCallback) 13 | DefaultCallback.Update().Register("gorm:before_update", beforeUpdateCallback) 14 | DefaultCallback.Update().Register("gorm:save_before_associations", saveBeforeAssociationsCallback) 15 | DefaultCallback.Update().Register("gorm:update_time_stamp", updateTimeStampForUpdateCallback) 16 | DefaultCallback.Update().Register("gorm:update", updateCallback) 17 | DefaultCallback.Update().Register("gorm:save_after_associations", saveAfterAssociationsCallback) 18 | DefaultCallback.Update().Register("gorm:after_update", afterUpdateCallback) 19 | DefaultCallback.Update().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback) 20 | } 21 | 22 | // assignUpdatingAttributesCallback assign updating attributes to model 23 | func assignUpdatingAttributesCallback(scope *Scope) { 24 | if attrs, ok := scope.InstanceGet("gorm:update_interface"); ok { 25 | if updateMaps, hasUpdate := scope.updatedAttrsWithValues(attrs); hasUpdate { 26 | scope.InstanceSet("gorm:update_attrs", updateMaps) 27 | } else { 28 | scope.SkipLeft() 29 | } 30 | } 31 | } 32 | 33 | // beforeUpdateCallback will invoke `BeforeSave`, `BeforeUpdate` method before updating 34 | func beforeUpdateCallback(scope *Scope) { 35 | if scope.DB().HasBlockGlobalUpdate() && !scope.hasConditions() { 36 | scope.Err(errors.New("Missing WHERE clause while updating")) 37 | return 38 | } 39 | if _, ok := scope.Get("gorm:update_column"); !ok { 40 | if !scope.HasError() { 41 | scope.CallMethod("BeforeSave") 42 | } 43 | if !scope.HasError() { 44 | scope.CallMethod("BeforeUpdate") 45 | } 46 | } 47 | } 48 | 49 | // updateTimeStampForUpdateCallback will set `UpdatedAt` when updating 50 | func updateTimeStampForUpdateCallback(scope *Scope) { 51 | if _, ok := scope.Get("gorm:update_column"); !ok { 52 | scope.SetColumn("UpdatedAt", NowFunc()) 53 | } 54 | } 55 | 56 | // updateCallback the callback used to update data to database 57 | func updateCallback(scope *Scope) { 58 | if !scope.HasError() { 59 | var sqls []string 60 | 61 | if updateAttrs, ok := scope.InstanceGet("gorm:update_attrs"); ok { 62 | for column, value := range updateAttrs.(map[string]interface{}) { 63 | sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(column), scope.AddToVars(value))) 64 | } 65 | } else { 66 | for _, field := range scope.Fields() { 67 | if scope.changeableField(field) { 68 | if !field.IsPrimaryKey && field.IsNormal { 69 | sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface()))) 70 | } else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" { 71 | for _, foreignKey := range relationship.ForeignDBNames { 72 | if foreignField, ok := scope.FieldByName(foreignKey); ok && !scope.changeableField(foreignField) { 73 | sqls = append(sqls, 74 | fmt.Sprintf("%v = %v", scope.Quote(foreignField.DBName), scope.AddToVars(foreignField.Field.Interface()))) 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | var extraOption string 83 | if str, ok := scope.Get("gorm:update_option"); ok { 84 | extraOption = fmt.Sprint(str) 85 | } 86 | 87 | if len(sqls) > 0 { 88 | scope.Raw(fmt.Sprintf( 89 | "UPDATE %v SET %v%v%v", 90 | scope.QuotedTableName(), 91 | strings.Join(sqls, ", "), 92 | addExtraSpaceIfExist(scope.CombinedConditionSql()), 93 | addExtraSpaceIfExist(extraOption), 94 | )).Exec() 95 | } 96 | } 97 | } 98 | 99 | // afterUpdateCallback will invoke `AfterUpdate`, `AfterSave` method after updating 100 | func afterUpdateCallback(scope *Scope) { 101 | if _, ok := scope.Get("gorm:update_column"); !ok { 102 | if !scope.HasError() { 103 | scope.CallMethod("AfterUpdate") 104 | } 105 | if !scope.HasError() { 106 | scope.CallMethod("AfterSave") 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialect.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // Dialect interface contains behaviors that differ across SQL database 12 | type Dialect interface { 13 | // GetName get dialect's name 14 | GetName() string 15 | 16 | // SetDB set db for dialect 17 | SetDB(db SQLCommon) 18 | 19 | // BindVar return the placeholder for actual values in SQL statements, in many dbs it is "?", Postgres using $1 20 | BindVar(i int) string 21 | // Quote quotes field name to avoid SQL parsing exceptions by using a reserved word as a field name 22 | Quote(key string) string 23 | // DataTypeOf return data's sql type 24 | DataTypeOf(field *StructField) string 25 | 26 | // HasIndex check has index or not 27 | HasIndex(tableName string, indexName string) bool 28 | // HasForeignKey check has foreign key or not 29 | HasForeignKey(tableName string, foreignKeyName string) bool 30 | // RemoveIndex remove index 31 | RemoveIndex(tableName string, indexName string) error 32 | // HasTable check has table or not 33 | HasTable(tableName string) bool 34 | // HasColumn check has column or not 35 | HasColumn(tableName string, columnName string) bool 36 | 37 | // LimitAndOffsetSQL return generated SQL with Limit and Offset, as mssql has special case 38 | LimitAndOffsetSQL(limit, offset interface{}) string 39 | // SelectFromDummyTable return select values, for most dbs, `SELECT values` just works, mysql needs `SELECT value FROM DUAL` 40 | SelectFromDummyTable() string 41 | // LastInsertIdReturningSuffix most dbs support LastInsertId, but postgres needs to use `RETURNING` 42 | LastInsertIDReturningSuffix(tableName, columnName string) string 43 | 44 | // BuildForeignKeyName returns a foreign key name for the given table, field and reference 45 | BuildForeignKeyName(tableName, field, dest string) string 46 | 47 | // CurrentDatabase return current database name 48 | CurrentDatabase() string 49 | } 50 | 51 | var dialectsMap = map[string]Dialect{} 52 | 53 | func newDialect(name string, db SQLCommon) Dialect { 54 | if value, ok := dialectsMap[name]; ok { 55 | dialect := reflect.New(reflect.TypeOf(value).Elem()).Interface().(Dialect) 56 | dialect.SetDB(db) 57 | return dialect 58 | } 59 | 60 | fmt.Printf("`%v` is not officially supported, running under compatibility mode.\n", name) 61 | commontDialect := &commonDialect{} 62 | commontDialect.SetDB(db) 63 | return commontDialect 64 | } 65 | 66 | // RegisterDialect register new dialect 67 | func RegisterDialect(name string, dialect Dialect) { 68 | dialectsMap[name] = dialect 69 | } 70 | 71 | // ParseFieldStructForDialect get field's sql data type 72 | var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fieldValue reflect.Value, sqlType string, size int, additionalType string) { 73 | // Get redirected field type 74 | var ( 75 | reflectType = field.Struct.Type 76 | dataType = field.TagSettings["TYPE"] 77 | ) 78 | 79 | for reflectType.Kind() == reflect.Ptr { 80 | reflectType = reflectType.Elem() 81 | } 82 | 83 | // Get redirected field value 84 | fieldValue = reflect.Indirect(reflect.New(reflectType)) 85 | 86 | if gormDataType, ok := fieldValue.Interface().(interface { 87 | GormDataType(Dialect) string 88 | }); ok { 89 | dataType = gormDataType.GormDataType(dialect) 90 | } 91 | 92 | // Get scanner's real value 93 | var getScannerValue func(reflect.Value) 94 | getScannerValue = func(value reflect.Value) { 95 | fieldValue = value 96 | if _, isScanner := reflect.New(fieldValue.Type()).Interface().(sql.Scanner); isScanner && fieldValue.Kind() == reflect.Struct { 97 | getScannerValue(fieldValue.Field(0)) 98 | } 99 | } 100 | getScannerValue(fieldValue) 101 | 102 | // Default Size 103 | if num, ok := field.TagSettings["SIZE"]; ok { 104 | size, _ = strconv.Atoi(num) 105 | } else { 106 | size = 255 107 | } 108 | 109 | // Default type from tag setting 110 | additionalType = field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"] 111 | if value, ok := field.TagSettings["DEFAULT"]; ok { 112 | additionalType = additionalType + " DEFAULT " + value 113 | } 114 | 115 | return fieldValue, dataType, size, strings.TrimSpace(additionalType) 116 | } 117 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialect_postgres.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | type postgres struct { 11 | commonDialect 12 | } 13 | 14 | func init() { 15 | RegisterDialect("postgres", &postgres{}) 16 | } 17 | 18 | func (postgres) GetName() string { 19 | return "postgres" 20 | } 21 | 22 | func (postgres) BindVar(i int) string { 23 | return fmt.Sprintf("$%v", i) 24 | } 25 | 26 | func (s *postgres) DataTypeOf(field *StructField) string { 27 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s) 28 | 29 | if sqlType == "" { 30 | switch dataValue.Kind() { 31 | case reflect.Bool: 32 | sqlType = "boolean" 33 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr: 34 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 35 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 36 | sqlType = "serial" 37 | } else { 38 | sqlType = "integer" 39 | } 40 | case reflect.Int64, reflect.Uint64: 41 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 42 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 43 | sqlType = "bigserial" 44 | } else { 45 | sqlType = "bigint" 46 | } 47 | case reflect.Float32, reflect.Float64: 48 | sqlType = "numeric" 49 | case reflect.String: 50 | if _, ok := field.TagSettings["SIZE"]; !ok { 51 | size = 0 // if SIZE haven't been set, use `text` as the default type, as there are no performance different 52 | } 53 | 54 | if size > 0 && size < 65532 { 55 | sqlType = fmt.Sprintf("varchar(%d)", size) 56 | } else { 57 | sqlType = "text" 58 | } 59 | case reflect.Struct: 60 | if _, ok := dataValue.Interface().(time.Time); ok { 61 | sqlType = "timestamp with time zone" 62 | } 63 | case reflect.Map: 64 | if dataValue.Type().Name() == "Hstore" { 65 | sqlType = "hstore" 66 | } 67 | default: 68 | if IsByteArrayOrSlice(dataValue) { 69 | sqlType = "bytea" 70 | } else if isUUID(dataValue) { 71 | sqlType = "uuid" 72 | } 73 | } 74 | } 75 | 76 | if sqlType == "" { 77 | panic(fmt.Sprintf("invalid sql type %s (%s) for postgres", dataValue.Type().Name(), dataValue.Kind().String())) 78 | } 79 | 80 | if strings.TrimSpace(additionalType) == "" { 81 | return sqlType 82 | } 83 | return fmt.Sprintf("%v %v", sqlType, additionalType) 84 | } 85 | 86 | func (s postgres) HasIndex(tableName string, indexName string) bool { 87 | var count int 88 | s.db.QueryRow("SELECT count(*) FROM pg_indexes WHERE tablename = $1 AND indexname = $2", tableName, indexName).Scan(&count) 89 | return count > 0 90 | } 91 | 92 | func (s postgres) HasForeignKey(tableName string, foreignKeyName string) bool { 93 | var count int 94 | s.db.QueryRow("SELECT count(con.conname) FROM pg_constraint con WHERE $1::regclass::oid = con.conrelid AND con.conname = $2 AND con.contype='f'", tableName, foreignKeyName).Scan(&count) 95 | return count > 0 96 | } 97 | 98 | func (s postgres) HasTable(tableName string) bool { 99 | var count int 100 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.tables WHERE table_name = $1 AND table_type = 'BASE TABLE'", tableName).Scan(&count) 101 | return count > 0 102 | } 103 | 104 | func (s postgres) HasColumn(tableName string, columnName string) bool { 105 | var count int 106 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_name = $1 AND column_name = $2", tableName, columnName).Scan(&count) 107 | return count > 0 108 | } 109 | 110 | func (s postgres) CurrentDatabase() (name string) { 111 | s.db.QueryRow("SELECT CURRENT_DATABASE()").Scan(&name) 112 | return 113 | } 114 | 115 | func (s postgres) LastInsertIDReturningSuffix(tableName, key string) string { 116 | return fmt.Sprintf("RETURNING %v.%v", tableName, key) 117 | } 118 | 119 | func (postgres) SupportLastInsertID() bool { 120 | return false 121 | } 122 | 123 | func isUUID(value reflect.Value) bool { 124 | if value.Kind() != reflect.Array || value.Type().Len() != 16 { 125 | return false 126 | } 127 | typename := value.Type().Name() 128 | lower := strings.ToLower(typename) 129 | return "uuid" == lower || "guid" == lower 130 | } 131 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import "errors" 18 | 19 | // Subscription represents a subscribe or unsubscribe notification. 20 | type Subscription struct { 21 | 22 | // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe" 23 | Kind string 24 | 25 | // The channel that was changed. 26 | Channel string 27 | 28 | // The current number of subscriptions for connection. 29 | Count int 30 | } 31 | 32 | // Message represents a message notification. 33 | type Message struct { 34 | 35 | // The originating channel. 36 | Channel string 37 | 38 | // The message data. 39 | Data []byte 40 | } 41 | 42 | // PMessage represents a pmessage notification. 43 | type PMessage struct { 44 | 45 | // The matched pattern. 46 | Pattern string 47 | 48 | // The originating channel. 49 | Channel string 50 | 51 | // The message data. 52 | Data []byte 53 | } 54 | 55 | // Pong represents a pubsub pong notification. 56 | type Pong struct { 57 | Data string 58 | } 59 | 60 | // PubSubConn wraps a Conn with convenience methods for subscribers. 61 | type PubSubConn struct { 62 | Conn Conn 63 | } 64 | 65 | // Close closes the connection. 66 | func (c PubSubConn) Close() error { 67 | return c.Conn.Close() 68 | } 69 | 70 | // Subscribe subscribes the connection to the specified channels. 71 | func (c PubSubConn) Subscribe(channel ...interface{}) error { 72 | c.Conn.Send("SUBSCRIBE", channel...) 73 | return c.Conn.Flush() 74 | } 75 | 76 | // PSubscribe subscribes the connection to the given patterns. 77 | func (c PubSubConn) PSubscribe(channel ...interface{}) error { 78 | c.Conn.Send("PSUBSCRIBE", channel...) 79 | return c.Conn.Flush() 80 | } 81 | 82 | // Unsubscribe unsubscribes the connection from the given channels, or from all 83 | // of them if none is given. 84 | func (c PubSubConn) Unsubscribe(channel ...interface{}) error { 85 | c.Conn.Send("UNSUBSCRIBE", channel...) 86 | return c.Conn.Flush() 87 | } 88 | 89 | // PUnsubscribe unsubscribes the connection from the given patterns, or from all 90 | // of them if none is given. 91 | func (c PubSubConn) PUnsubscribe(channel ...interface{}) error { 92 | c.Conn.Send("PUNSUBSCRIBE", channel...) 93 | return c.Conn.Flush() 94 | } 95 | 96 | // Ping sends a PING to the server with the specified data. 97 | func (c PubSubConn) Ping(data string) error { 98 | c.Conn.Send("PING", data) 99 | return c.Conn.Flush() 100 | } 101 | 102 | // Receive returns a pushed message as a Subscription, Message, PMessage, Pong 103 | // or error. The return value is intended to be used directly in a type switch 104 | // as illustrated in the PubSubConn example. 105 | func (c PubSubConn) Receive() interface{} { 106 | reply, err := Values(c.Conn.Receive()) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | var kind string 112 | reply, err = Scan(reply, &kind) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | switch kind { 118 | case "message": 119 | var m Message 120 | if _, err := Scan(reply, &m.Channel, &m.Data); err != nil { 121 | return err 122 | } 123 | return m 124 | case "pmessage": 125 | var pm PMessage 126 | if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil { 127 | return err 128 | } 129 | return pm 130 | case "subscribe", "psubscribe", "unsubscribe", "punsubscribe": 131 | s := Subscription{Kind: kind} 132 | if _, err := Scan(reply, &s.Channel, &s.Count); err != nil { 133 | return err 134 | } 135 | return s 136 | case "pong": 137 | var p Pong 138 | if _, err := Scan(reply, &p.Data); err != nil { 139 | return err 140 | } 141 | return p 142 | } 143 | return errors.New("redigo: unknown pubsub notification") 144 | } 145 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/spec.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "time" 4 | 5 | // SpecSchedule specifies a duty cycle (to the second granularity), based on a 6 | // traditional crontab specification. It is computed initially and stored as bit sets. 7 | type SpecSchedule struct { 8 | Second, Minute, Hour, Dom, Month, Dow uint64 9 | } 10 | 11 | // bounds provides a range of acceptable values (plus a map of name to value). 12 | type bounds struct { 13 | min, max uint 14 | names map[string]uint 15 | } 16 | 17 | // The bounds for each field. 18 | var ( 19 | seconds = bounds{0, 59, nil} 20 | minutes = bounds{0, 59, nil} 21 | hours = bounds{0, 23, nil} 22 | dom = bounds{1, 31, nil} 23 | months = bounds{1, 12, map[string]uint{ 24 | "jan": 1, 25 | "feb": 2, 26 | "mar": 3, 27 | "apr": 4, 28 | "may": 5, 29 | "jun": 6, 30 | "jul": 7, 31 | "aug": 8, 32 | "sep": 9, 33 | "oct": 10, 34 | "nov": 11, 35 | "dec": 12, 36 | }} 37 | dow = bounds{0, 6, map[string]uint{ 38 | "sun": 0, 39 | "mon": 1, 40 | "tue": 2, 41 | "wed": 3, 42 | "thu": 4, 43 | "fri": 5, 44 | "sat": 6, 45 | }} 46 | ) 47 | 48 | const ( 49 | // Set the top bit if a star was included in the expression. 50 | starBit = 1 << 63 51 | ) 52 | 53 | // Next returns the next time this schedule is activated, greater than the given 54 | // time. If no time can be found to satisfy the schedule, return the zero time. 55 | func (s *SpecSchedule) Next(t time.Time) time.Time { 56 | // General approach: 57 | // For Month, Day, Hour, Minute, Second: 58 | // Check if the time value matches. If yes, continue to the next field. 59 | // If the field doesn't match the schedule, then increment the field until it matches. 60 | // While incrementing the field, a wrap-around brings it back to the beginning 61 | // of the field list (since it is necessary to re-verify previous field 62 | // values) 63 | 64 | // Start at the earliest possible time (the upcoming second). 65 | t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond) 66 | 67 | // This flag indicates whether a field has been incremented. 68 | added := false 69 | 70 | // If no time is found within five years, return zero. 71 | yearLimit := t.Year() + 5 72 | 73 | WRAP: 74 | if t.Year() > yearLimit { 75 | return time.Time{} 76 | } 77 | 78 | // Find the first applicable month. 79 | // If it's this month, then do nothing. 80 | for 1< 0 152 | dowMatch bool = 1< 0 153 | ) 154 | if s.Dom&starBit > 0 || s.Dow&starBit > 0 { 155 | return domMatch && dowMatch 156 | } 157 | return domMatch || dowMatch 158 | } 159 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/search.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | ) 7 | 8 | type search struct { 9 | db *DB 10 | whereConditions []map[string]interface{} 11 | orConditions []map[string]interface{} 12 | notConditions []map[string]interface{} 13 | havingConditions []map[string]interface{} 14 | joinConditions []map[string]interface{} 15 | initAttrs []interface{} 16 | assignAttrs []interface{} 17 | selects map[string]interface{} 18 | omits []string 19 | orders []interface{} 20 | preload []searchPreload 21 | offset interface{} 22 | limit interface{} 23 | group string 24 | tableName string 25 | raw bool 26 | Unscoped bool 27 | ignoreOrderQuery bool 28 | } 29 | 30 | type searchPreload struct { 31 | schema string 32 | conditions []interface{} 33 | } 34 | 35 | func (s *search) clone() *search { 36 | clone := *s 37 | return &clone 38 | } 39 | 40 | func (s *search) Where(query interface{}, values ...interface{}) *search { 41 | s.whereConditions = append(s.whereConditions, map[string]interface{}{"query": query, "args": values}) 42 | return s 43 | } 44 | 45 | func (s *search) Not(query interface{}, values ...interface{}) *search { 46 | s.notConditions = append(s.notConditions, map[string]interface{}{"query": query, "args": values}) 47 | return s 48 | } 49 | 50 | func (s *search) Or(query interface{}, values ...interface{}) *search { 51 | s.orConditions = append(s.orConditions, map[string]interface{}{"query": query, "args": values}) 52 | return s 53 | } 54 | 55 | func (s *search) Attrs(attrs ...interface{}) *search { 56 | s.initAttrs = append(s.initAttrs, toSearchableMap(attrs...)) 57 | return s 58 | } 59 | 60 | func (s *search) Assign(attrs ...interface{}) *search { 61 | s.assignAttrs = append(s.assignAttrs, toSearchableMap(attrs...)) 62 | return s 63 | } 64 | 65 | func (s *search) Order(value interface{}, reorder ...bool) *search { 66 | if len(reorder) > 0 && reorder[0] { 67 | s.orders = []interface{}{} 68 | } 69 | 70 | if value != nil && value != "" { 71 | s.orders = append(s.orders, value) 72 | } 73 | return s 74 | } 75 | 76 | var distinctSQLRegexp = regexp.MustCompile(`(?i)distinct[^a-z]+[a-z]+`) 77 | 78 | func (s *search) Select(query interface{}, args ...interface{}) *search { 79 | if distinctSQLRegexp.MatchString(fmt.Sprint(query)) { 80 | s.ignoreOrderQuery = true 81 | } 82 | 83 | s.selects = map[string]interface{}{"query": query, "args": args} 84 | return s 85 | } 86 | 87 | func (s *search) Omit(columns ...string) *search { 88 | s.omits = columns 89 | return s 90 | } 91 | 92 | func (s *search) Limit(limit interface{}) *search { 93 | s.limit = limit 94 | return s 95 | } 96 | 97 | func (s *search) Offset(offset interface{}) *search { 98 | s.offset = offset 99 | return s 100 | } 101 | 102 | func (s *search) Group(query string) *search { 103 | s.group = s.getInterfaceAsSQL(query) 104 | return s 105 | } 106 | 107 | func (s *search) Having(query string, values ...interface{}) *search { 108 | s.havingConditions = append(s.havingConditions, map[string]interface{}{"query": query, "args": values}) 109 | return s 110 | } 111 | 112 | func (s *search) Joins(query string, values ...interface{}) *search { 113 | s.joinConditions = append(s.joinConditions, map[string]interface{}{"query": query, "args": values}) 114 | return s 115 | } 116 | 117 | func (s *search) Preload(schema string, values ...interface{}) *search { 118 | var preloads []searchPreload 119 | for _, preload := range s.preload { 120 | if preload.schema != schema { 121 | preloads = append(preloads, preload) 122 | } 123 | } 124 | preloads = append(preloads, searchPreload{schema, values}) 125 | s.preload = preloads 126 | return s 127 | } 128 | 129 | func (s *search) Raw(b bool) *search { 130 | s.raw = b 131 | return s 132 | } 133 | 134 | func (s *search) unscoped() *search { 135 | s.Unscoped = true 136 | return s 137 | } 138 | 139 | func (s *search) Table(name string) *search { 140 | s.tableName = name 141 | return s 142 | } 143 | 144 | func (s *search) getInterfaceAsSQL(value interface{}) (str string) { 145 | switch value.(type) { 146 | case string, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 147 | str = fmt.Sprintf("%v", value) 148 | default: 149 | s.db.AddError(ErrInvalidSQL) 150 | } 151 | 152 | if str == "-1" { 153 | return "" 154 | } 155 | return 156 | } 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wechat_pusher 2 | 3 | ## 功能列表 4 | - 消息推送 5 | - 模板消息推送 6 | - model -> message.go 7 | - task -> template_task.go 8 | - 图片推送(TODO) 9 | - 文字推送(TODO) 10 | - 图文推送(TODO) 11 | - 日志存储 12 | - 计划任务 13 | 14 | ## 如何开始? 15 | ### 第一步:当然是go get 16 | - `go get github.com/hundredlee/wechat_pusher.git` 17 | 18 | - 项目结构如下: 19 | 20 | ``` 21 | ├── README.md 22 | ├── config 23 | │   └── config.go 24 | ├── config.conf 25 | ├── config.conf.example 26 | ├── enum 27 | │   └── task_type.go 28 | ├── glide.lock 29 | ├── glide.yaml 30 | ├── hlog 31 | │   ├── filelog.go 32 | │   ├── filelog_test.go 33 | │   └── hlog.go 34 | ├── main.go 35 | ├── main.go.example 36 | ├── models 37 | │   ├── message.go 38 | │   └── token.go 39 | ├── redis 40 | │   ├── redis.go 41 | │   └── redis_test.go 42 | ├── statics 43 | │   └── global.go 44 | ├── task 45 | │   ├── task.go 46 | │   └── template_task.go 47 | ├── utils 48 | │   ├── access_token.go 49 | │   ├── crontab.go 50 | │   └── push.go 51 | └── vendor 52 | └── github.com 53 | 54 | ``` 55 | 56 | ### 第二步:创建一个项目 57 | 58 | #### 创建配置文件 59 | - 项目根目录有一个config.conf.example,重命名为config.conf即可 60 | - 内容如下: 61 | 62 | ``` 63 | [WeChat] 64 | APPID= 65 | SECRET= 66 | TOKEN= 67 | 68 | [Redis] 69 | POOL_SIZE= 70 | TIMEOUT= 71 | HOST= 72 | PASS= 73 | DB= 74 | 75 | [Log] 76 | LOG_PATH= 77 | 78 | ``` 79 | 80 | - WeChat部分 81 | - APPID && SECRET && TOKEN 这些是微信开发者必须了解的东西。不细讲 82 | - Redis部分 83 | - POOL_SIZE 连接池大小 ,整型 int 84 | - TIMEOUT 连接超时时间 ,整型 int 85 | - HOST 连接的IP 字符串 string 86 | - PASS 密码 字符串 string 87 | - DB 数据库选择 整型 int 88 | - Log部分 89 | - LOG_PATH 日志存放文件夹,例如值为wechat_log,那么完整的目录应该是 GOPATH/wechat_log 90 | 91 | - 调用的时候这么写: 92 | 93 | ```Go 94 | 95 | conf := config.Instance() 96 | //例如wechat 的 appid 97 | appId := conf.ConMap["WeChat.APPID"] 98 | 99 | ``` 100 | 101 | 102 | #### 模板怎么配置 103 | - 以模板消息作为例子说明: 104 | - message.go 是模板消息的结构 105 | - template_task.go 是将一个模板消息封装成任务(template_task.go 是实现了接口task.go的) 106 | ```Go 107 | mess := models.Message{ 108 | ToUser: "openid", 109 | TemplateId: "templateid", 110 | Url: "url", 111 | Data: models.Data{ 112 | First: models.Raw{"xxx", "#173177"}, 113 | Subject: models.Raw{"xxx", "#173177"}, 114 | Sender: models.Raw{"xxx", "#173177"}, 115 | Remark: models.Raw{"xxx", "#173177"}}} 116 | 117 | //封装成一个任务,TemplateTask表示模板消息任务 118 | task := task.TemplateTask{} 119 | task.SetTask(mess) 120 | 121 | ``` 122 | - 以上代码是模板消息的配置,这个微信开发者应该都能看懂。 123 | 124 | 125 | #### 如何创建一个任务 126 | 127 | - 例如我们要创建一个模板消息定时推送任务 128 | - 第一步,封装任务 129 | - 第二步,添加任务,并设置任务类型、并发执行的个数、失败尝试次数等。 130 | - 第三步,启动任务 131 | - 我们用示例代码演示整个完整的过程 132 | 133 | ```Go 134 | package main 135 | 136 | import ( 137 | "github.com/hundredlee/wechat_pusher/enum" 138 | "github.com/hundredlee/wechat_pusher/models" 139 | "github.com/hundredlee/wechat_pusher/task" 140 | "github.com/hundredlee/wechat_pusher/utils" 141 | "runtime" 142 | ) 143 | 144 | func main() { 145 | 146 | runtime.GOMAXPROCS(runtime.NumCPU()) 147 | var tasks []task.Task 148 | tasks = make([]task.Task, 100) 149 | mess := models.Message{ 150 | ToUser: "oBv9cuLU5zyI27CtzI4VhV6Xabms", 151 | TemplateId: "UXb6s5dahNC5Zt-xQIxbLJG1BdP8mP73LGLhNXl68J8", 152 | Url: "http://baidu.com", 153 | Data: models.Data{ 154 | First: models.Raw{"xxx", "#173177"}, 155 | Subject: models.Raw{"xxx", "#173177"}, 156 | Sender: models.Raw{"xxx", "#173177"}, 157 | Remark: models.Raw{"xxx", "#173177"}}} 158 | task := task.TemplateTask{} 159 | task.SetTask(mess) 160 | 161 | for i := 0; i < 100; i++ { 162 | tasks[i] = &task 163 | } 164 | 165 | utils.NewPush(&utils.Push{ 166 | Tasks:tasks, 167 | TaskType:enum.TASK_TYPE_TEMPLATE, 168 | Retries:4, 169 | BufferNum:10, 170 | }).Add("45 * * * * *") 171 | 172 | utils.StartCron() 173 | 174 | } 175 | 176 | ``` 177 | 178 | 179 | - Add方法里面填写的是执行的时间 180 | - ("10 * * * * *") 表示每分钟的第十秒钟执行一次。 181 | - ("@hourly") 每小时执行一次 182 | - 具体请参照 https://github.com/robfig/cron/blob/master/doc.go 183 | - 本推送服务的计划任务是由 https://github.com/robfig/cron 实现的。 184 | 185 | ### Run 186 | - 很简单,当你组装好所有的task以后,直接运行一句话就可以了。 187 | 188 | ```Go 189 | utils.NewPush(&utils.Push{ 190 | Tasks:tasks, 191 | TaskType:enum.TASK_TYPE_TEMPLATE, 192 | Retries:4, 193 | BufferNum:10, 194 | }).Add("45 * * * * *") 195 | 196 | utils.StartCron() 197 | 198 | ``` 199 | 200 | - `utils.StartCron()` 201 | 202 | ## Contributor 203 | - HundredLee https://github.com/hundredlee 204 | - Cospotato https://github.com/cospotato (感谢提供Redis连接池的解决思路) 205 | 206 | 207 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/callback_system_test.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "reflect" 5 | "runtime" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func equalFuncs(funcs []*func(s *Scope), fnames []string) bool { 11 | var names []string 12 | for _, f := range funcs { 13 | fnames := strings.Split(runtime.FuncForPC(reflect.ValueOf(*f).Pointer()).Name(), ".") 14 | names = append(names, fnames[len(fnames)-1]) 15 | } 16 | return reflect.DeepEqual(names, fnames) 17 | } 18 | 19 | func create(s *Scope) {} 20 | func beforeCreate1(s *Scope) {} 21 | func beforeCreate2(s *Scope) {} 22 | func afterCreate1(s *Scope) {} 23 | func afterCreate2(s *Scope) {} 24 | 25 | func TestRegisterCallback(t *testing.T) { 26 | var callback = &Callback{} 27 | 28 | callback.Create().Register("before_create1", beforeCreate1) 29 | callback.Create().Register("before_create2", beforeCreate2) 30 | callback.Create().Register("create", create) 31 | callback.Create().Register("after_create1", afterCreate1) 32 | callback.Create().Register("after_create2", afterCreate2) 33 | 34 | if !equalFuncs(callback.creates, []string{"beforeCreate1", "beforeCreate2", "create", "afterCreate1", "afterCreate2"}) { 35 | t.Errorf("register callback") 36 | } 37 | } 38 | 39 | func TestRegisterCallbackWithOrder(t *testing.T) { 40 | var callback1 = &Callback{} 41 | callback1.Create().Register("before_create1", beforeCreate1) 42 | callback1.Create().Register("create", create) 43 | callback1.Create().Register("after_create1", afterCreate1) 44 | callback1.Create().Before("after_create1").Register("after_create2", afterCreate2) 45 | if !equalFuncs(callback1.creates, []string{"beforeCreate1", "create", "afterCreate2", "afterCreate1"}) { 46 | t.Errorf("register callback with order") 47 | } 48 | 49 | var callback2 = &Callback{} 50 | 51 | callback2.Update().Register("create", create) 52 | callback2.Update().Before("create").Register("before_create1", beforeCreate1) 53 | callback2.Update().After("after_create2").Register("after_create1", afterCreate1) 54 | callback2.Update().Before("before_create1").Register("before_create2", beforeCreate2) 55 | callback2.Update().Register("after_create2", afterCreate2) 56 | 57 | if !equalFuncs(callback2.updates, []string{"beforeCreate2", "beforeCreate1", "create", "afterCreate2", "afterCreate1"}) { 58 | t.Errorf("register callback with order") 59 | } 60 | } 61 | 62 | func TestRegisterCallbackWithComplexOrder(t *testing.T) { 63 | var callback1 = &Callback{} 64 | 65 | callback1.Query().Before("after_create1").After("before_create1").Register("create", create) 66 | callback1.Query().Register("before_create1", beforeCreate1) 67 | callback1.Query().Register("after_create1", afterCreate1) 68 | 69 | if !equalFuncs(callback1.queries, []string{"beforeCreate1", "create", "afterCreate1"}) { 70 | t.Errorf("register callback with order") 71 | } 72 | 73 | var callback2 = &Callback{} 74 | 75 | callback2.Delete().Before("after_create1").After("before_create1").Register("create", create) 76 | callback2.Delete().Before("create").Register("before_create1", beforeCreate1) 77 | callback2.Delete().After("before_create1").Register("before_create2", beforeCreate2) 78 | callback2.Delete().Register("after_create1", afterCreate1) 79 | callback2.Delete().After("after_create1").Register("after_create2", afterCreate2) 80 | 81 | if !equalFuncs(callback2.deletes, []string{"beforeCreate1", "beforeCreate2", "create", "afterCreate1", "afterCreate2"}) { 82 | t.Errorf("register callback with order") 83 | } 84 | } 85 | 86 | func replaceCreate(s *Scope) {} 87 | 88 | func TestReplaceCallback(t *testing.T) { 89 | var callback = &Callback{} 90 | 91 | callback.Create().Before("after_create1").After("before_create1").Register("create", create) 92 | callback.Create().Register("before_create1", beforeCreate1) 93 | callback.Create().Register("after_create1", afterCreate1) 94 | callback.Create().Replace("create", replaceCreate) 95 | 96 | if !equalFuncs(callback.creates, []string{"beforeCreate1", "replaceCreate", "afterCreate1"}) { 97 | t.Errorf("replace callback") 98 | } 99 | } 100 | 101 | func TestRemoveCallback(t *testing.T) { 102 | var callback = &Callback{} 103 | 104 | callback.Create().Before("after_create1").After("before_create1").Register("create", create) 105 | callback.Create().Register("before_create1", beforeCreate1) 106 | callback.Create().Register("after_create1", afterCreate1) 107 | callback.Create().Remove("create") 108 | 109 | if !equalFuncs(callback.creates, []string{"beforeCreate1", "afterCreate1"}) { 110 | t.Errorf("remove callback") 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/reply_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | type valueError struct { 26 | v interface{} 27 | err error 28 | } 29 | 30 | func ve(v interface{}, err error) valueError { 31 | return valueError{v, err} 32 | } 33 | 34 | var replyTests = []struct { 35 | name interface{} 36 | actual valueError 37 | expected valueError 38 | }{ 39 | { 40 | "ints([v1, v2])", 41 | ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)), 42 | ve([]int{4, 5}, nil), 43 | }, 44 | { 45 | "ints(nil)", 46 | ve(redis.Ints(nil, nil)), 47 | ve([]int(nil), redis.ErrNil), 48 | }, 49 | { 50 | "strings([v1, v2])", 51 | ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 52 | ve([]string{"v1", "v2"}, nil), 53 | }, 54 | { 55 | "strings(nil)", 56 | ve(redis.Strings(nil, nil)), 57 | ve([]string(nil), redis.ErrNil), 58 | }, 59 | { 60 | "byteslices([v1, v2])", 61 | ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 62 | ve([][]byte{[]byte("v1"), []byte("v2")}, nil), 63 | }, 64 | { 65 | "byteslices(nil)", 66 | ve(redis.ByteSlices(nil, nil)), 67 | ve([][]byte(nil), redis.ErrNil), 68 | }, 69 | { 70 | "values([v1, v2])", 71 | ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 72 | ve([]interface{}{[]byte("v1"), []byte("v2")}, nil), 73 | }, 74 | { 75 | "values(nil)", 76 | ve(redis.Values(nil, nil)), 77 | ve([]interface{}(nil), redis.ErrNil), 78 | }, 79 | { 80 | "float64(1.0)", 81 | ve(redis.Float64([]byte("1.0"), nil)), 82 | ve(float64(1.0), nil), 83 | }, 84 | { 85 | "float64(nil)", 86 | ve(redis.Float64(nil, nil)), 87 | ve(float64(0.0), redis.ErrNil), 88 | }, 89 | { 90 | "uint64(1)", 91 | ve(redis.Uint64(int64(1), nil)), 92 | ve(uint64(1), nil), 93 | }, 94 | { 95 | "uint64(-1)", 96 | ve(redis.Uint64(int64(-1), nil)), 97 | ve(uint64(0), redis.ErrNegativeInt), 98 | }, 99 | { 100 | "positions([[1, 2], nil, [3, 4]])", 101 | ve(redis.Positions([]interface{}{[]interface{}{[]byte("1"), []byte("2")}, nil, []interface{}{[]byte("3"), []byte("4")}}, nil)), 102 | ve([]*[2]float64{{1.0, 2.0}, nil, {3.0, 4.0}}, nil), 103 | }, 104 | } 105 | 106 | func TestReply(t *testing.T) { 107 | for _, rt := range replyTests { 108 | if rt.actual.err != rt.expected.err { 109 | t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err) 110 | continue 111 | } 112 | if !reflect.DeepEqual(rt.actual.v, rt.expected.v) { 113 | t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v) 114 | } 115 | } 116 | } 117 | 118 | // dial wraps DialDefaultServer() with a more suitable function name for examples. 119 | func dial() (redis.Conn, error) { 120 | return redis.DialDefaultServer() 121 | } 122 | 123 | func ExampleBool() { 124 | c, err := dial() 125 | if err != nil { 126 | fmt.Println(err) 127 | return 128 | } 129 | defer c.Close() 130 | 131 | c.Do("SET", "foo", 1) 132 | exists, _ := redis.Bool(c.Do("EXISTS", "foo")) 133 | fmt.Printf("%#v\n", exists) 134 | // Output: 135 | // true 136 | } 137 | 138 | func ExampleInt() { 139 | c, err := dial() 140 | if err != nil { 141 | fmt.Println(err) 142 | return 143 | } 144 | defer c.Close() 145 | 146 | c.Do("SET", "k1", 1) 147 | n, _ := redis.Int(c.Do("GET", "k1")) 148 | fmt.Printf("%#v\n", n) 149 | n, _ = redis.Int(c.Do("INCR", "k1")) 150 | fmt.Printf("%#v\n", n) 151 | // Output: 152 | // 1 153 | // 2 154 | } 155 | 156 | func ExampleInts() { 157 | c, err := dial() 158 | if err != nil { 159 | fmt.Println(err) 160 | return 161 | } 162 | defer c.Close() 163 | 164 | c.Do("SADD", "set_with_integers", 4, 5, 6) 165 | ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers")) 166 | fmt.Printf("%#v\n", ints) 167 | // Output: 168 | // []int{4, 5, 6} 169 | } 170 | 171 | func ExampleString() { 172 | c, err := dial() 173 | if err != nil { 174 | fmt.Println(err) 175 | return 176 | } 177 | defer c.Close() 178 | 179 | c.Do("SET", "hello", "world") 180 | s, err := redis.String(c.Do("GET", "hello")) 181 | fmt.Printf("%#v\n", s) 182 | // Output: 183 | // "world" 184 | } 185 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "sync" 21 | "testing" 22 | 23 | "github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | func publish(channel, value interface{}) { 27 | c, err := dial() 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | defer c.Close() 33 | c.Do("PUBLISH", channel, value) 34 | } 35 | 36 | // Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine. 37 | func ExamplePubSubConn() { 38 | c, err := dial() 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | defer c.Close() 44 | var wg sync.WaitGroup 45 | wg.Add(2) 46 | 47 | psc := redis.PubSubConn{Conn: c} 48 | 49 | // This goroutine receives and prints pushed notifications from the server. 50 | // The goroutine exits when the connection is unsubscribed from all 51 | // channels or there is an error. 52 | go func() { 53 | defer wg.Done() 54 | for { 55 | switch n := psc.Receive().(type) { 56 | case redis.Message: 57 | fmt.Printf("Message: %s %s\n", n.Channel, n.Data) 58 | case redis.PMessage: 59 | fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) 60 | case redis.Subscription: 61 | fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) 62 | if n.Count == 0 { 63 | return 64 | } 65 | case error: 66 | fmt.Printf("error: %v\n", n) 67 | return 68 | } 69 | } 70 | }() 71 | 72 | // This goroutine manages subscriptions for the connection. 73 | go func() { 74 | defer wg.Done() 75 | 76 | psc.Subscribe("example") 77 | psc.PSubscribe("p*") 78 | 79 | // The following function calls publish a message using another 80 | // connection to the Redis server. 81 | publish("example", "hello") 82 | publish("example", "world") 83 | publish("pexample", "foo") 84 | publish("pexample", "bar") 85 | 86 | // Unsubscribe from all connections. This will cause the receiving 87 | // goroutine to exit. 88 | psc.Unsubscribe() 89 | psc.PUnsubscribe() 90 | }() 91 | 92 | wg.Wait() 93 | 94 | // Output: 95 | // Subscription: subscribe example 1 96 | // Subscription: psubscribe p* 2 97 | // Message: example hello 98 | // Message: example world 99 | // PMessage: p* pexample foo 100 | // PMessage: p* pexample bar 101 | // Subscription: unsubscribe example 1 102 | // Subscription: punsubscribe p* 0 103 | } 104 | 105 | func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { 106 | actual := c.Receive() 107 | if !reflect.DeepEqual(actual, expected) { 108 | t.Errorf("%s = %v, want %v", message, actual, expected) 109 | } 110 | } 111 | 112 | func TestPushed(t *testing.T) { 113 | pc, err := redis.DialDefaultServer() 114 | if err != nil { 115 | t.Fatalf("error connection to database, %v", err) 116 | } 117 | defer pc.Close() 118 | 119 | sc, err := redis.DialDefaultServer() 120 | if err != nil { 121 | t.Fatalf("error connection to database, %v", err) 122 | } 123 | defer sc.Close() 124 | 125 | c := redis.PubSubConn{Conn: sc} 126 | 127 | c.Subscribe("c1") 128 | expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1}) 129 | c.Subscribe("c2") 130 | expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2}) 131 | c.PSubscribe("p1") 132 | expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3}) 133 | c.PSubscribe("p2") 134 | expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4}) 135 | c.PUnsubscribe() 136 | expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3}) 137 | expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2}) 138 | 139 | pc.Do("PUBLISH", "c1", "hello") 140 | expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")}) 141 | 142 | c.Ping("hello") 143 | expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"}) 144 | 145 | c.Conn.Send("PING") 146 | c.Conn.Flush() 147 | expectPushed(t, c, `Send("PING")`, redis.Pong{}) 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/test_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bufio" 19 | "errors" 20 | "flag" 21 | "fmt" 22 | "io" 23 | "io/ioutil" 24 | "os" 25 | "os/exec" 26 | "strconv" 27 | "strings" 28 | "sync" 29 | "testing" 30 | "time" 31 | ) 32 | 33 | func SetNowFunc(f func() time.Time) { 34 | nowFunc = f 35 | } 36 | 37 | var ( 38 | ErrNegativeInt = errNegativeInt 39 | 40 | serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary") 41 | serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers") 42 | serverLogName = flag.String("redis-wlog", "", "Write Redis server logs to `filename`") 43 | serverLog = ioutil.Discard 44 | 45 | defaultServerMu sync.Mutex 46 | defaultServer *Server 47 | defaultServerErr error 48 | ) 49 | 50 | type Server struct { 51 | name string 52 | cmd *exec.Cmd 53 | done chan struct{} 54 | } 55 | 56 | func NewServer(name string, args ...string) (*Server, error) { 57 | s := &Server{ 58 | name: name, 59 | cmd: exec.Command(*serverPath, args...), 60 | done: make(chan struct{}), 61 | } 62 | 63 | r, err := s.cmd.StdoutPipe() 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | err = s.cmd.Start() 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | ready := make(chan error, 1) 74 | go s.watch(r, ready) 75 | 76 | select { 77 | case err = <-ready: 78 | case <-time.After(time.Second * 10): 79 | err = errors.New("timeout waiting for server to start") 80 | } 81 | 82 | if err != nil { 83 | s.Stop() 84 | return nil, err 85 | } 86 | 87 | return s, nil 88 | } 89 | 90 | func (s *Server) watch(r io.Reader, ready chan error) { 91 | fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name) 92 | var listening bool 93 | var text string 94 | scn := bufio.NewScanner(r) 95 | for scn.Scan() { 96 | text = scn.Text() 97 | fmt.Fprintf(serverLog, "%s\n", text) 98 | if !listening { 99 | if strings.Contains(text, "The server is now ready to accept connections on port") { 100 | listening = true 101 | ready <- nil 102 | } 103 | } 104 | } 105 | if !listening { 106 | ready <- fmt.Errorf("server exited: %s", text) 107 | } 108 | s.cmd.Wait() 109 | fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name) 110 | close(s.done) 111 | } 112 | 113 | func (s *Server) Stop() { 114 | s.cmd.Process.Signal(os.Interrupt) 115 | <-s.done 116 | } 117 | 118 | // stopDefaultServer stops the server created by DialDefaultServer. 119 | func stopDefaultServer() { 120 | defaultServerMu.Lock() 121 | defer defaultServerMu.Unlock() 122 | if defaultServer != nil { 123 | defaultServer.Stop() 124 | defaultServer = nil 125 | } 126 | } 127 | 128 | // startDefaultServer starts the default server if not already running. 129 | func startDefaultServer() error { 130 | defaultServerMu.Lock() 131 | defer defaultServerMu.Unlock() 132 | if defaultServer != nil || defaultServerErr != nil { 133 | return defaultServerErr 134 | } 135 | defaultServer, defaultServerErr = NewServer( 136 | "default", 137 | "--port", strconv.Itoa(*serverBasePort), 138 | "--save", "", 139 | "--appendonly", "no") 140 | return defaultServerErr 141 | } 142 | 143 | // DialDefaultServer starts the test server if not already started and dials a 144 | // connection to the server. 145 | func DialDefaultServer() (Conn, error) { 146 | if err := startDefaultServer(); err != nil { 147 | return nil, err 148 | } 149 | c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second)) 150 | if err != nil { 151 | return nil, err 152 | } 153 | c.Do("FLUSHDB") 154 | return c, nil 155 | } 156 | 157 | func TestMain(m *testing.M) { 158 | os.Exit(func() int { 159 | flag.Parse() 160 | 161 | var f *os.File 162 | if *serverLogName != "" { 163 | var err error 164 | f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600) 165 | if err != nil { 166 | fmt.Fprintf(os.Stderr, "Error opening redis-wlog: %v\n", err) 167 | return 1 168 | } 169 | defer f.Close() 170 | serverLog = f 171 | } 172 | 173 | defer stopDefaultServer() 174 | 175 | return m.Run() 176 | }()) 177 | } 178 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialect_common.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "regexp" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // DefaultForeignKeyNamer contains the default foreign key name generator method 13 | type DefaultForeignKeyNamer struct { 14 | } 15 | 16 | type commonDialect struct { 17 | db SQLCommon 18 | DefaultForeignKeyNamer 19 | } 20 | 21 | func init() { 22 | RegisterDialect("common", &commonDialect{}) 23 | } 24 | 25 | func (commonDialect) GetName() string { 26 | return "common" 27 | } 28 | 29 | func (s *commonDialect) SetDB(db SQLCommon) { 30 | s.db = db 31 | } 32 | 33 | func (commonDialect) BindVar(i int) string { 34 | return "$$$" // ? 35 | } 36 | 37 | func (commonDialect) Quote(key string) string { 38 | return fmt.Sprintf(`"%s"`, key) 39 | } 40 | 41 | func (s *commonDialect) DataTypeOf(field *StructField) string { 42 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s) 43 | 44 | if sqlType == "" { 45 | switch dataValue.Kind() { 46 | case reflect.Bool: 47 | sqlType = "BOOLEAN" 48 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr: 49 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok { 50 | sqlType = "INTEGER AUTO_INCREMENT" 51 | } else { 52 | sqlType = "INTEGER" 53 | } 54 | case reflect.Int64, reflect.Uint64: 55 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok { 56 | sqlType = "BIGINT AUTO_INCREMENT" 57 | } else { 58 | sqlType = "BIGINT" 59 | } 60 | case reflect.Float32, reflect.Float64: 61 | sqlType = "FLOAT" 62 | case reflect.String: 63 | if size > 0 && size < 65532 { 64 | sqlType = fmt.Sprintf("VARCHAR(%d)", size) 65 | } else { 66 | sqlType = "VARCHAR(65532)" 67 | } 68 | case reflect.Struct: 69 | if _, ok := dataValue.Interface().(time.Time); ok { 70 | sqlType = "TIMESTAMP" 71 | } 72 | default: 73 | if _, ok := dataValue.Interface().([]byte); ok { 74 | if size > 0 && size < 65532 { 75 | sqlType = fmt.Sprintf("BINARY(%d)", size) 76 | } else { 77 | sqlType = "BINARY(65532)" 78 | } 79 | } 80 | } 81 | } 82 | 83 | if sqlType == "" { 84 | panic(fmt.Sprintf("invalid sql type %s (%s) for commonDialect", dataValue.Type().Name(), dataValue.Kind().String())) 85 | } 86 | 87 | if strings.TrimSpace(additionalType) == "" { 88 | return sqlType 89 | } 90 | return fmt.Sprintf("%v %v", sqlType, additionalType) 91 | } 92 | 93 | func (s commonDialect) HasIndex(tableName string, indexName string) bool { 94 | var count int 95 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = ? AND table_name = ? AND index_name = ?", s.CurrentDatabase(), tableName, indexName).Scan(&count) 96 | return count > 0 97 | } 98 | 99 | func (s commonDialect) RemoveIndex(tableName string, indexName string) error { 100 | _, err := s.db.Exec(fmt.Sprintf("DROP INDEX %v", indexName)) 101 | return err 102 | } 103 | 104 | func (s commonDialect) HasForeignKey(tableName string, foreignKeyName string) bool { 105 | return false 106 | } 107 | 108 | func (s commonDialect) HasTable(tableName string) bool { 109 | var count int 110 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_name = ?", s.CurrentDatabase(), tableName).Scan(&count) 111 | return count > 0 112 | } 113 | 114 | func (s commonDialect) HasColumn(tableName string, columnName string) bool { 115 | var count int 116 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = ? AND table_name = ? AND column_name = ?", s.CurrentDatabase(), tableName, columnName).Scan(&count) 117 | return count > 0 118 | } 119 | 120 | func (s commonDialect) CurrentDatabase() (name string) { 121 | s.db.QueryRow("SELECT DATABASE()").Scan(&name) 122 | return 123 | } 124 | 125 | func (commonDialect) LimitAndOffsetSQL(limit, offset interface{}) (sql string) { 126 | if limit != nil { 127 | if parsedLimit, err := strconv.ParseInt(fmt.Sprint(limit), 0, 0); err == nil && parsedLimit >= 0 { 128 | sql += fmt.Sprintf(" LIMIT %d", parsedLimit) 129 | } 130 | } 131 | if offset != nil { 132 | if parsedOffset, err := strconv.ParseInt(fmt.Sprint(offset), 0, 0); err == nil && parsedOffset >= 0 { 133 | sql += fmt.Sprintf(" OFFSET %d", parsedOffset) 134 | } 135 | } 136 | return 137 | } 138 | 139 | func (commonDialect) SelectFromDummyTable() string { 140 | return "" 141 | } 142 | 143 | func (commonDialect) LastInsertIDReturningSuffix(tableName, columnName string) string { 144 | return "" 145 | } 146 | 147 | func (DefaultForeignKeyNamer) BuildForeignKeyName(tableName, field, dest string) string { 148 | keyName := fmt.Sprintf("%s_%s_%s_foreign", tableName, field, dest) 149 | keyName = regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(keyName, "_") 150 | return keyName 151 | } 152 | 153 | // IsByteArrayOrSlice returns true of the reflected value is an array or slice 154 | func IsByteArrayOrSlice(value reflect.Value) bool { 155 | return (value.Kind() == reflect.Array || value.Kind() == reflect.Slice) && value.Type().Elem() == reflect.TypeOf(uint8(0)) 156 | } 157 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/gorm/dialect_mysql.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | "reflect" 7 | "regexp" 8 | "strings" 9 | "time" 10 | "unicode/utf8" 11 | ) 12 | 13 | type mysql struct { 14 | commonDialect 15 | } 16 | 17 | func init() { 18 | RegisterDialect("mysql", &mysql{}) 19 | } 20 | 21 | func (mysql) GetName() string { 22 | return "mysql" 23 | } 24 | 25 | func (mysql) Quote(key string) string { 26 | return fmt.Sprintf("`%s`", key) 27 | } 28 | 29 | // Get Data Type for MySQL Dialect 30 | func (s *mysql) DataTypeOf(field *StructField) string { 31 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s) 32 | 33 | // MySQL allows only one auto increment column per table, and it must 34 | // be a KEY column. 35 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok { 36 | if _, ok = field.TagSettings["INDEX"]; !ok && !field.IsPrimaryKey { 37 | delete(field.TagSettings, "AUTO_INCREMENT") 38 | } 39 | } 40 | 41 | if sqlType == "" { 42 | switch dataValue.Kind() { 43 | case reflect.Bool: 44 | sqlType = "boolean" 45 | case reflect.Int8: 46 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 47 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 48 | sqlType = "tinyint AUTO_INCREMENT" 49 | } else { 50 | sqlType = "tinyint" 51 | } 52 | case reflect.Int, reflect.Int16, reflect.Int32: 53 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 54 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 55 | sqlType = "int AUTO_INCREMENT" 56 | } else { 57 | sqlType = "int" 58 | } 59 | case reflect.Uint8: 60 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 61 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 62 | sqlType = "tinyint unsigned AUTO_INCREMENT" 63 | } else { 64 | sqlType = "tinyint unsigned" 65 | } 66 | case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uintptr: 67 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 68 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 69 | sqlType = "int unsigned AUTO_INCREMENT" 70 | } else { 71 | sqlType = "int unsigned" 72 | } 73 | case reflect.Int64: 74 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 75 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 76 | sqlType = "bigint AUTO_INCREMENT" 77 | } else { 78 | sqlType = "bigint" 79 | } 80 | case reflect.Uint64: 81 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey { 82 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT" 83 | sqlType = "bigint unsigned AUTO_INCREMENT" 84 | } else { 85 | sqlType = "bigint unsigned" 86 | } 87 | case reflect.Float32, reflect.Float64: 88 | sqlType = "double" 89 | case reflect.String: 90 | if size > 0 && size < 65532 { 91 | sqlType = fmt.Sprintf("varchar(%d)", size) 92 | } else { 93 | sqlType = "longtext" 94 | } 95 | case reflect.Struct: 96 | if _, ok := dataValue.Interface().(time.Time); ok { 97 | if _, ok := field.TagSettings["NOT NULL"]; ok { 98 | sqlType = "timestamp" 99 | } else { 100 | sqlType = "timestamp NULL" 101 | } 102 | } 103 | default: 104 | if IsByteArrayOrSlice(dataValue) { 105 | if size > 0 && size < 65532 { 106 | sqlType = fmt.Sprintf("varbinary(%d)", size) 107 | } else { 108 | sqlType = "longblob" 109 | } 110 | } 111 | } 112 | } 113 | 114 | if sqlType == "" { 115 | panic(fmt.Sprintf("invalid sql type %s (%s) for mysql", dataValue.Type().Name(), dataValue.Kind().String())) 116 | } 117 | 118 | if strings.TrimSpace(additionalType) == "" { 119 | return sqlType 120 | } 121 | return fmt.Sprintf("%v %v", sqlType, additionalType) 122 | } 123 | 124 | func (s mysql) RemoveIndex(tableName string, indexName string) error { 125 | _, err := s.db.Exec(fmt.Sprintf("DROP INDEX %v ON %v", indexName, s.Quote(tableName))) 126 | return err 127 | } 128 | 129 | func (s mysql) HasForeignKey(tableName string, foreignKeyName string) bool { 130 | var count int 131 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=? AND TABLE_NAME=? AND CONSTRAINT_NAME=? AND CONSTRAINT_TYPE='FOREIGN KEY'", s.CurrentDatabase(), tableName, foreignKeyName).Scan(&count) 132 | return count > 0 133 | } 134 | 135 | func (s mysql) CurrentDatabase() (name string) { 136 | s.db.QueryRow("SELECT DATABASE()").Scan(&name) 137 | return 138 | } 139 | 140 | func (mysql) SelectFromDummyTable() string { 141 | return "FROM DUAL" 142 | } 143 | 144 | func (s mysql) BuildForeignKeyName(tableName, field, dest string) string { 145 | keyName := s.commonDialect.BuildForeignKeyName(tableName, field, dest) 146 | if utf8.RuneCountInString(keyName) <= 64 { 147 | return keyName 148 | } 149 | h := sha1.New() 150 | h.Write([]byte(keyName)) 151 | bs := h.Sum(nil) 152 | 153 | // sha1 is 40 digits, keep first 24 characters of destination 154 | destRunes := []rune(regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(dest, "_")) 155 | if len(destRunes) > 24 { 156 | destRunes = destRunes[:24] 157 | } 158 | 159 | return fmt.Sprintf("%s%x", string(destRunes), bs) 160 | } 161 | -------------------------------------------------------------------------------- /vendor/github.com/robfig/cron/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package cron implements a cron spec parser and job runner. 3 | 4 | Usage 5 | 6 | Callers may register Funcs to be invoked on a given schedule. Cron will run 7 | them in their own goroutines. 8 | 9 | c := cron.New() 10 | c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") }) 11 | c.AddFunc("@hourly", func() { fmt.Println("Every hour") }) 12 | c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") }) 13 | c.Start() 14 | .. 15 | // Funcs are invoked in their own goroutine, asynchronously. 16 | ... 17 | // Funcs may also be added to a running Cron 18 | c.AddFunc("@daily", func() { fmt.Println("Every day") }) 19 | .. 20 | // Inspect the cron job entries' next and previous run times. 21 | inspect(c.Entries()) 22 | .. 23 | c.Stop() // Stop the scheduler (does not stop any jobs already running). 24 | 25 | CRON Expression Format 26 | 27 | A cron expression represents a set of times, using 6 space-separated fields. 28 | 29 | Field name | Mandatory? | Allowed values | Allowed special characters 30 | ---------- | ---------- | -------------- | -------------------------- 31 | Seconds | Yes | 0-59 | * / , - 32 | Minutes | Yes | 0-59 | * / , - 33 | Hours | Yes | 0-23 | * / , - 34 | Day of month | Yes | 1-31 | * / , - ? 35 | Month | Yes | 1-12 or JAN-DEC | * / , - 36 | Day of week | Yes | 0-6 or SUN-SAT | * / , - ? 37 | 38 | Note: Month and Day-of-week field values are case insensitive. "SUN", "Sun", 39 | and "sun" are equally accepted. 40 | 41 | Special Characters 42 | 43 | Asterisk ( * ) 44 | 45 | The asterisk indicates that the cron expression will match for all values of the 46 | field; e.g., using an asterisk in the 5th field (month) would indicate every 47 | month. 48 | 49 | Slash ( / ) 50 | 51 | Slashes are used to describe increments of ranges. For example 3-59/15 in the 52 | 1st field (minutes) would indicate the 3rd minute of the hour and every 15 53 | minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...", 54 | that is, an increment over the largest possible range of the field. The form 55 | "N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the 56 | increment until the end of that specific range. It does not wrap around. 57 | 58 | Comma ( , ) 59 | 60 | Commas are used to separate items of a list. For example, using "MON,WED,FRI" in 61 | the 5th field (day of week) would mean Mondays, Wednesdays and Fridays. 62 | 63 | Hyphen ( - ) 64 | 65 | Hyphens are used to define ranges. For example, 9-17 would indicate every 66 | hour between 9am and 5pm inclusive. 67 | 68 | Question mark ( ? ) 69 | 70 | Question mark may be used instead of '*' for leaving either day-of-month or 71 | day-of-week blank. 72 | 73 | Predefined schedules 74 | 75 | You may use one of several pre-defined schedules in place of a cron expression. 76 | 77 | Entry | Description | Equivalent To 78 | ----- | ----------- | ------------- 79 | @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * 80 | @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * 81 | @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 82 | @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * 83 | @hourly | Run once an hour, beginning of hour | 0 0 * * * * 84 | 85 | Intervals 86 | 87 | You may also schedule a job to execute at fixed intervals, starting at the time it's added 88 | or cron is run. This is supported by formatting the cron spec like this: 89 | 90 | @every 91 | 92 | where "duration" is a string accepted by time.ParseDuration 93 | (http://golang.org/pkg/time/#ParseDuration). 94 | 95 | For example, "@every 1h30m10s" would indicate a schedule that activates immediately, 96 | and then every 1 hour, 30 minutes, 10 seconds. 97 | 98 | Note: The interval does not take the job runtime into account. For example, 99 | if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, 100 | it will have only 2 minutes of idle time between each run. 101 | 102 | Time zones 103 | 104 | All interpretation and scheduling is done in the machine's local time zone (as 105 | provided by the Go time package (http://www.golang.org/pkg/time). 106 | 107 | Be aware that jobs scheduled during daylight-savings leap-ahead transitions will 108 | not be run! 109 | 110 | Thread safety 111 | 112 | Since the Cron service runs concurrently with the calling code, some amount of 113 | care must be taken to ensure proper synchronization. 114 | 115 | All cron methods are designed to be correctly synchronized as long as the caller 116 | ensures that invocations have a clear happens-before ordering between them. 117 | 118 | Implementation 119 | 120 | Cron entries are stored in an array, sorted by their next activation time. Cron 121 | sleeps until the next job is due to be run. 122 | 123 | Upon waking: 124 | - it runs each entry that is active on that second 125 | - it calculates the next run times for the jobs that were run 126 | - it re-sorts the array of entries by next activation time. 127 | - it goes to sleep until the soonest job. 128 | */ 129 | package cron 130 | --------------------------------------------------------------------------------