Merge branch 'master' into develop

This commit is contained in:
John Guo
2021-01-16 20:42:44 +08:00
58 changed files with 1040 additions and 409 deletions

View File

@ -8,6 +8,7 @@ import (
func main() {
s := g.Server()
s.SetSessionCookieMaxAge(0)
s.Group("/", func(group *ghttp.RouterGroup) {
group.GET("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -83,9 +83,13 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error
func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
sql, args = formatSql(sql, args)
sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args)
ctx := c.DB.GetCtx()
if c.GetConfig().QueryTimeout > 0 {
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
}
if c.DB.GetDebug() {
mTime1 := gtime.TimestampMilli()
rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...)
rows, err = link.QueryContext(ctx, sql, args...)
mTime2 := gtime.TimestampMilli()
s := &Sql{
Sql: sql,
@ -98,7 +102,7 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro
}
c.writeSqlToLogger(s)
} else {
rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...)
rows, err = link.QueryContext(ctx, sql, args...)
}
if err == nil {
return rows, nil
@ -123,10 +127,14 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err
func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) {
sql, args = formatSql(sql, args)
sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args)
ctx := c.DB.GetCtx()
if c.GetConfig().ExecTimeout > 0 {
ctx, _ = context.WithTimeout(ctx, c.GetConfig().ExecTimeout)
}
if c.DB.GetDebug() {
mTime1 := gtime.TimestampMilli()
if !c.DB.GetDryRun() {
result, err = link.ExecContext(c.DB.GetCtx(), sql, args...)
result, err = link.ExecContext(ctx, sql, args...)
} else {
result = new(SqlResult)
}
@ -143,7 +151,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
c.writeSqlToLogger(s)
} else {
if !c.DB.GetDryRun() {
result, err = link.ExecContext(c.DB.GetCtx(), sql, args...)
result, err = link.ExecContext(ctx, sql, args...)
} else {
result = new(SqlResult)
}
@ -178,7 +186,11 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) {
// doPrepare calls prepare function on given link object and returns the statement object.
func (c *Core) DoPrepare(link Link, sql string) (*sql.Stmt, error) {
return link.PrepareContext(c.DB.GetCtx(), sql)
ctx := c.DB.GetCtx()
if c.GetConfig().QueryTimeout > 0 {
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
}
return link.PrepareContext(ctx, sql)
}
// GetAll queries and returns data records from database.
@ -320,7 +332,11 @@ func (c *Core) Begin() (*TX, error) {
if master, err := c.DB.Master(); err != nil {
return nil, err
} else {
if tx, err := master.Begin(); err == nil {
ctx := c.DB.GetCtx()
if c.GetConfig().TranTimeout > 0 {
ctx, _ = context.WithTimeout(ctx, c.GetConfig().TranTimeout)
}
if tx, err := master.BeginTx(ctx, nil); err == nil {
return &TX{
db: c.DB,
tx: tx,

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -16,8 +16,7 @@ import (
)
const (
DEFAULT_GROUP_NAME = "default" // Deprecated, use DefaultGroupName instead.
DefaultGroupName = "default" // Default group name.
DefaultGroupName = "default" // Default group name.
)
// Config is the configuration management object.
@ -28,26 +27,30 @@ type ConfigGroup []ConfigNode
// ConfigNode is configuration for one node.
type ConfigNode struct {
Host string // Host of server, ip or domain like: 127.0.0.1, localhost
Port string // Port, it's commonly 3306.
User string // Authentication username.
Pass string // Authentication password.
Name string // Default used database name.
Type string // Database type: mysql, sqlite, mssql, pgsql, oracle.
Role string // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
Debug bool // (Optional) Debug mode enables debug information logging and output.
Prefix string // (Optional) Table prefix.
DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node.
Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database.
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
MaxIdleConnCount int `json:"maxidle"` // (Optional) Max idle connection configuration for underlying connection pool.
MaxOpenConnCount int `json:"maxopen"` // (Optional) Max open connection configuration for underlying connection pool.
MaxConnLifetime time.Duration `json:"maxlifetime"` // (Optional) Max connection TTL configuration for underlying connection pool.
CreatedAt string // (Optional) The filed name of table for automatic-filled created datetime.
UpdatedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
DeletedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
TimeMaintainDisabled bool // (Optional) Disable the automatic time maintaining feature.
Host string `json:"host"` // Host of server, ip or domain like: 127.0.0.1, localhost
Port string `json:"port"` // Port, it's commonly 3306.
User string `json:"user"` // Authentication username.
Pass string `json:"pass"` // Authentication password.
Name string `json:"name"` // Default used database name.
Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle.
Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output.
Prefix string `json:"prefix"` // (Optional) Table prefix.
DryRun bool `json:"dryRun"` // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node.
Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database.
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool.
MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool.
MaxConnLifetime time.Duration `json:"maxLifetime"` // (Optional) Max connection TTL configuration for underlying connection pool.
QueryTimeout time.Duration `json:"queryTimeout"` // (Optional) Max query time for per dql.
ExecTimeout time.Duration `json:"execTimeout"` // (Optional) Max exec time for dml.
TranTimeout time.Duration `json:"tranTimeout"` // (Optional) Max exec time time for a transaction.
PrepareTimeout time.Duration `json:"prepareTimeout"` // (Optional) Max exec time time for prepare operation.
CreatedAt string `json:"createdAt"` // (Optional) The filed name of table for automatic-filled created datetime.
UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime.
DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime.
TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature.
}
// configs is internal used configuration object.

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -48,9 +48,9 @@ type apiMapStrAny interface {
}
const (
ORM_TAG_FOR_STRUCT = "orm"
ORM_TAG_FOR_UNIQUE = "unique"
ORM_TAG_FOR_PRIMARY = "primary"
OrmTagForStruct = "orm"
OrmTagForUnique = "unique"
OrmTagForPrimary = "primary"
)
var (
@ -58,7 +58,7 @@ var (
quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
// Priority tags for struct converting for orm field mapping.
structTagPriority = append([]string{ORM_TAG_FOR_STRUCT}, gconv.StructTagPriority...)
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
)
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
@ -315,14 +315,14 @@ func doQuoteString(s, charLeft, charRight string) string {
// GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer.
// This function automatically retrieves primary or unique field and its attribute value as condition.
func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}, err error) {
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
if err != nil {
return "", nil, err
}
array := ([]string)(nil)
for _, field := range tagField {
array = strings.Split(field.TagValue, ",")
if len(array) > 1 && gstr.InArray([]string{ORM_TAG_FOR_UNIQUE, ORM_TAG_FOR_PRIMARY}, array[1]) {
if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) {
return array[0], []interface{}{field.Value()}, nil
}
if len(where) > 0 {
@ -336,14 +336,14 @@ func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interf
// GetPrimaryKey retrieves and returns primary key field name from given struct.
func GetPrimaryKey(pointer interface{}) (string, error) {
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
if err != nil {
return "", err
}
array := ([]string)(nil)
for _, field := range tagField {
array = strings.Split(field.TagValue, ",")
if len(array) > 1 && array[1] == ORM_TAG_FOR_PRIMARY {
if len(array) > 1 && array[1] == OrmTagForPrimary {
return array[0], nil
}
}
@ -741,7 +741,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
// convertMapToStruct maps the <data> to given struct.
// Note that the given parameter <pointer> should be a pointer to s struct.
func convertMapToStruct(data map[string]interface{}, pointer interface{}) error {
tagNameMap, err := structs.TagMapName(pointer, []string{ORM_TAG_FOR_STRUCT})
tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct})
if err != nil {
return err
}

View File

@ -11,6 +11,7 @@ import (
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/encoding/gparser"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/util/gconv"
"reflect"
)
@ -48,7 +49,10 @@ func (r Record) GMap() *gmap.StrAnyMap {
func (r Record) Struct(pointer interface{}) error {
// If the record is empty, it returns error.
if r.IsEmpty() {
return sql.ErrNoRows
if !empty.IsNil(pointer, true) {
return sql.ErrNoRows
}
return nil
}
// Special handling for parameter type: reflect.Value
if _, ok := pointer.(reflect.Value); ok {

View File

@ -981,6 +981,18 @@ func Test_Model_Struct(t *testing.T) {
err := db.Table(table).Where("id=-1").Struct(user)
t.Assert(err, sql.ErrNoRows)
})
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
var user *User
err := db.Table(table).Where("id=-1").Struct(&user)
t.Assert(err, nil)
})
}
func Test_Model_Struct_CustomType(t *testing.T) {
@ -2135,6 +2147,27 @@ func Test_Model_Option_List(t *testing.T) {
})
}
func Test_Model_OmitEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr())
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(45) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)
_, err := db.Table(table).OmitEmpty().Data(g.Map{
"id": 1,
"name": "",
}).Save()
t.AssertNE(err, nil)
})
}
func Test_Model_Option_Where(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table := createInitTable()

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -7,6 +7,7 @@
package gdb_test
import (
"database/sql"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/test/gtest"
@ -252,24 +253,25 @@ func Test_Struct_Empty(t *testing.T) {
}
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
user := new(User)
t.AssertNE(one.Struct(user), nil)
err := db.Table(table).Where("id=100").Struct(user)
t.Assert(err, sql.ErrNoRows)
t.AssertNE(user, nil)
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
var user *User
t.AssertNE(one.Struct(&user), nil)
t.Assert(one.Struct(&user), nil)
t.Assert(user, nil)
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
var user *User
t.AssertNE(one.Struct(user), nil)
err := db.Table(table).Where("id=100").Struct(&user)
t.Assert(err, nil)
t.Assert(user, nil)
})
}

View File

@ -1,4 +1,4 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -36,17 +36,17 @@ type Conn struct {
// Redis configuration.
type Config struct {
Host string
Port int
Db int
Pass string // Password for AUTH.
MaxIdle int // Maximum number of connections allowed to be idle (default is 10)
MaxActive int // Maximum number of connections limit (default is 0 means no limit).
IdleTimeout time.Duration // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0)
MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0)
ConnectTimeout time.Duration // Dial connection timeout.
TLS bool // Specifies the config to use when a TLS connection is dialed.
TLSSkipVerify bool // Disables server name verification when connecting over TLS
Host string `json:"host"`
Port int `json:"port"`
Db int `json:"db"`
Pass string `json:"pass"` // Password for AUTH.
MaxIdle int `json:"maxIdle"` // Maximum number of connections allowed to be idle (default is 10)
MaxActive int `json:"maxActive"` // Maximum number of connections limit (default is 0 means no limit).
IdleTimeout time.Duration `json:"idleTimeout"` // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0)
MaxConnLifetime time.Duration `json:"maxConnLifetime"` // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0)
ConnectTimeout time.Duration `json:"connectTimeout"` // Dial connection timeout.
TLS bool `json:"tls"` // Specifies the config to use when a TLS connection is dialed.
TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS
}
// Pool statistics.

View File

@ -10,6 +10,7 @@ import (
"fmt"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gutil"
)
func Example_autoMarshalUnmarshalMap() {
@ -101,7 +102,7 @@ func Example_autoMarshalUnmarshalStructSlice() {
fmt.Println(users2)
}
func Example_hashSet() {
func Example_hSet() {
var (
err error
result *gvar.Var
@ -124,3 +125,54 @@ func Example_hashSet() {
// May Output:
// map[id:10000 name:john]
}
func Example_hMSet_Map() {
var (
key = "user_100"
data = g.Map{
"name": "gf",
"sex": 0,
"score": 100,
}
)
_, err := g.Redis().Do("HMSET", append(g.Slice{key}, gutil.MapToSlice(data)...)...)
if err != nil {
g.Log().Fatal(err)
}
v, err := g.Redis().DoVar("HMGET", key, "name")
if err != nil {
g.Log().Fatal(err)
}
fmt.Println(v.Slice())
// May Output:
// [gf]
}
func Example_hMSet_Struct() {
type User struct {
Name string `json:"name"`
Sex int `json:"sex"`
Score int `json:"score"`
}
var (
key = "user_100"
data = &User{
Name: "gf",
Sex: 0,
Score: 100,
}
)
_, err := g.Redis().Do("HMSET", append(g.Slice{key}, gutil.StructToSlice(data)...)...)
if err != nil {
g.Log().Fatal(err)
}
v, err := g.Redis().DoVar("HMGET", key, "name")
if err != nil {
g.Log().Fatal(err)
}
fmt.Println(v.Slice())
// May Output:
// ["gf"]
}

View File

@ -10,6 +10,7 @@ import (
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/guid"
"github.com/gogf/gf/util/gutil"
"testing"
"time"
@ -255,9 +256,11 @@ func Test_HSet(t *testing.T) {
func Test_HGetAll1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var err error
redis := gredis.New(config)
key := guid.S()
var (
err error
key = guid.S()
redis = gredis.New(config)
)
defer redis.Do("DEL", key)
_, err = redis.Do("HSET", key, "id", 100)
@ -296,6 +299,54 @@ func Test_HGetAll2(t *testing.T) {
})
}
func Test_HMSet(t *testing.T) {
// map
gtest.C(t, func(t *gtest.T) {
var (
err error
key = guid.S()
redis = gredis.New(config)
data = g.Map{
"name": "gf",
"sex": 0,
"score": 100,
}
)
defer redis.Do("DEL", key)
_, err = redis.Do("HMSET", append(g.Slice{key}, gutil.MapToSlice(data)...)...)
t.Assert(err, nil)
v, err := redis.DoVar("HMGET", key, "name")
t.Assert(err, nil)
t.Assert(v.Slice(), g.Slice{data["name"]})
})
// struct
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string `json:"name"`
Sex int `json:"sex"`
Score int `json:"score"`
}
var (
err error
key = guid.S()
redis = gredis.New(config)
data = &User{
Name: "gf",
Sex: 0,
Score: 100,
}
)
defer redis.Do("DEL", key)
_, err = redis.Do("HMSET", append(g.Slice{key}, gutil.StructToSlice(data)...)...)
t.Assert(err, nil)
v, err := redis.DoVar("HMGET", key, "name")
t.Assert(err, nil)
t.Assert(v.Slice(), g.Slice{data.Name})
})
}
func Test_Auto_Marshal(t *testing.T) {
var (
err error

View File

@ -169,6 +169,12 @@ func (err *Error) Next() error {
return err.error
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
// Note that do not use pointer as its receiver here.
func (err *Error) MarshalJSON() ([]byte, error) {
return []byte(`"` + err.Error() + `"`), nil
}
// formatSubStack formats the stack for error.
func formatSubStack(st stack, buffer *bytes.Buffer) {
index := 1

View File

@ -9,6 +9,7 @@ package gerror_test
import (
"errors"
"fmt"
"github.com/gogf/gf/internal/json"
"testing"
"github.com/gogf/gf/errors/gerror"
@ -308,3 +309,12 @@ func Test_Code(t *testing.T) {
t.Assert(err.Error(), "3: 2: 1")
})
}
func Test_Json(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
err := gerror.Wrap(gerror.New("1"), "2")
b, e := json.Marshal(err)
t.Assert(e, nil)
t.Assert(string(b), `"2: 1"`)
})
}

View File

@ -52,9 +52,12 @@ func TryCatch(try func(), catch ...func(exception error)) {
}
// IsNil checks whether given <value> is nil.
// Parameter <traceSource> is used for tracing to the source variable if given <value> is type
// of a pinter that also points to a pointer. It returns nil if the source is nil when <traceSource>
// is true.
// Note that it might use reflect feature which affects performance a little bit.
func IsNil(value interface{}) bool {
return empty.IsNil(value)
func IsNil(value interface{}, traceSource ...bool) bool {
return empty.IsNil(value, traceSource...)
}
// IsEmpty checks whether given <value> empty.

View File

@ -1,10 +1,10 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package empty provides functions for checking empty variables.
// Package empty provides functions for checking empty/nil variables.
package empty
import (
@ -152,8 +152,11 @@ func IsEmpty(value interface{}) bool {
}
// IsNil checks whether given <value> is nil.
// Parameter <traceSource> is used for tracing to the source variable if given <value> is type
// of a pinter that also points to a pointer. It returns nil if the source is nil when <traceSource>
// is true.
// Note that it might use reflect feature which affects performance a little bit.
func IsNil(value interface{}) bool {
func IsNil(value interface{}, traceSource ...bool) bool {
if value == nil {
return true
}
@ -168,10 +171,24 @@ func IsNil(value interface{}) bool {
reflect.Map,
reflect.Slice,
reflect.Func,
reflect.Ptr,
reflect.Interface,
reflect.UnsafePointer:
return rv.IsNil()
return !rv.IsValid() || rv.IsNil()
case reflect.Ptr:
if len(traceSource) > 0 && traceSource[0] {
for rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if !rv.IsValid() {
return true
}
if rv.Kind() == reflect.Ptr {
return rv.IsNil()
}
} else {
return !rv.IsValid() || rv.IsNil()
}
}
return false
}

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -108,3 +108,22 @@ func TestIsEmpty(t *testing.T) {
t.Assert(empty.IsEmpty(tmpF6), false)
})
}
func TestIsNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(empty.IsNil(nil), true)
})
gtest.C(t, func(t *gtest.T) {
var i int
t.Assert(empty.IsNil(i), false)
})
gtest.C(t, func(t *gtest.T) {
var i *int
t.Assert(empty.IsNil(i), true)
})
gtest.C(t, func(t *gtest.T) {
var i *int
t.Assert(empty.IsNil(&i), false)
t.Assert(empty.IsNil(&i, true), true)
})
}

View File

@ -350,7 +350,7 @@ func (s *Server) startServer(fdMap listenerFdMap) {
s.config.HTTPSAddr = s.config.Address
s.config.Address = ""
} else {
s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR
s.config.HTTPSAddr = defaultHttpsAddr
}
}
httpsEnabled = len(s.config.HTTPSAddr) > 0
@ -385,7 +385,7 @@ func (s *Server) startServer(fdMap listenerFdMap) {
}
// HTTP
if !httpsEnabled && len(s.config.Address) == 0 {
s.config.Address = gDEFAULT_HTTP_ADDR
s.config.Address = defaultHttpAddr
}
var array []string
if v, ok := fdMap["http"]; ok && len(v) > 0 {

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -233,8 +233,13 @@ func shutdownWebServers(signal ...string) {
}
}
// gracefulShutdownWebServers gracefully shuts down all servers.
func gracefulShutdownWebServers() {
// shutdownWebServersGracefully gracefully shuts down all servers.
func shutdownWebServersGracefully(signal ...string) {
if len(signal) > 0 {
glog.Printf("%d: server gracefully shutting down by signal: %s", gproc.Pid(), signal[0])
} else {
glog.Printf("%d: server gracefully shutting down by api", gproc.Pid())
}
serverMapping.RLockFunc(func(m map[string]interface{}) {
for _, v := range m {
for _, s := range v.(*Server).servers {
@ -262,7 +267,7 @@ func handleProcessMessage() {
if msg := gproc.Receive(adminGProcCommGroup); msg != nil {
if bytes.EqualFold(msg.Data, []byte("exit")) {
intlog.Printf("%d: process message: exit", gproc.Pid())
gracefulShutdownWebServers()
shutdownWebServersGracefully()
allDoneChan <- struct{}{}
intlog.Printf("%d: process message: exit done", gproc.Pid())
return

View File

@ -1,4 +1,4 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright 2017 gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -35,14 +35,22 @@ func handleProcessSignal() {
sig = <-procSignalChan
intlog.Printf(`signal received: %s`, sig.String())
switch sig {
// Stop the servers.
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGABRT:
// Shutdown the servers.
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT:
shutdownWebServers(sig.String())
return
// Shutdown the servers gracefully.
// Especially from K8S when running server in POD.
case syscall.SIGTERM:
shutdownWebServersGracefully(sig.String())
return
// Restart the servers.
case syscall.SIGUSR1:
restartWebServers(sig.String())
if err := restartWebServers(sig.String()); err != nil {
intlog.Error(err)
}
return
default:

View File

@ -27,12 +27,12 @@ import (
)
const (
gDEFAULT_HTTP_ADDR = ":80" // Default listening port for HTTP.
gDEFAULT_HTTPS_ADDR = ":443" // Default listening port for HTTPS.
URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'.
URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name.
URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case.
URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case.
defaultHttpAddr = ":80" // Default listening port for HTTP.
defaultHttpsAddr = ":443" // Default listening port for HTTPS.
URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'.
URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name.
URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case.
URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case.
)
// ServerConfig is the HTTP Server configuration manager.
@ -43,16 +43,16 @@ type ServerConfig struct {
// Address specifies the server listening address like "port" or ":port",
// multiple addresses joined using ','.
Address string
Address string `json:"address"`
// HTTPSAddr specifies the HTTPS addresses, multiple addresses joined using char ','.
HTTPSAddr string
HTTPSAddr string `json:"httpsAddr"`
// HTTPSCertPath specifies certification file path for HTTPS service.
HTTPSCertPath string
HTTPSCertPath string `json:"httpsCertPath"`
// HTTPSKeyPath specifies the key file path for HTTPS service.
HTTPSKeyPath string
HTTPSKeyPath string `json:"httpsKeyPath"`
// TLSConfig optionally provides a TLS configuration for use
// by ServeTLS and ListenAndServeTLS. Note that this value is
@ -61,10 +61,10 @@ type ServerConfig struct {
// tls.Config.SetSessionTicketKeys. To use
// SetSessionTicketKeys, use Server.Serve with a TLS Listener
// instead.
TLSConfig *tls.Config
TLSConfig *tls.Config `json:"tlsConfig"`
// Handler the handler for HTTP request.
Handler http.Handler
Handler http.Handler `json:"-"`
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.
@ -73,19 +73,19 @@ type ServerConfig struct {
// decisions on each request body's acceptable deadline or
// upload rate, most users will prefer to use
// ReadHeaderTimeout. It is valid to use them both.
ReadTimeout time.Duration
ReadTimeout time.Duration `json:"readTimeout"`
// WriteTimeout is the maximum duration before timing out
// writes of the response. It is reset whenever a new
// request's header is read. Like ReadTimeout, it does not
// let Handlers make decisions on a per-request basis.
WriteTimeout time.Duration
WriteTimeout time.Duration `json:"writeTimeout"`
// IdleTimeout is the maximum amount of time to wait for the
// next request when keep-alives are enabled. If IdleTimeout
// is zero, the value of ReadTimeout is used. If both are
// zero, there is no timeout.
IdleTimeout time.Duration
IdleTimeout time.Duration `json:"idleTimeout"`
// MaxHeaderBytes controls the maximum number of bytes the
// server will read parsing the request header's keys and
@ -94,98 +94,102 @@ type ServerConfig struct {
//
// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
// It's 10240 bytes in default.
MaxHeaderBytes int
MaxHeaderBytes int `json:"maxHeaderBytes"`
// KeepAlive enables HTTP keep-alive.
KeepAlive bool
KeepAlive bool `json:"keepAlive"`
// ServerAgent specifies the server agent information, which is wrote to
// HTTP response header as "Server".
ServerAgent string
ServerAgent string `json:"serverAgent"`
// View specifies the default template view object for the server.
View *gview.View
View *gview.View `json:"view"`
// ==================================
// Static.
// ==================================
// Rewrites specifies the URI rewrite rules map.
Rewrites map[string]string
Rewrites map[string]string `json:"rewrites"`
// IndexFiles specifies the index files for static folder.
IndexFiles []string
IndexFiles []string `json:"indexFiles"`
// IndexFolder specifies if listing sub-files when requesting folder.
// The server responses HTTP status code 403 if it is false.
IndexFolder bool
IndexFolder bool `json:"indexFolder"`
// ServerRoot specifies the root directory for static service.
ServerRoot string
ServerRoot string `json:"serverRoot"`
// SearchPaths specifies additional searching directories for static service.
SearchPaths []string
SearchPaths []string `json:"searchPaths"`
// StaticPaths specifies URI to directory mapping array.
StaticPaths []staticPathItem
StaticPaths []staticPathItem `json:"staticPaths"`
// FileServerEnabled is the global switch for static service.
// It is automatically set enabled if any static path is set.
FileServerEnabled bool
FileServerEnabled bool `json:"fileServerEnabled"`
// ==================================
// Cookie.
// ==================================
// CookieMaxAge specifies the max TTL for cookie items.
CookieMaxAge time.Duration
CookieMaxAge time.Duration `json:"cookieMaxAge"`
// CookiePath specifies cookie path.
// It also affects the default storage for session id.
CookiePath string
CookiePath string `json:"cookiePath"`
// CookieDomain specifies cookie domain.
// It also affects the default storage for session id.
CookieDomain string
CookieDomain string `json:"cookieDomain"`
// ==================================
// Session.
// ==================================
// SessionMaxAge specifies max TTL for session items.
SessionMaxAge time.Duration
// SessionIdName specifies the session id name.
SessionIdName string
SessionIdName string `json:"sessionIdName"`
// SessionCookieOutput specifies whether automatic outputting session id to cookie.
SessionCookieOutput bool
// SessionMaxAge specifies max TTL for session items.
SessionMaxAge time.Duration `json:"sessionMaxAge"`
// SessionPath specifies the session storage directory path for storing session files.
// It only makes sense if the session storage is type of file storage.
SessionPath string
SessionPath string `json:"sessionPath"`
// SessionStorage specifies the session storage.
SessionStorage gsession.Storage
SessionStorage gsession.Storage `json:"sessionStorage"`
// SessionCookieMaxAge specifies the cookie ttl for session id.
// It it is set 0, it means it expires along with browser session.
SessionCookieMaxAge time.Duration `json:"sessionCookieMaxAge"`
// SessionCookieOutput specifies whether automatic outputting session id to cookie.
SessionCookieOutput bool `json:"sessionCookieOutput"`
// ==================================
// Logging.
// ==================================
Logger *glog.Logger // Logger specifies the logger for server.
LogPath string // LogPath specifies the directory for storing logging files.
LogLevel string // LogLevel specifies the logging level for logger.
LogStdout bool // LogStdout specifies whether printing logging content to stdout.
ErrorStack bool // ErrorStack specifies whether logging stack information when error.
ErrorLogEnabled bool // ErrorLogEnabled enables error logging content to files.
ErrorLogPattern string // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log
AccessLogEnabled bool // AccessLogEnabled enables access logging content to files.
AccessLogPattern string // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log
Logger *glog.Logger `json:"logger"` // Logger specifies the logger for server.
LogPath string `json:"logPath"` // LogPath specifies the directory for storing logging files.
LogLevel string `json:"logLevel"` // LogLevel specifies the logging level for logger.
LogStdout bool `json:"logStdout"` // LogStdout specifies whether printing logging content to stdout.
ErrorStack bool `json:"errorStack"` // ErrorStack specifies whether logging stack information when error.
ErrorLogEnabled bool `json:"errorLogEnabled"` // ErrorLogEnabled enables error logging content to files.
ErrorLogPattern string `json:"errorLogPattern"` // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log
AccessLogEnabled bool `json:"accessLogEnabled"` // AccessLogEnabled enables access logging content to files.
AccessLogPattern string `json:"accessLogPattern"` // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log
// ==================================
// PProf.
// ==================================
PProfEnabled bool // PProfEnabled enables PProf feature.
PProfPattern string // PProfPattern specifies the PProf service pattern for router.
PProfEnabled bool `json:"pprofEnabled"` // PProfEnabled enables PProf feature.
PProfPattern string `json:"pprofPattern"` // PProfPattern specifies the PProf service pattern for router.
// ==================================
// Other.
@ -194,26 +198,26 @@ type ServerConfig struct {
// ClientMaxBodySize specifies the max body size limit in bytes for client request.
// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
// It's 8MB in default.
ClientMaxBodySize int64
ClientMaxBodySize int64 `json:"clientMaxBodySize"`
// FormParsingMemory specifies max memory buffer size in bytes which can be used for
// parsing multimedia form.
// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
// It's 1MB in default.
FormParsingMemory int64
FormParsingMemory int64 `json:"formParsingMemory"`
// NameToUriType specifies the type for converting struct method name to URI when
// registering routes.
NameToUriType int
NameToUriType int `json:"nameToUriType"`
// RouteOverWrite allows overwrite the route if duplicated.
RouteOverWrite bool
RouteOverWrite bool `json:"routeOverWrite"`
// DumpRouterMap specifies whether automatically dumps router map when server starts.
DumpRouterMap bool
DumpRouterMap bool `json:"dumpRouterMap"`
// Graceful enables graceful reload feature for all servers of the process.
Graceful bool
Graceful bool `json:"graceful"`
}
// Deprecated. Use NewConfig instead.
@ -243,10 +247,11 @@ func NewConfig() ServerConfig {
CookieMaxAge: time.Hour * 24 * 365,
CookiePath: "/",
CookieDomain: "",
SessionMaxAge: time.Hour * 24,
SessionIdName: "gfsessionid",
SessionPath: gsession.DefaultStorageFilePath,
SessionMaxAge: time.Hour * 24,
SessionCookieOutput: true,
SessionCookieMaxAge: time.Hour * 24,
Logger: glog.New(),
LogLevel: "all",
LogStdout: true,

View File

@ -1,4 +1,4 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -32,6 +32,11 @@ func (s *Server) SetSessionCookieOutput(enabled bool) {
s.config.SessionCookieOutput = enabled
}
// SetSessionCookieMaxAge sets the SessionCookieMaxAge for server.
func (s *Server) SetSessionCookieMaxAge(maxAge time.Duration) {
s.config.SessionCookieMaxAge = maxAge
}
// GetSessionMaxAge returns the SessionMaxAge of server.
func (s *Server) GetSessionMaxAge() time.Duration {
return s.config.SessionMaxAge
@ -41,3 +46,8 @@ func (s *Server) GetSessionMaxAge() time.Duration {
func (s *Server) GetSessionIdName() string {
return s.config.SessionIdName
}
// GetSessionCookieMaxAge returns the SessionCookieMaxAge of server.
func (s *Server) GetSessionCookieMaxAge() time.Duration {
return s.config.SessionCookieMaxAge
}

View File

@ -1,10 +1,10 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// 静态文件搜索优先级: Resource > ServerPaths > ServerRoot > SearchPath
// Static Searching Priority: Resource > ServerPaths > ServerRoot > SearchPath
package ghttp

View File

@ -1,4 +1,4 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -14,9 +14,6 @@ import (
// Cookie for HTTP COOKIE management.
type Cookie struct {
data map[string]*cookieItem // Underlying cookie items.
path string // The default cookie path.
domain string // The default cookie domain
maxAge time.Duration // The default cookie max age.
server *Server // Belonged HTTP server
request *Request // Belonged HTTP request.
response *Response // Belonged HTTP response.
@ -47,13 +44,10 @@ func (c *Cookie) init() {
return
}
c.data = make(map[string]*cookieItem)
c.path = c.request.Server.GetCookiePath()
c.domain = c.request.Server.GetCookieDomain()
c.maxAge = c.request.Server.GetCookieMaxAge()
c.response = c.request.Response
// DO NOT ADD ANY DEFAULT COOKIE DOMAIN!
//if c.domain == "" {
// c.domain = c.request.GetHost()
//if c.request.Server.GetCookieDomain() == "" {
// c.request.Server.GetCookieDomain() = c.request.GetHost()
//}
for _, v := range c.request.Cookies() {
c.data[v.Name] = &cookieItem{
@ -86,7 +80,13 @@ func (c *Cookie) Contains(key string) bool {
// Set sets cookie item with default domain, path and expiration age.
func (c *Cookie) Set(key, value string) {
c.SetCookie(key, value, c.domain, c.path, c.maxAge)
c.SetCookie(
key,
value,
c.request.Server.GetCookieDomain(),
c.request.Server.GetCookiePath(),
c.request.Server.GetCookieMaxAge(),
)
}
// SetCookie sets cookie item given given domain, path and expiration age.
@ -128,7 +128,13 @@ func (c *Cookie) GetSessionId() string {
// SetSessionId sets session id in the cookie.
func (c *Cookie) SetSessionId(id string) {
c.Set(c.server.GetSessionIdName(), id)
c.SetCookie(
c.server.GetSessionIdName(),
id,
c.request.Server.GetCookieDomain(),
c.request.Server.GetCookiePath(),
c.server.GetSessionCookieMaxAge(),
)
}
// Get retrieves and returns the value with specified key.
@ -149,7 +155,13 @@ func (c *Cookie) Get(key string, def ...string) string {
// Remove deletes specified key and its value from cookie using default domain and path.
// It actually tells the http client that the cookie is expired, do not send it to server next time.
func (c *Cookie) Remove(key string) {
c.SetCookie(key, "", c.domain, c.path, -86400)
c.SetCookie(
key,
"",
c.request.Server.GetCookieDomain(),
c.request.Server.GetCookiePath(),
-86400,
)
}
// RemoveCookie deletes specified key and its value from cookie using given domain and path.

View File

@ -18,7 +18,7 @@ import (
type utilPProf struct{}
const (
gDEFAULT_PPROF_PATTERN = "/debug/pprof"
defaultPProfPattern = "/debug/pprof"
)
// EnablePProf enables PProf feature for server.
@ -28,7 +28,7 @@ func (s *Server) EnablePProf(pattern ...string) {
// EnablePProf enables PProf feature for server of specified domain.
func (d *Domain) EnablePProf(pattern ...string) {
p := gDEFAULT_PPROF_PATTERN
p := defaultPProfPattern
if len(pattern) > 0 && pattern[0] != "" {
p = pattern[0]
}

View File

@ -283,6 +283,10 @@ func (c *Config) FilePath(file ...string) (path string) {
}
})
}
// Already found?
if path != "" {
return
}
// Searching the file system.
c.paths.RLockFunc(func(array []string) {
for _, prefix := range array {

View File

@ -1,4 +1,4 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -21,25 +21,25 @@ import (
// Config is the configuration object for logger.
type Config struct {
Writer io.Writer // Customized io.Writer.
Flags int // Extra flags for logging output features.
Path string // Logging directory path.
File string // Format for logging file.
Level int // Output level.
Prefix string // Prefix string for every logging content.
StSkip int // Skip count for stack.
StStatus int // Stack status(1: enabled - default; 0: disabled)
StFilter string // Stack string filter.
CtxKeys []interface{} // Context keys for logging, which is used for value retrieving from context.
HeaderPrint bool `c:"header"` // Print header or not(true in default).
StdoutPrint bool `c:"stdout"` // Output to stdout or not(true in default).
LevelPrefixes map[int]string // Logging level to its prefix string mapping.
RotateSize int64 // Rotate the logging file if its size > 0 in bytes.
RotateExpire time.Duration // Rotate the logging file if its mtime exceeds this duration.
RotateBackupLimit int // Max backup for rotated files, default is 0, means no backups.
RotateBackupExpire time.Duration // Max expire for rotated files, which is 0 in default, means no expiration.
RotateBackupCompress int // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression.
RotateCheckInterval time.Duration // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default.
Writer io.Writer `json:"-"` // Customized io.Writer.
Flags int `json:"flags"` // Extra flags for logging output features.
Path string `json:"path"` // Logging directory path.
File string `json:"file"` // Format for logging file.
Level int `json:"level"` // Output level.
Prefix string `json:"prefix"` // Prefix string for every logging content.
StSkip int `json:"stSkip"` // Skip count for stack.
StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled)
StFilter string `json:"stFilter"` // Stack string filter.
CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context.
HeaderPrint bool `json:"header"` // Print header or not(true in default).
StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default).
LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping.
RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes.
RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration.
RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups.
RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration.
RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression.
RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default.
}
// DefaultConfig returns the default configuration for logger.

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -20,7 +20,7 @@ import (
)
const (
gPACKAGE_TEMPLATE = `
packedGoSouceTemplate = `
package %s
import "github.com/gogf/gf/os/gres"
@ -39,8 +39,10 @@ func init() {
//
// Note that parameter <srcPaths> supports multiple paths join with ','.
func Pack(srcPaths string, keyPrefix ...string) ([]byte, error) {
buffer := bytes.NewBuffer(nil)
headerPrefix := ""
var (
buffer = bytes.NewBuffer(nil)
headerPrefix = ""
)
if len(keyPrefix) > 0 && keyPrefix[0] != "" {
headerPrefix = keyPrefix[0]
}
@ -79,7 +81,7 @@ func PackToGoFile(srcPath, goFilePath, pkgName string, keyPrefix ...string) erro
}
return gfile.PutContents(
goFilePath,
fmt.Sprintf(gstr.TrimLeft(gPACKAGE_TEMPLATE), pkgName, gbase64.EncodeToString(data)),
fmt.Sprintf(gstr.TrimLeft(packedGoSouceTemplate), pkgName, gbase64.EncodeToString(data)),
)
}

View File

@ -1,4 +1,4 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -11,6 +11,7 @@ import (
"github.com/gogf/gf/internal/fileinfo"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/text/gregex"
"io"
"os"
"strings"
@ -72,7 +73,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix
intlog.Printf(`exclude file path: %s`, file)
continue
}
err := zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter)
err = zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter)
if err != nil {
return err
}
@ -83,7 +84,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix
path = headerPrefix
for {
name = gfile.Basename(path)
err := zipFileVirtual(
err = zipFileVirtual(
fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), path, zipWriter,
)
if err != nil {
@ -136,7 +137,7 @@ func zipFileVirtual(info os.FileInfo, path string, zw *zip.Writer) error {
return err
}
header.Name = path
if _, err := zw.CreateHeader(header); err != nil {
if _, err = zw.CreateHeader(header); err != nil {
return err
}
return nil
@ -148,9 +149,9 @@ func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error)
return nil, err
}
if len(prefix) > 0 {
prefix = strings.Replace(prefix, `\`, `/`, -1)
prefix = strings.TrimRight(prefix, `/`)
header.Name = prefix + `/` + header.Name
header.Name = strings.Replace(header.Name, `\`, `/`, -1)
header.Name, _ = gregex.ReplaceString(`/{2,}`, `/`, header.Name)
}
return header, nil
}

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -74,6 +74,7 @@ func (r *Resource) Get(path string) *File {
return nil
}
path = strings.Replace(path, "\\", "/", -1)
path = strings.Replace(path, "//", "/", -1)
if path != "/" {
for path[len(path)-1] == '/' {
path = path[:len(path)-1]
@ -93,6 +94,7 @@ func (r *Resource) Get(path string) *File {
func (r *Resource) GetWithIndex(path string, indexFiles []string) *File {
// Necessary for double char '/' replacement in prefix.
path = strings.Replace(path, "\\", "/", -1)
path = strings.Replace(path, "//", "/", -1)
if path != "/" {
for path[len(path)-1] == '/' {
path = path[:len(path)-1]
@ -114,7 +116,6 @@ func (r *Resource) GetWithIndex(path string, indexFiles []string) *File {
// GetContent directly returns the content of <path>.
func (r *Resource) GetContent(path string) []byte {
path = strings.Replace(path, "\\", "/", -1)
file := r.Get(path)
if file != nil {
return file.Content()
@ -169,6 +170,7 @@ func (r *Resource) ScanDirFile(path string, pattern string, recursive ...bool) [
// It scans directory recursively if given parameter <recursive> is true.
func (r *Resource) doScanDir(path string, pattern string, recursive bool, onlyFile bool) []*File {
path = strings.Replace(path, "\\", "/", -1)
path = strings.Replace(path, "//", "/", -1)
if path != "/" {
for path[len(path)-1] == '/' {
path = path[:len(path)-1]

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -68,8 +68,10 @@ func NewStorageFile(path ...string) *StorageFile {
// Batch updates the TTL for session ids timely.
gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() {
//intlog.Print("StorageFile.timer start")
var id string
var err error
var (
id string
err error
)
for {
if id = s.updatingIdSet.Pop(); id == "" {
break
@ -221,7 +223,7 @@ func (s *StorageFile) SetSession(id string, data *gmap.StrAnyMap, ttl time.Durat
// It just adds the session id to the async handling queue.
func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
intlog.Printf("StorageFile.UpdateTTL: %s, %v", id, ttl)
if ttl >= DefaultStorageRedisLoopInterval {
if ttl >= DefaultStorageFileLoopInterval {
s.updatingIdSet.Add(id)
}
return nil

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -26,7 +26,7 @@ type StorageRedis struct {
var (
// DefaultStorageRedisLoopInterval is the interval updating TTL for session ids
// in last duration.
DefaultStorageRedisLoopInterval = time.Minute
DefaultStorageRedisLoopInterval = 10 * time.Second
)
// NewStorageRedis creates and returns a redis storage object for session.
@ -45,9 +45,11 @@ func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis {
// Batch updates the TTL for session ids timely.
gtimer.AddSingleton(DefaultStorageRedisLoopInterval, func() {
intlog.Print("StorageRedis.timer start")
var id string
var err error
var ttlSeconds int
var (
id string
err error
ttlSeconds int
)
for {
if id, ttlSeconds = s.updatingIdMap.Pop(); id == "" {
break

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

View File

@ -318,6 +318,113 @@ func (t *Time) Sub(u *Time) time.Duration {
return t.Time.Sub(u.Time)
}
// StartOfMinute clones and returns a new time of which the seconds is set to 0.
func (t *Time) StartOfMinute() *Time {
newTime := t.Clone()
newTime.Time = newTime.Time.Truncate(time.Minute)
return newTime
}
// StartOfHour clones and returns a new time of which the hour, minutes and seconds are set to 0.
func (t *Time) StartOfHour() *Time {
y, m, d := t.Date()
newTime := t.Clone()
newTime.Time = time.Date(y, m, d, newTime.Time.Hour(), 0, 0, 0, newTime.Time.Location())
return newTime
}
// StartOfDay clones and returns a new time which is the start of day, its time is set to 00:00:00.
func (t *Time) StartOfDay() *Time {
y, m, d := t.Date()
newTime := t.Clone()
newTime.Time = time.Date(y, m, d, 0, 0, 0, 0, newTime.Time.Location())
return newTime
}
// StartOfWeek clones and returns a new time which is the first day of week and its time is set to
// 00:00:00.
func (t *Time) StartOfWeek() *Time {
weekday := int(t.Weekday())
return t.StartOfDay().AddDate(0, 0, -weekday)
}
// StartOfMonth clones and returns a new time which is the first day of the month and its is set to
// 00:00:00
func (t *Time) StartOfMonth() *Time {
y, m, _ := t.Date()
newTime := t.Clone()
newTime.Time = time.Date(y, m, 1, 0, 0, 0, 0, newTime.Time.Location())
return newTime
}
// StartOfQuarter clones and returns a new time which is the first day of the quarter and its time is set
// to 00:00:00.
func (t *Time) StartOfQuarter() *Time {
month := t.StartOfMonth()
offset := (int(month.Month()) - 1) % 3
return month.AddDate(0, -offset, 0)
}
// StartOfHalf clones and returns a new time which is the first day of the half year and its time is set
// to 00:00:00.
func (t *Time) StartOfHalf() *Time {
month := t.StartOfMonth()
offset := (int(month.Month()) - 1) % 6
return month.AddDate(0, -offset, 0)
}
// StartOfYear clones and returns a new time which is the first day of the year and its time is set to
// 00:00:00.
func (t *Time) StartOfYear() *Time {
y, _, _ := t.Date()
newTime := t.Clone()
newTime.Time = time.Date(y, time.January, 1, 0, 0, 0, 0, newTime.Time.Location())
return newTime
}
// EndOfMinute clones and returns a new time of which the seconds is set to 59.
func (t *Time) EndOfMinute() *Time {
return t.StartOfMinute().Add(time.Minute - time.Nanosecond)
}
// EndOfHour clones and returns a new time of which the minutes and seconds are both set to 59.
func (t *Time) EndOfHour() *Time {
return t.StartOfHour().Add(time.Hour - time.Nanosecond)
}
// EndOfDay clones and returns a new time which is the end of day the and its time is set to 23:59:59.
func (t *Time) EndOfDay() *Time {
y, m, d := t.Date()
newTime := t.Clone()
newTime.Time = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), newTime.Time.Location())
return newTime
}
// EndOfWeek clones and returns a new time which is the end of week and its time is set to 23:59:59.
func (t *Time) EndOfWeek() *Time {
return t.StartOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond)
}
// EndOfMonth clones and returns a new time which is the end of the month and its time is set to 23:59:59.
func (t *Time) EndOfMonth() *Time {
return t.StartOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond)
}
// EndOfQuarter clones and returns a new time which is end of the quarter and its time is set to 23:59:59.
func (t *Time) EndOfQuarter() *Time {
return t.StartOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond)
}
// EndOfHalf clones and returns a new time which is the end of the half year and its time is set to 23:59:59.
func (t *Time) EndOfHalf() *Time {
return t.StartOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond)
}
// EndOfYear clones and returns a new time which is the end of the year and its time is set to 23:59:59.
func (t *Time) EndOfYear() *Time {
return t.StartOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (t *Time) MarshalJSON() ([]byte, error) {
return []byte(`"` + t.String() + `"`), nil

View File

@ -256,3 +256,131 @@ func Test_Truncate(t *testing.T) {
t.Assert(timeTemp.UnixNano(), timeTemp1.Truncate(time.Hour).UnixNano())
})
}
func Test_StartOfMinute(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.StartOfMinute()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:24:00")
})
}
func Test_EndOfMinute(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.EndOfMinute()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:24:59")
})
}
func Test_StartOfHour(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.StartOfHour()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:00:00")
})
}
func Test_EndOfHour(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.EndOfHour()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:59:59")
})
}
func Test_StartOfDay(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.StartOfDay()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 00:00:00")
})
}
func Test_EndOfDay(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.EndOfDay()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 23:59:59")
})
}
func Test_StartOfWeek(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.StartOfWeek()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-06 00:00:00")
})
}
func Test_EndOfWeek(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.EndOfWeek()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 23:59:59")
})
}
func Test_StartOfMonth(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.StartOfMonth()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-01 00:00:00")
})
}
func Test_EndOfMonth(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
timeTemp1 := timeTemp.EndOfMonth()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
})
}
func Test_StartOfQuarter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
timeTemp1 := timeTemp.StartOfQuarter()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-10-01 00:00:00")
})
}
func Test_EndOfQuarter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
timeTemp1 := timeTemp.EndOfQuarter()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
})
}
func Test_StartOfHalf(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
timeTemp1 := timeTemp.StartOfHalf()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-07-01 00:00:00")
})
}
func Test_EndOfHalf(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
timeTemp1 := timeTemp.EndOfHalf()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
})
}
func Test_StartOfYear(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
timeTemp1 := timeTemp.StartOfYear()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-01-01 00:00:00")
})
}
func Test_EndOfYear(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
timeTemp1 := timeTemp.EndOfYear()
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
})
}

View File

@ -1,4 +1,4 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -21,12 +21,12 @@ import (
// Config is the configuration object for template engine.
type Config struct {
Paths []string // Searching array for path, NOT concurrent-safe for performance purpose.
Data map[string]interface{} // Global template variables including configuration.
DefaultFile string // Default template file for parsing.
Delimiters []string // Custom template delimiters.
AutoEncode bool // Automatically encodes and provides safe html output, which is good for avoiding XSS.
I18nManager *gi18n.Manager // I18n manager for the view.
Paths []string `json:"paths"` // Searching array for path, NOT concurrent-safe for performance purpose.
Data map[string]interface{} `json:"data"` // Global template variables including configuration.
DefaultFile string `json:"defaultFile"` // Default template file for parsing.
Delimiters []string `json:"delimiters"` // Custom template delimiters.
AutoEncode bool `json:"autoEncode"` // Automatically encodes and provides safe html output, which is good for avoiding XSS.
I18nManager *gi18n.Manager `json:"-"` // I18n manager for the view.
}
const (

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -6,15 +6,15 @@
//
// | Function | Result |
// |-----------------------------------|--------------------|
// | SnakeCase(s) | any_kind_of_string |
// | SnakeScreamingCase(s) | ANY_KIND_OF_STRING |
// | KebabCase(s) | any-kind-of-string |
// | KebabScreamingCase(s) | ANY-KIND-OF-STRING |
// | DelimitedCase(s, '.') | any.kind.of.string |
// | DelimitedScreamingCase(s, '.') | ANY.KIND.OF.STRING |
// | CamelCase(s) | AnyKindOfString |
// | CamelLowerCase(s) | anyKindOfString |
// | SnakeFirstUpperCase(RGBCodeMd5) | rgb_code_md5 |
// | CaseSnake(s) | any_kind_of_string |
// | CaseSnakeScreaming(s) | ANY_KIND_OF_STRING |
// | CaseSnakeFirstUpper("RGBCodeMd5") | rgb_code_md5 |
// | CaseKebab(s) | any-kind-of-string |
// | CaseKebabScreaming(s) | ANY-KIND-OF-STRING |
// | CaseDelimited(s, '.') | any.kind.of.string |
// | CaseDelimitedScreaming(s, '.') | ANY.KIND.OF.STRING |
// | CaseCamel(s) | AnyKindOfString |
// | CaseCamelLower(s) | anyKindOfString |
package gstr
@ -30,12 +30,24 @@ var (
)
// CamelCase converts a string to CamelCase.
// Deprecated, use CaseCamel instead.
func CamelCase(s string) string {
return CaseCamel(s)
}
// CaseCamel converts a string to CamelCase.
func CaseCamel(s string) string {
return toCamelInitCase(s, true)
}
// CamelLowerCase converts a string to lowerCamelCase.
// Deprecated, use CaseCamelLower instead.
func CamelLowerCase(s string) string {
return CaseCamelLower(s)
}
// CaseCamelLower converts a string to lowerCamelCase.
func CaseCamelLower(s string) string {
if s == "" {
return s
}
@ -46,19 +58,37 @@ func CamelLowerCase(s string) string {
}
// SnakeCase converts a string to snake_case.
// Deprecated, use CaseSnake instead.
func SnakeCase(s string) string {
return CaseSnake(s)
}
// CaseSnake converts a string to snake_case.
func CaseSnake(s string) string {
return DelimitedCase(s, '_')
}
// SnakeScreamingCase converts a string to SNAKE_CASE_SCREAMING.
// Deprecated, use CaseSnakeScreaming instead.
func SnakeScreamingCase(s string) string {
return CaseSnakeScreaming(s)
}
// CaseSnakeScreaming converts a string to SNAKE_CASE_SCREAMING.
func CaseSnakeScreaming(s string) string {
return DelimitedScreamingCase(s, '_', true)
}
// SnakeFirstUpperCase converts a string from RGBCodeMd5 to rgb_code_md5.
// The length of word should not be too long
// TODO for efficiency should change regexp to traversing string in future
// Deprecated, use CaseSnakeFirstUpper instead.
func SnakeFirstUpperCase(word string, underscore ...string) string {
return CaseSnakeFirstUpper(word, underscore...)
}
// CaseSnakeFirstUpper converts a string like "RGBCodeMd5" to "rgb_code_md5".
// TODO for efficiency should change regexp to traversing string in future.
func CaseSnakeFirstUpper(word string, underscore ...string) string {
replace := "_"
if len(underscore) > 0 {
replace = underscore[0]
@ -73,7 +103,7 @@ func SnakeFirstUpperCase(word string, underscore ...string) string {
m := firstCamelCaseStart.FindAllStringSubmatch(word, 1)
if len(m) > 0 && m[0][1] != "" {
w := strings.ToLower(m[0][1])
w = string(w[:len(w)-1]) + replace + string(w[len(w)-1])
w = w[:len(w)-1] + replace + string(w[len(w)-1])
word = strings.Replace(word, m[0][1], w, 1)
} else {
@ -84,23 +114,47 @@ func SnakeFirstUpperCase(word string, underscore ...string) string {
return TrimLeft(word, replace)
}
// KebabCase converts a string to kebab-case
// KebabCase converts a string to kebab-case.
// Deprecated, use CaseKebab instead.
func KebabCase(s string) string {
return DelimitedCase(s, '-')
return CaseKebab(s)
}
// CaseKebab converts a string to kebab-case
func CaseKebab(s string) string {
return CaseDelimited(s, '-')
}
// KebabScreamingCase converts a string to KEBAB-CASE-SCREAMING.
// Deprecated, use CaseKebabScreaming instead.
func KebabScreamingCase(s string) string {
return DelimitedScreamingCase(s, '-', true)
return CaseKebabScreaming(s)
}
// CaseKebabScreaming converts a string to KEBAB-CASE-SCREAMING.
func CaseKebabScreaming(s string) string {
return CaseDelimitedScreaming(s, '-', true)
}
// DelimitedCase converts a string to snake.case.delimited.
// Deprecated, use CaseDelimited instead.
func DelimitedCase(s string, del uint8) string {
return DelimitedScreamingCase(s, del, false)
return CaseDelimited(s, del)
}
// CaseDelimited converts a string to snake.case.delimited.
func CaseDelimited(s string, del uint8) string {
return CaseDelimitedScreaming(s, del, false)
}
// DelimitedScreamingCase converts a string to DELIMITED.SCREAMING.CASE or delimited.screaming.case.
// Deprecated, use CaseDelimitedScreaming instead.
func DelimitedScreamingCase(s string, del uint8, screaming bool) string {
return CaseDelimitedScreaming(s, del, screaming)
}
// CaseDelimitedScreaming converts a string to DELIMITED.SCREAMING.CASE or delimited.screaming.case.
func CaseDelimitedScreaming(s string, del uint8, screaming bool) string {
s = addWordBoundariesToNumbers(s)
s = strings.Trim(s, " ")
n := ""

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -7,12 +7,13 @@
package gstr_test
import (
"github.com/gogf/gf/test/gtest"
"testing"
"github.com/gogf/gf/text/gstr"
)
func Test_CamelCase(t *testing.T) {
func Test_CaseCamel(t *testing.T) {
cases := [][]string{
{"test_case", "TestCase"},
{"test", "Test"},
@ -28,14 +29,14 @@ func Test_CamelCase(t *testing.T) {
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.CamelCase(in)
result := gstr.CaseCamel(in)
if result != out {
t.Error("'" + result + "' != '" + out + "'")
}
}
}
func Test_CamelLowerCase(t *testing.T) {
func Test_CaseCamelLower(t *testing.T) {
cases := [][]string{
{"foo-bar", "fooBar"},
{"TestCase", "testCase"},
@ -45,14 +46,14 @@ func Test_CamelLowerCase(t *testing.T) {
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.CamelLowerCase(in)
result := gstr.CaseCamelLower(in)
if result != out {
t.Error("'" + result + "' != '" + out + "'")
}
}
}
func Test_SnakeCase(t *testing.T) {
func Test_CaseSnake(t *testing.T) {
cases := [][]string{
{"testCase", "test_case"},
{"TestCase", "test_case"},
@ -75,14 +76,14 @@ func Test_SnakeCase(t *testing.T) {
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.SnakeCase(in)
result := gstr.CaseSnake(in)
if result != out {
t.Error("'" + in + "'('" + result + "' != '" + out + "')")
}
}
}
func Test_DelimitedCase(t *testing.T) {
func Test_CaseDelimited(t *testing.T) {
cases := [][]string{
{"testCase", "test@case"},
{"TestCase", "test@case"},
@ -106,28 +107,28 @@ func Test_DelimitedCase(t *testing.T) {
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.DelimitedCase(in, '@')
result := gstr.CaseDelimited(in, '@')
if result != out {
t.Error("'" + in + "' ('" + result + "' != '" + out + "')")
}
}
}
func Test_SnakeScreamingCase(t *testing.T) {
func Test_CaseSnakeScreaming(t *testing.T) {
cases := [][]string{
{"testCase", "TEST_CASE"},
}
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.SnakeScreamingCase(in)
result := gstr.CaseSnakeScreaming(in)
if result != out {
t.Error("'" + result + "' != '" + out + "'")
}
}
}
func Test_KebabCase(t *testing.T) {
func Test_CaseKebab(t *testing.T) {
cases := [][]string{
{"testCase", "test-case"},
{"optimization1.0.0", "optimization-1-0-0"},
@ -135,42 +136,42 @@ func Test_KebabCase(t *testing.T) {
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.KebabCase(in)
result := gstr.CaseKebab(in)
if result != out {
t.Error("'" + result + "' != '" + out + "'")
}
}
}
func Test_KebabScreamingCase(t *testing.T) {
func Test_CaseKebabScreaming(t *testing.T) {
cases := [][]string{
{"testCase", "TEST-CASE"},
}
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.KebabScreamingCase(in)
result := gstr.CaseKebabScreaming(in)
if result != out {
t.Error("'" + result + "' != '" + out + "'")
}
}
}
func Test_DelimitedScreamingCase(t *testing.T) {
func Test_CaseDelimitedScreaming(t *testing.T) {
cases := [][]string{
{"testCase", "TEST.CASE"},
}
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.DelimitedScreamingCase(in, '.', true)
result := gstr.CaseDelimitedScreaming(in, '.', true)
if result != out {
t.Error("'" + result + "' != '" + out + "'")
}
}
}
func TestSnakeFirstUpperCase(t *testing.T) {
func Test_CaseSnakeFirstUpper(t *testing.T) {
cases := [][]string{
{"RGBCodeMd5", "rgb_code_md5"},
{"testCase", "test_case"},
@ -182,13 +183,12 @@ func TestSnakeFirstUpperCase(t *testing.T) {
{"User_ID", "user_id"},
{"user_id", "user_id"},
{"md5", "md5"},
{"Numbers2And55With000", "numbers2_and55_with000"},
}
for _, i := range cases {
in := i[0]
out := i[1]
result := gstr.SnakeFirstUpperCase(in)
if result != out {
t.Error("'" + result + "' != '" + out + "'")
gtest.C(t, func(t *gtest.T) {
for _, item := range cases {
t.Assert(gstr.CaseSnakeFirstUpper(item[0]), item[1])
}
}
})
}

View File

@ -9,11 +9,12 @@
package grand_test
import (
"crypto/rand"
"encoding/binary"
"testing"
cryptoRand "crypto/rand"
mathRand "math/rand"
"encoding/binary"
"github.com/gogf/gf/util/grand"
"testing"
)
var (
@ -23,15 +24,21 @@ var (
strForStr = "我爱GoFrame"
)
func Benchmark_Rand_Buffer4(b *testing.B) {
func Benchmark_Math_Rand_Int(b *testing.B) {
for i := 0; i < b.N; i++ {
rand.Read(randBuffer4)
mathRand.Int()
}
}
func Benchmark_Rand_Buffer1024(b *testing.B) {
func Benchmark_CryptoRand_Buffer4(b *testing.B) {
for i := 0; i < b.N; i++ {
rand.Read(randBuffer1024)
cryptoRand.Read(randBuffer4)
}
}
func Benchmark_CryptoRand_Buffer1024(b *testing.B) {
for i := 0; i < b.N; i++ {
cryptoRand.Read(randBuffer1024)
}
}
@ -101,9 +108,9 @@ func Benchmark_Uint32Converting(b *testing.B) {
}
}
func Benchmark_Buffer(b *testing.B) {
func Benchmark_CryptoRand_Buffer(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := rand.Read(buffer); err == nil {
if _, err := cryptoRand.Read(buffer); err == nil {
binary.LittleEndian.Uint64(buffer)
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -8,6 +8,7 @@
package gvalid
import (
"regexp"
"strings"
"github.com/gogf/gf/text/gregex"
@ -62,6 +63,136 @@ import (
// like: map[field] => string|map[rule]string
type CustomMsg = map[string]interface{}
const (
// regular expression pattern for single validation rule.
singleRulePattern = `^([\w-]+):{0,1}(.*)`
invalidRulesErrKey = "invalid_rules"
invalidParamsErrKey = "invalid_params"
invalidObjectErrKey = "invalid_object"
)
var (
// defaultValidator is the default validator for package functions.
defaultValidator = New()
// all internal error keys.
internalErrKeyMap = map[string]string{
invalidRulesErrKey: invalidRulesErrKey,
invalidParamsErrKey: invalidParamsErrKey,
invalidObjectErrKey: invalidObjectErrKey,
}
// regular expression object for single rule
// which is compiled just once and of repeatable usage.
ruleRegex, _ = regexp.Compile(singleRulePattern)
// mustCheckRulesEvenValueEmpty specifies some rules that must be validated
// even the value is empty (nil or empty).
mustCheckRulesEvenValueEmpty = map[string]struct{}{
"required": {},
"required-if": {},
"required-unless": {},
"required-with": {},
"required-with-all": {},
"required-without": {},
"required-without-all": {},
//"same": {},
//"different": {},
//"in": {},
//"not-in": {},
//"regex": {},
}
// allSupportedRules defines all supported rules that is used for quick checks.
allSupportedRules = map[string]struct{}{
"required": {},
"required-if": {},
"required-unless": {},
"required-with": {},
"required-with-all": {},
"required-without": {},
"required-without-all": {},
"date": {},
"date-format": {},
"email": {},
"phone": {},
"phone-loose": {},
"telephone": {},
"passport": {},
"password": {},
"password2": {},
"password3": {},
"postcode": {},
"resident-id": {},
"bank-card": {},
"qq": {},
"ip": {},
"ipv4": {},
"ipv6": {},
"mac": {},
"url": {},
"domain": {},
"length": {},
"min-length": {},
"max-length": {},
"between": {},
"min": {},
"max": {},
"json": {},
"integer": {},
"float": {},
"boolean": {},
"same": {},
"different": {},
"in": {},
"not-in": {},
"regex": {},
}
// boolMap defines the boolean values.
boolMap = map[string]struct{}{
"1": {},
"true": {},
"on": {},
"yes": {},
"": {},
"0": {},
"false": {},
"off": {},
"no": {},
}
)
// Check checks single value with specified rules.
// It returns nil if successful validation.
//
// The parameter <value> can be any type of variable, which will be converted to string
// for validation.
// The parameter <rules> can be one or more rules, multiple rules joined using char '|'.
// The parameter <messages> specifies the custom error messages, which can be type of:
// string/map/struct/*struct.
// The optional parameter <params> specifies the extra validation parameters for some rules
// like: required-*、same、different, etc.
func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
return defaultValidator.Check(value, rules, messages, params...)
}
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
//
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
// if <rules> is type of []string.
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
return defaultValidator.CheckMap(params, rules, messages...)
}
// CheckStruct validates strcut and returns the error result.
//
// The parameter <object> should be type of struct/*struct.
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
// if <rules> is type of []string.
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
return defaultValidator.CheckStruct(object, rules, messages...)
}
// parseSequenceTag parses one sequence tag to field, rule and error message.
// The sequence tag is like: [alias@]rule[...#msg...]
func parseSequenceTag(tag string) (field, rule, msg string) {

View File

@ -0,0 +1,31 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvalid
// Validator is the validation manager.
type Validator struct {
i18nLang string // I18n language.
}
// New creates and returns a new Validator.
func New() *Validator {
return &Validator{}
}
// Clone creates and returns a new Validator which is a shallow copy of current one.
func (v *Validator) Clone() *Validator {
newValidator := New()
*newValidator = *v
return newValidator
}
// I18n is a chaining operation function which sets the I18n language for next validation.
func (v *Validator) I18n(language string) *Validator {
newValidator := v.Clone()
newValidator.i18nLang = language
return newValidator
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -14,105 +14,10 @@ import (
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/util/gconv"
"regexp"
"strconv"
"strings"
)
const (
// regular expression pattern for single validation rule.
singleRulePattern = `^([\w-]+):{0,1}(.*)`
invalidRulesErrKey = "invalid_rules"
invalidParamsErrKey = "invalid_params"
invalidObjectErrKey = "invalid_object"
)
var (
// all internal error keys.
internalErrKeyMap = map[string]string{
invalidRulesErrKey: invalidRulesErrKey,
invalidParamsErrKey: invalidParamsErrKey,
invalidObjectErrKey: invalidObjectErrKey,
}
// regular expression object for single rule
// which is compiled just once and of repeatable usage.
ruleRegex, _ = regexp.Compile(singleRulePattern)
// mustCheckRulesEvenValueEmpty specifies some rules that must be validated
// even the value is empty (nil or empty).
mustCheckRulesEvenValueEmpty = map[string]struct{}{
"required": {},
"required-if": {},
"required-unless": {},
"required-with": {},
"required-with-all": {},
"required-without": {},
"required-without-all": {},
//"same": {},
//"different": {},
//"in": {},
//"not-in": {},
//"regex": {},
}
// allSupportedRules defines all supported rules that is used for quick checks.
allSupportedRules = map[string]struct{}{
"required": {},
"required-if": {},
"required-unless": {},
"required-with": {},
"required-with-all": {},
"required-without": {},
"required-without-all": {},
"date": {},
"date-format": {},
"email": {},
"phone": {},
"phone-loose": {},
"telephone": {},
"passport": {},
"password": {},
"password2": {},
"password3": {},
"postcode": {},
"resident-id": {},
"bank-card": {},
"qq": {},
"ip": {},
"ipv4": {},
"ipv6": {},
"mac": {},
"url": {},
"domain": {},
"length": {},
"min-length": {},
"max-length": {},
"between": {},
"min": {},
"max": {},
"json": {},
"integer": {},
"float": {},
"boolean": {},
"same": {},
"different": {},
"in": {},
"not-in": {},
"regex": {},
}
// boolMap defines the boolean values.
boolMap = map[string]struct{}{
"1": {},
"true": {},
"on": {},
"yes": {},
"": {},
"0": {},
"false": {},
"off": {},
"no": {},
}
)
// Check checks single value with specified rules.
// It returns nil if successful validation.
//
@ -123,12 +28,12 @@ var (
// string/map/struct/*struct.
// The optional parameter <params> specifies the extra validation parameters for some rules
// like: required-*、same、different, etc.
func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
return doCheck("", value, rules, messages, params...)
func (v *Validator) Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
return v.doCheck("", value, rules, messages, params...)
}
// doCheck does the really rules validation for single key-value.
func doCheck(key string, value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
// If there's no validation rules, it does nothing and returns quickly.
if rules == "" {
return nil
@ -196,7 +101,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
// It checks custom validation rules with most priority.
var (
dataMap map[string]interface{}
message = getErrorMessageByRule(ruleKey, customMsgMap)
message = v.getErrorMessageByRule(ruleKey, customMsgMap)
)
if len(params) > 0 {
dataMap = gconv.Map(params[0])
@ -209,7 +114,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
}
} else {
// It checks build-in validation rules if there's no custom rule.
match, err = doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap)
match, err = v.doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap)
if !match && err != nil {
errorMsgArray[ruleKey] = err.Error()
}
@ -220,7 +125,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
// It does nothing if the error message for this rule
// is already set in previous validation.
if _, ok := errorMsgArray[ruleKey]; !ok {
errorMsgArray[ruleKey] = getErrorMessageByRule(ruleKey, customMsgMap)
errorMsgArray[ruleKey] = v.getErrorMessageByRule(ruleKey, customMsgMap)
}
}
index++
@ -233,7 +138,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
return nil
}
func doCheckBuildInRules(
func (v *Validator) doCheckBuildInRules(
index int,
value interface{},
ruleKey string,
@ -253,7 +158,7 @@ func doCheckBuildInRules(
"required-with-all",
"required-without",
"required-without-all":
match = checkRequired(valueStr, ruleKey, rulePattern, dataMap)
match = v.checkRequired(valueStr, ruleKey, rulePattern, dataMap)
// Length rules.
// It also supports length of unicode string.
@ -261,7 +166,7 @@ func doCheckBuildInRules(
"length",
"min-length",
"max-length":
if msg := checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
return match, errors.New(msg)
} else {
match = true
@ -272,7 +177,7 @@ func doCheckBuildInRules(
"min",
"max",
"between":
if msg := checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
if msg := v.checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
return match, errors.New(msg)
} else {
match = true
@ -308,7 +213,7 @@ func doCheckBuildInRules(
match = true
} else {
var msg string
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":format", rulePattern, -1)
return match, errors.New(msg)
}
@ -322,7 +227,7 @@ func doCheckBuildInRules(
}
if !match {
var msg string
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":field", rulePattern, -1)
return match, errors.New(msg)
}
@ -337,7 +242,7 @@ func doCheckBuildInRules(
}
if !match {
var msg string
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":field", rulePattern, -1)
return match, errors.New(msg)
}
@ -430,11 +335,11 @@ func doCheckBuildInRules(
// 总:
// (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
case "resident-id":
match = checkResidentId(valueStr)
match = v.checkResidentId(valueStr)
// Bank card number using LUHN algorithm.
case "bank-card":
match = checkLuHn(valueStr)
match = v.checkLuHn(valueStr)
// Universal passport format rule:
// Starting with letter, containing only numbers or underscores, length between 6 and 18.

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -7,9 +7,8 @@
package gvalid
import (
"strings"
"github.com/gogf/gf/util/gconv"
"strings"
)
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
@ -17,7 +16,7 @@ import (
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
// if <rules> is type of []string.
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
// If there's no validation rules, it does nothing and returns quickly.
if params == nil || rules == nil {
return nil
@ -96,7 +95,7 @@ func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Err
value = v
}
// It checks each rule and its value in loop.
if e := doCheck(key, value, rule, customMsgs[key], data); e != nil {
if e := v.doCheck(key, value, rule, customMsgs[key], data); e != nil {
_, item := e.FirstItem()
// ===========================================================
// Only in map and struct validations, if value is nil or empty

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -7,10 +7,9 @@
package gvalid
import (
"strings"
"github.com/gogf/gf/internal/structs"
"github.com/gogf/gf/util/gconv"
"strings"
)
var (
@ -24,7 +23,7 @@ var (
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
// if <rules> is type of []string.
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
// It here must use structs.TagFields not structs.MapField to ensure error sequence.
tagField, err := structs.TagFields(object, structTagPriority)
if err != nil {
@ -166,7 +165,7 @@ func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *
value = v
}
// It checks each rule and its value in loop.
if e := doCheck(key, value, rule, customMessage[key], params); e != nil {
if e := v.doCheck(key, value, rule, customMessage[key], params); e != nil {
_, item := e.FirstItem()
// ===========================================================
// Only in map and struct validations, if value is nil or empty

View File

@ -1,4 +1,4 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -63,18 +63,18 @@ var defaultMessages = map[string]string{
// getErrorMessageByRule retrieves and returns the error message for specified rule.
// It firstly retrieves the message from custom message map, and then checks i18n manager,
// it returns the default error message if it's not found in custom message map or i18n manager.
func getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string {
func (v *Validator) getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string {
content := customMsgMap[ruleKey]
if content != "" {
return content
}
content = gi18n.GetContent(fmt.Sprintf(`gf.gvalid.rule.%s`, ruleKey))
content = gi18n.GetContent(fmt.Sprintf(`gf.gvalid.rule.%s`, ruleKey), v.i18nLang)
if content == "" {
content = defaultMessages[ruleKey]
}
// If there's no configured rule message, it uses default one.
if content == "" {
content = gi18n.GetContent(`gf.gvalid.rule.__default__`)
content = gi18n.GetContent(`gf.gvalid.rule.__default__`, v.i18nLang)
if content == "" {
content = defaultMessages["__default__"]
}

View File

@ -1,4 +1,4 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -15,7 +15,7 @@ import (
// checkLength checks <value> using length rules.
// The length is calculated using unicode string, which means one chinese character or letter
// both has the length of 1.
func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
var (
msg = ""
runeArray = gconv.Runes(value)
@ -39,7 +39,7 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string)
}
}
if valueLen < min || valueLen > max {
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
return msg
@ -48,14 +48,14 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string)
case "min-length":
min, err := strconv.Atoi(ruleVal)
if valueLen < min || err != nil {
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
}
case "max-length":
max, err := strconv.Atoi(ruleVal)
if valueLen > max || err != nil {
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -8,7 +8,7 @@ package gvalid
// checkLuHn checks <value> with LUHN algorithm.
// It's usually used for bank card number validation.
func checkLuHn(value string) bool {
func (v *Validator) checkLuHn(value string) bool {
var (
sum = 0
nDigits = len(value)

View File

@ -1,4 +1,4 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -12,7 +12,7 @@ import (
)
// checkRange checks <value> using range rules.
func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
msg := ""
switch ruleKey {
// Value range.
@ -30,9 +30,9 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string)
max = v
}
}
v, err := strconv.ParseFloat(value, 10)
if v < min || v > max || err != nil {
msg = getErrorMessageByRule(ruleKey, customMsgMap)
valueF, err := strconv.ParseFloat(value, 10)
if valueF < min || valueF > max || err != nil {
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
}
@ -44,7 +44,7 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string)
valueN, err2 = strconv.ParseFloat(value, 10)
)
if valueN < min || err1 != nil || err2 != nil {
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
}
@ -55,7 +55,7 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string)
valueN, err2 = strconv.ParseFloat(value, 10)
)
if valueN > max || err1 != nil || err2 != nil {
msg = getErrorMessageByRule(ruleKey, customMsgMap)
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
}

View File

@ -1,4 +1,4 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -11,7 +11,7 @@ import (
)
// checkRequired checks <value> using required rules.
func checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool {
func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool {
required := false
switch ruleKey {
// Required.

View File

@ -1,4 +1,4 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -32,7 +32,7 @@ import (
//
// 总:
// (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
func checkResidentId(id string) bool {
func (v *Validator) checkResidentId(id string) bool {
id = strings.ToUpper(strings.TrimSpace(id))
if len(id) != 18 {
return false

View File

@ -1,4 +1,4 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

View File

@ -1,4 +1,4 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.15.0"
const VERSION = "v1.15.1"
const AUTHORS = "john<john@goframe.org>"