mirror of
https://gitee.com/johng/gf
synced 2026-06-12 04:03:22 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bc8142974f | |||
| fadb7a8f8f | |||
| e1bfe90833 | |||
| 042dc0b33f | |||
| 8ca535dbf0 | |||
| e20183e7a1 | |||
| df86ffb61e | |||
| bfcf133c91 | |||
| 24a377d3a8 | |||
| baf51bc68f | |||
| 5f4b585164 | |||
| 17a11187b0 | |||
| 7caf7976cf | |||
| 1a31792c14 | |||
| d56eb49e41 | |||
| a1236b5e16 | |||
| 8f278be0dc |
@ -2,7 +2,7 @@
|
||||
# MySQL.
|
||||
[database]
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true"
|
||||
MaxOpen = 100
|
||||
|
||||
# Redis.
|
||||
|
||||
@ -2,11 +2,17 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
|
||||
db.Table("user").Data("num=num+1").Where("id", 8).Update()
|
||||
t1, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:32")
|
||||
t2, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:34")
|
||||
u, err := g.DB().Table("orders").Where("updated_at>? and updated_at<?", t1, t2).One()
|
||||
//u, err := g.DB().Table("orders").Where("updated_at>? and updated_at<?", gtime.New("2020-10-27 19:03:32"), gtime.New("2020-10-27 19:03:34")).One()
|
||||
//u, err := g.DB().Table("orders").Fields("id").Where("updated_at>'2020-10-27 19:03:32' and updated_at<'2020-10-27 19:03:34'").Value()
|
||||
g.Dump(u, err)
|
||||
}
|
||||
|
||||
@ -140,6 +140,7 @@ please note your github/gitee account in your payment bill. All the donations wi
|
||||
|RAGGA-TIME|alipay|¥50.00|
|
||||
|[ChArmy](https://gitee.com/charmy)|alipay|¥50.00|
|
||||
|[sanfenzui](https://gitee.com/sanfenzui)|alipay|¥88.00|
|
||||
|刘宇|wechat|¥30.00|请你喝咖啡
|
||||
|
||||
|
||||
|
||||
|
||||
@ -125,6 +125,7 @@ The concurrency starts from `100` to `10000`.
|
||||
- [LeYouJia](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [XiMaLaYa](https://www.ximalaya.com)
|
||||
- [ZYBang](https://www.zybang.com/)
|
||||
|
||||
> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://github.com/gogf/gf/issues/168).
|
||||
|
||||
|
||||
@ -145,6 +145,7 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
- [乐有家](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [喜马拉雅](https://www.ximalaya.com)
|
||||
- [作业帮](https://www.zybang.com/)
|
||||
|
||||
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://github.com/gogf/gf/issues/168) 留言。
|
||||
|
||||
|
||||
@ -199,7 +199,7 @@ func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[st
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`mssql_table_fields_%s_%s`, table, checkSchema),
|
||||
fmt.Sprintf(`mssql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -41,7 +41,7 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",
|
||||
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset,
|
||||
)
|
||||
}
|
||||
@ -103,7 +103,7 @@ func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[st
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`mysql_table_fields_%s_%s`, table, checkSchema),
|
||||
fmt.Sprintf(`mysql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -159,7 +159,7 @@ func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[s
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`oracle_table_fields_%s_%s`, table, checkSchema),
|
||||
fmt.Sprintf(`oracle_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
result := (Result)(nil)
|
||||
structureSql := fmt.Sprintf(`
|
||||
|
||||
@ -108,7 +108,7 @@ func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[st
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`pgsql_table_fields_%s_%s`, table, checkSchema),
|
||||
fmt.Sprintf(`pgsql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -36,15 +36,14 @@ func (d *DriverSqlite) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
// Open creates and returns a underlying sql.DB object for sqlite.
|
||||
func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
var err error
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = config.Name
|
||||
}
|
||||
source, err = gfile.Search(source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// It searches the source file to locate its absolute path..
|
||||
if absolutePath, _ := gfile.Search(source); absolutePath != "" {
|
||||
source = absolutePath
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
if db, err := sql.Open("sqlite3", source); err == nil {
|
||||
@ -99,7 +98,7 @@ func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[s
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`sqlite_table_fields_%s_%s`, table, checkSchema),
|
||||
fmt.Sprintf(`sqlite_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -141,6 +141,7 @@ func (m *Model) DB(db DB) *Model {
|
||||
// TX sets/changes the transaction for current operation.
|
||||
func (m *Model) TX(tx *TX) *Model {
|
||||
model := m.getModel()
|
||||
model.db = tx.db
|
||||
model.tx = tx
|
||||
return model
|
||||
}
|
||||
@ -199,3 +200,10 @@ func (m *Model) Safe(safe ...bool) *Model {
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Args sets custom arguments for model operation.
|
||||
func (m *Model) Args(args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
model.extraArgs = append(model.extraArgs, args)
|
||||
return model
|
||||
}
|
||||
|
||||
@ -42,6 +42,8 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
|
||||
switch r := fieldNamesOrMapStruct[0].(type) {
|
||||
case string:
|
||||
model.fields = gstr.Join(m.mappingToTableFields([]string{r}), ",")
|
||||
case []string:
|
||||
model.fields = gstr.Join(m.mappingToTableFields(r), ",")
|
||||
default:
|
||||
model.fields = gstr.Join(m.mappingToTableFields(gutil.Keys(r)), ",")
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ package gdb
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
@ -312,3 +313,26 @@ func Test_isSubQuery(t *testing.T) {
|
||||
t.Assert(isSubQuery("select 1"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestResult_Structs1(t *testing.T) {
|
||||
type A struct {
|
||||
Id int `orm:"id"`
|
||||
}
|
||||
type B struct {
|
||||
*A
|
||||
Name string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r := Result{
|
||||
Record{"id": gvar.New(nil), "name": gvar.New("john")},
|
||||
Record{"id": gvar.New(nil), "name": gvar.New("smith")},
|
||||
}
|
||||
array := make([]*B, 2)
|
||||
err := r.Structs(&array)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(array[0].Id, 0)
|
||||
t.Assert(array[1].Id, 0)
|
||||
t.Assert(array[0].Name, "john")
|
||||
t.Assert(array[1].Name, "smith")
|
||||
})
|
||||
}
|
||||
|
||||
@ -2717,6 +2717,49 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Fields_Struct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type A struct {
|
||||
Passport string
|
||||
Password string
|
||||
}
|
||||
type B struct {
|
||||
A
|
||||
NickName string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(A{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(&A{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(B{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 3)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(&B{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 3)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
}
|
||||
func Test_Model_NullField(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
@ -2793,3 +2836,87 @@ func Test_Model_HasField(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// Issue: https://github.com/gogf/gf/issues/1002
|
||||
func Test_Model_Issue1002(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
result, err := db.Table(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "port_1",
|
||||
"password": "pass_1",
|
||||
"nickname": "name_2",
|
||||
"create_time": "2020-10-27 19:03:33",
|
||||
}).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
// where + string.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
// where + string arguments.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", "2020-10-27 19:03:32", "2020-10-27 19:03:34").Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", "2020-10-27 19:03:32", "2020-10-27 19:03:34").FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>? and create_time<?", "2020-10-27 19:03:32", "2020-10-27 19:03:34").FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
// where + gtime.Time arguments.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", gtime.New("2020-10-27 19:03:32"), gtime.New("2020-10-27 19:03:34")).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", gtime.New("2020-10-27 19:03:32"), gtime.New("2020-10-27 19:03:34")).FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>? and create_time<?", gtime.New("2020-10-27 19:03:32"), gtime.New("2020-10-27 19:03:34")).FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
// where + time.Time arguments.
|
||||
t1, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:32")
|
||||
t2, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:34")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", t1, t2).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", t1, t2).FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>? and create_time<?", t1, t2).FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ package ghtml
|
||||
|
||||
import (
|
||||
"html"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
strip "github.com/grokify/html-strip-tags-go"
|
||||
@ -57,3 +58,46 @@ func SpecialCharsDecode(s string) string {
|
||||
"'", "'",
|
||||
).Replace(s)
|
||||
}
|
||||
|
||||
// SpecialCharsMapOrStruct automatically encodes string values/attributes for map/struct.
|
||||
func SpecialCharsMapOrStruct(mapOrStruct interface{}) error {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(mapOrStruct)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectValue.IsValid() && (reflectKind == reflect.Ptr || reflectKind == reflect.Interface) {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
var (
|
||||
mapKeys = reflectValue.MapKeys()
|
||||
mapValue reflect.Value
|
||||
)
|
||||
for _, key := range mapKeys {
|
||||
mapValue = reflectValue.MapIndex(key)
|
||||
switch mapValue.Kind() {
|
||||
case reflect.String:
|
||||
reflectValue.SetMapIndex(key, reflect.ValueOf(SpecialChars(mapValue.String())))
|
||||
case reflect.Interface:
|
||||
if mapValue.Elem().Kind() == reflect.String {
|
||||
reflectValue.SetMapIndex(key, reflect.ValueOf(SpecialChars(mapValue.Elem().String())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
var (
|
||||
fieldValue reflect.Value
|
||||
)
|
||||
for i := 0; i < reflectValue.NumField(); i++ {
|
||||
fieldValue = reflectValue.Field(i)
|
||||
switch fieldValue.Kind() {
|
||||
case reflect.String:
|
||||
fieldValue.Set(reflect.ValueOf(SpecialChars(fieldValue.String())))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -7,13 +7,14 @@
|
||||
package ghtml_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/encoding/ghtml"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func TestStripTags(t *testing.T) {
|
||||
func Test_StripTags(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
src := `<p>Test paragraph.</p><!-- Comment --> <a href="#fragment">Other text</a>`
|
||||
dst := `Test paragraph. Other text`
|
||||
@ -21,7 +22,7 @@ func TestStripTags(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEntities(t *testing.T) {
|
||||
func Test_Entities(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
@ -30,7 +31,7 @@ func TestEntities(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpecialChars(t *testing.T) {
|
||||
func Test_SpecialChars(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
@ -38,3 +39,43 @@ func TestSpecialChars(t *testing.T) {
|
||||
t.Assert(ghtml.SpecialCharsDecode(dst), src)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SpecialCharsMapOrStruct_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := g.Map{
|
||||
"Title": "<h1>T</h1>",
|
||||
"Content": "<div>C</div>",
|
||||
}
|
||||
err := ghtml.SpecialCharsMapOrStruct(a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a["Title"], `<h1>T</h1>`)
|
||||
t.Assert(a["Content"], `<div>C</div>`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := g.MapStrStr{
|
||||
"Title": "<h1>T</h1>",
|
||||
"Content": "<div>C</div>",
|
||||
}
|
||||
err := ghtml.SpecialCharsMapOrStruct(a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a["Title"], `<h1>T</h1>`)
|
||||
t.Assert(a["Content"], `<div>C</div>`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SpecialCharsMapOrStruct_Struct(t *testing.T) {
|
||||
type A struct {
|
||||
Title string
|
||||
Content string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := &A{
|
||||
Title: "<h1>T</h1>",
|
||||
Content: "<div>C</div>",
|
||||
}
|
||||
err := ghtml.SpecialCharsMapOrStruct(a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a.Title, `<h1>T</h1>`)
|
||||
t.Assert(a.Content, `<div>C</div>`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -261,7 +261,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
|
||||
if c.browserMode {
|
||||
now := time.Now()
|
||||
for _, v := range resp.Response.Cookies() {
|
||||
if v.Expires.UnixNano() < now.UnixNano() {
|
||||
if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() {
|
||||
delete(c.cookies, v.Name)
|
||||
} else {
|
||||
c.cookies[v.Name] = v.Value
|
||||
|
||||
@ -74,13 +74,16 @@ func (r *Response) ParseTplContent(content string, params ...gview.Params) (stri
|
||||
}
|
||||
|
||||
// buildInVars merges build-in variables into <params> and returns the new template variables.
|
||||
// TODO performance improving.
|
||||
func (r *Response) buildInVars(params ...map[string]interface{}) map[string]interface{} {
|
||||
m := gutil.MapMergeCopy(params...)
|
||||
m := gutil.MapMergeCopy(r.Request.viewParams)
|
||||
if len(params) > 0 {
|
||||
gutil.MapMerge(m, params[0])
|
||||
}
|
||||
// Retrieve custom template variables from request object.
|
||||
gutil.MapMerge(m, r.Request.viewParams, map[string]interface{}{
|
||||
gutil.MapMerge(m, map[string]interface{}{
|
||||
"Form": r.Request.GetFormMap(),
|
||||
"Query": r.Request.GetQueryMap(),
|
||||
"Request": r.Request.GetMap(),
|
||||
"Cookie": r.Request.Cookie.Map(),
|
||||
"Session": r.Request.Session.Map(),
|
||||
})
|
||||
|
||||
@ -13,13 +13,19 @@ import (
|
||||
|
||||
// Cookie for HTTP COOKIE management.
|
||||
type Cookie struct {
|
||||
data map[string]*http.Cookie // 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.
|
||||
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.
|
||||
}
|
||||
|
||||
// cookieItem is the item stored in Cookie.
|
||||
type cookieItem struct {
|
||||
*http.Cookie // Underlying cookie items.
|
||||
FromClient bool // Mark this cookie received from client.
|
||||
}
|
||||
|
||||
// GetCookie creates or retrieves a cookie object with given request.
|
||||
@ -40,7 +46,7 @@ func (c *Cookie) init() {
|
||||
if c.data != nil {
|
||||
return
|
||||
}
|
||||
c.data = make(map[string]*http.Cookie)
|
||||
c.data = make(map[string]*cookieItem)
|
||||
c.path = c.request.Server.GetCookiePath()
|
||||
c.domain = c.request.Server.GetCookieDomain()
|
||||
c.maxAge = c.request.Server.GetCookieMaxAge()
|
||||
@ -50,7 +56,10 @@ func (c *Cookie) init() {
|
||||
// c.domain = c.request.GetHost()
|
||||
//}
|
||||
for _, v := range c.request.Cookies() {
|
||||
c.data[v.Name] = v
|
||||
c.data[v.Name] = &cookieItem{
|
||||
Cookie: v,
|
||||
FromClient: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,23 +98,27 @@ func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration
|
||||
if len(httpOnly) > 0 {
|
||||
isHttpOnly = httpOnly[0]
|
||||
}
|
||||
c.data[key] = &http.Cookie{
|
||||
httpCookie := &http.Cookie{
|
||||
Name: key,
|
||||
Value: value,
|
||||
Path: path,
|
||||
Domain: domain,
|
||||
Expires: time.Now().Add(maxAge),
|
||||
HttpOnly: isHttpOnly,
|
||||
}
|
||||
if maxAge != 0 {
|
||||
httpCookie.Expires = time.Now().Add(maxAge)
|
||||
}
|
||||
c.data[key] = &cookieItem{
|
||||
Cookie: httpCookie,
|
||||
}
|
||||
}
|
||||
|
||||
// SetHttpCookie sets cookie with *http.Cookie.
|
||||
func (c *Cookie) SetHttpCookie(cookie *http.Cookie) {
|
||||
func (c *Cookie) SetHttpCookie(httpCookie *http.Cookie) {
|
||||
c.init()
|
||||
if cookie.Expires.IsZero() {
|
||||
cookie.Expires = time.Now().Add(c.maxAge)
|
||||
c.data[httpCookie.Name] = &cookieItem{
|
||||
Cookie: httpCookie,
|
||||
}
|
||||
c.data[cookie.Name] = cookie
|
||||
}
|
||||
|
||||
// GetSessionId retrieves and returns the session id from cookie.
|
||||
@ -151,11 +164,9 @@ func (c *Cookie) Flush() {
|
||||
return
|
||||
}
|
||||
for _, v := range c.data {
|
||||
// If cookie item is v.Expires.IsZero() means it is set in this request,
|
||||
// which should be outputted to client.
|
||||
if v.Expires.IsZero() {
|
||||
if v.FromClient {
|
||||
continue
|
||||
}
|
||||
http.SetCookie(c.response.Writer, v)
|
||||
http.SetCookie(c.response.Writer, v.Cookie)
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,8 +175,8 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han
|
||||
parsedItemList.PushBack(parsedItem)
|
||||
|
||||
// The middleware is inserted before the serving handler.
|
||||
// If there're multiple middlewares, they're inserted into the result list by their registering order.
|
||||
// The middlewares are also executed by their registering order.
|
||||
// If there're multiple middleware, they're inserted into the result list by their registering order.
|
||||
// The middleware are also executed by their registered order.
|
||||
case gHANDLER_TYPE_MIDDLEWARE:
|
||||
if lastMiddlewareElem == nil {
|
||||
lastMiddlewareElem = parsedItemList.PushFront(parsedItem)
|
||||
|
||||
@ -95,12 +95,12 @@ func Test_SetHttpCookie(t *testing.T) {
|
||||
t.Assert(client.GetContent("/set?k=key2&v=200"), "")
|
||||
|
||||
t.Assert(client.GetContent("/get?k=key1"), "100")
|
||||
t.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
t.Assert(client.GetContent("/get?k=key3"), "")
|
||||
t.Assert(client.GetContent("/remove?k=key1"), "")
|
||||
t.Assert(client.GetContent("/remove?k=key3"), "")
|
||||
t.Assert(client.GetContent("/remove?k=key4"), "")
|
||||
t.Assert(client.GetContent("/get?k=key1"), "")
|
||||
t.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
//t.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
//t.Assert(client.GetContent("/get?k=key3"), "")
|
||||
//t.Assert(client.GetContent("/remove?k=key1"), "")
|
||||
//t.Assert(client.GetContent("/remove?k=key3"), "")
|
||||
//t.Assert(client.GetContent("/remove?k=key4"), "")
|
||||
//t.Assert(client.GetContent("/get?k=key1"), "")
|
||||
//t.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
})
|
||||
}
|
||||
|
||||
@ -242,6 +242,9 @@ func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) {
|
||||
gmlock.Lock(memoryLockKey)
|
||||
defer gmlock.Unlock(memoryLockKey)
|
||||
file := l.getFilePointer(logFilePath)
|
||||
if file == nil {
|
||||
return
|
||||
}
|
||||
// Rotation file size checks.
|
||||
if l.config.RotateSize > 0 {
|
||||
stat, err := file.Stat()
|
||||
|
||||
@ -50,6 +50,11 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
if pointer == nil {
|
||||
return gerror.New("object pointer cannot be nil")
|
||||
}
|
||||
|
||||
if doStructByDirectReflectSet(params, pointer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflect operation panics.
|
||||
if e := recover(); e != nil {
|
||||
@ -143,18 +148,6 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
// doneMap is used to check repeated converting, its key is the real attribute name
|
||||
// of the struct.
|
||||
doneMap := make(map[string]struct{})
|
||||
// It first checks the passed mapping rules.
|
||||
if len(mapping) > 0 && len(mapping[0]) > 0 {
|
||||
for mapK, mapV := range mapping[0] {
|
||||
// mapV is the the attribute name of the struct.
|
||||
if paramV, ok := paramsMap[mapK]; ok {
|
||||
doneMap[mapV] = struct{}{}
|
||||
if err := bindVarToStructAttr(elem, mapV, paramV, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The key of the attrMap is the attribute name of the struct,
|
||||
// and the value is its replaced name for later comparison to improve performance.
|
||||
@ -171,7 +164,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
if !utils.IsLetterUpper(elemFieldType.Name[0]) {
|
||||
continue
|
||||
}
|
||||
// Maybe it's struct/*struct.
|
||||
// Maybe it's struct/*struct embedded.
|
||||
if elemFieldType.Anonymous {
|
||||
elemFieldValue = elem.Field(i)
|
||||
// Ignore the interface attribute if it's nil.
|
||||
@ -210,35 +203,43 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
)
|
||||
for mapK, mapV := range paramsMap {
|
||||
attrName = ""
|
||||
checkName = utils.RemoveSymbols(mapK)
|
||||
// Loop to find the matched attribute name with or without
|
||||
// string cases and chars like '-'/'_'/'.'/' '.
|
||||
|
||||
// Matching the parameters to struct tag names.
|
||||
// The <tagV> is the attribute name of the struct.
|
||||
for attrKey, cmpKey := range tagMap {
|
||||
if strings.EqualFold(checkName, cmpKey) {
|
||||
attrName = attrKey
|
||||
break
|
||||
// It firstly checks the passed mapping rules.
|
||||
if len(mapping) > 0 && len(mapping[0]) > 0 {
|
||||
if passedAttrKey, ok := mapping[0][mapK]; ok {
|
||||
attrName = passedAttrKey
|
||||
}
|
||||
}
|
||||
|
||||
// Matching the parameters to struct attributes.
|
||||
// It secondly checks the predefined tags and matching rules.
|
||||
if attrName == "" {
|
||||
for attrKey, cmpKey := range attrMap {
|
||||
// Eg:
|
||||
// UserName eq user_name
|
||||
// User-Name eq username
|
||||
// username eq userName
|
||||
// etc.
|
||||
checkName = utils.RemoveSymbols(mapK)
|
||||
// Loop to find the matched attribute name with or without
|
||||
// string cases and chars like '-'/'_'/'.'/' '.
|
||||
|
||||
// Matching the parameters to struct tag names.
|
||||
// The <tagV> is the attribute name of the struct.
|
||||
for attrKey, cmpKey := range tagMap {
|
||||
if strings.EqualFold(checkName, cmpKey) {
|
||||
attrName = attrKey
|
||||
break
|
||||
}
|
||||
}
|
||||
// Matching the parameters to struct attributes.
|
||||
if attrName == "" {
|
||||
for attrKey, cmpKey := range attrMap {
|
||||
// Eg:
|
||||
// UserName eq user_name
|
||||
// User-Name eq username
|
||||
// username eq userName
|
||||
// etc.
|
||||
if strings.EqualFold(checkName, cmpKey) {
|
||||
attrName = attrKey
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No matching, give up this attribute converting.
|
||||
// No matching, it gives up this attribute converting.
|
||||
if attrName == "" {
|
||||
continue
|
||||
}
|
||||
@ -255,6 +256,20 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
return nil
|
||||
}
|
||||
|
||||
// doStructByDirectReflectSet do the converting directly using reflect Set.
|
||||
// It returns true if success, or else false.
|
||||
func doStructByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) {
|
||||
v1 := reflect.ValueOf(pointer)
|
||||
v2 := reflect.ValueOf(params)
|
||||
if v1.Kind() == reflect.Ptr {
|
||||
if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() {
|
||||
elem.Set(v2)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// bindVarToStructAttr sets value to struct object attribute by name.
|
||||
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping ...map[string]string) (err error) {
|
||||
structFieldValue := elem.FieldByName(name)
|
||||
|
||||
@ -38,6 +38,11 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st
|
||||
if pointer == nil {
|
||||
return gerror.New("object pointer cannot be nil")
|
||||
}
|
||||
|
||||
if doStructsByDirectReflectSet(params, pointer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflect operation panics.
|
||||
if e := recover(); e != nil {
|
||||
@ -104,5 +109,18 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st
|
||||
}
|
||||
pointerRv.Elem().Set(array)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// doStructsByDirectReflectSet do the converting directly using reflect Set.
|
||||
// It returns true if success, or else false.
|
||||
func doStructsByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) {
|
||||
v1 := reflect.ValueOf(pointer)
|
||||
v2 := reflect.ValueOf(params)
|
||||
if v1.Kind() == reflect.Ptr {
|
||||
if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() {
|
||||
elem.Set(v2)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -22,7 +23,27 @@ var (
|
||||
"name": "gf",
|
||||
"score": 100,
|
||||
}
|
||||
structPointer = new(structType)
|
||||
structObj = structType{
|
||||
Name: "john",
|
||||
Score: 60,
|
||||
}
|
||||
structPointer = &structType{
|
||||
Name: "john",
|
||||
Score: 60,
|
||||
}
|
||||
structPointerNil *structType
|
||||
// struct slice
|
||||
structSliceNil []structType
|
||||
structSlice = []structType{
|
||||
{Name: "john", Score: 60},
|
||||
{Name: "smith", Score: 100},
|
||||
}
|
||||
// struct pointer slice
|
||||
structPointerSliceNil []*structType
|
||||
structPointerSlice = []*structType{
|
||||
{Name: "john", Score: 60},
|
||||
{Name: "smith", Score: 100},
|
||||
}
|
||||
)
|
||||
|
||||
func Benchmark_Struct_Basic(b *testing.B) {
|
||||
@ -30,3 +51,83 @@ func Benchmark_Struct_Basic(b *testing.B) {
|
||||
Struct(structMap, structPointer)
|
||||
}
|
||||
}
|
||||
|
||||
// *struct -> **struct
|
||||
func Benchmark_Reflect_PPStruct_PStruct(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v1 := reflect.ValueOf(&structPointerNil)
|
||||
v2 := reflect.ValueOf(structPointer)
|
||||
//if v1.Kind() == reflect.Ptr {
|
||||
// if elem := v1.Elem(); elem.Type() == v2.Type() {
|
||||
// elem.Set(v2)
|
||||
// }
|
||||
//}
|
||||
v1.Elem().Set(v2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Struct_PPStruct_PStruct(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Struct(structPointer, &structPointerNil)
|
||||
}
|
||||
}
|
||||
|
||||
// struct -> *struct
|
||||
func Benchmark_Reflect_PStruct_Struct(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v1 := reflect.ValueOf(structPointer)
|
||||
v2 := reflect.ValueOf(structObj)
|
||||
//if v1.Kind() == reflect.Ptr {
|
||||
// if elem := v1.Elem(); elem.Type() == v2.Type() {
|
||||
// elem.Set(v2)
|
||||
// }
|
||||
//}
|
||||
v1.Elem().Set(v2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Struct_PStruct_Struct(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Struct(structObj, structPointer)
|
||||
}
|
||||
}
|
||||
|
||||
// []struct -> *[]struct
|
||||
func Benchmark_Reflect_PStructs_Structs(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v1 := reflect.ValueOf(&structSliceNil)
|
||||
v2 := reflect.ValueOf(structSlice)
|
||||
//if v1.Kind() == reflect.Ptr {
|
||||
// if elem := v1.Elem(); elem.Type() == v2.Type() {
|
||||
// elem.Set(v2)
|
||||
// }
|
||||
//}
|
||||
v1.Elem().Set(v2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Structs_PStructs_Structs(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Structs(structSlice, &structSliceNil)
|
||||
}
|
||||
}
|
||||
|
||||
// []*struct -> *[]*struct
|
||||
func Benchmark_Reflect_PPStructs_PStructs(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v1 := reflect.ValueOf(&structPointerSliceNil)
|
||||
v2 := reflect.ValueOf(structPointerSlice)
|
||||
//if v1.Kind() == reflect.Ptr {
|
||||
// if elem := v1.Elem(); elem.Type() == v2.Type() {
|
||||
// elem.Set(v2)
|
||||
// }
|
||||
//}
|
||||
v1.Elem().Set(v2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Structs_PPStructs_PStructs(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Structs(structPointerSlice, &structPointerSliceNil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,3 +143,57 @@ func Test_Struct_SliceWithTag(t *testing.T) {
|
||||
t.Assert(users[1].NickName, "name2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_DirectReflectSet(t *testing.T) {
|
||||
type A struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
a = []*A{
|
||||
{Id: 1, Name: "john"},
|
||||
{Id: 2, Name: "smith"},
|
||||
}
|
||||
b []*A
|
||||
)
|
||||
err := gconv.Structs(a, &b)
|
||||
t.Assert(err, nil)
|
||||
t.AssertEQ(a, b)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
a = []A{
|
||||
{Id: 1, Name: "john"},
|
||||
{Id: 2, Name: "smith"},
|
||||
}
|
||||
b []A
|
||||
)
|
||||
err := gconv.Structs(a, &b)
|
||||
t.Assert(err, nil)
|
||||
t.AssertEQ(a, b)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_SliceIntAttribute(t *testing.T) {
|
||||
type A struct {
|
||||
Id []int
|
||||
}
|
||||
type B struct {
|
||||
*A
|
||||
Name string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
array []*B
|
||||
)
|
||||
err := gconv.Structs(g.Slice{
|
||||
g.Map{"id": nil, "name": "john"},
|
||||
g.Map{"id": nil, "name": "smith"},
|
||||
}, &array)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(array), 2)
|
||||
t.Assert(array[0].Name, "john")
|
||||
t.Assert(array[1].Name, "smith")
|
||||
})
|
||||
}
|
||||
|
||||
@ -1003,3 +1003,57 @@ func Test_Struct_AttrStructHasTheSameTag(t *testing.T) {
|
||||
t.Assert(order.Product.UpdatedAtFormat, "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_DirectReflectSet(t *testing.T) {
|
||||
type A struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
a = &A{
|
||||
Id: 1,
|
||||
Name: "john",
|
||||
}
|
||||
b *A
|
||||
)
|
||||
err := gconv.Struct(a, &b)
|
||||
t.Assert(err, nil)
|
||||
t.AssertEQ(a, b)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
a = A{
|
||||
Id: 1,
|
||||
Name: "john",
|
||||
}
|
||||
b A
|
||||
)
|
||||
err := gconv.Struct(a, &b)
|
||||
t.Assert(err, nil)
|
||||
t.AssertEQ(a, b)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_NilEmbeddedStructAttribute(t *testing.T) {
|
||||
type A struct {
|
||||
Name string
|
||||
}
|
||||
type B struct {
|
||||
*A
|
||||
Id int
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
b *B
|
||||
)
|
||||
err := gconv.Struct(g.Map{
|
||||
"id": 1,
|
||||
"name": nil,
|
||||
}, &b)
|
||||
t.Assert(err, nil)
|
||||
g.Dump(b)
|
||||
})
|
||||
}
|
||||
|
||||
@ -59,9 +59,15 @@ func Keys(mapOrStruct interface{}) (keysOrAttrs []string) {
|
||||
return
|
||||
}
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(mapOrStruct)
|
||||
reflectKind = reflectValue.Kind()
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
)
|
||||
if v, ok := mapOrStruct.(reflect.Value); ok {
|
||||
reflectValue = v
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(mapOrStruct)
|
||||
}
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind == reflect.Ptr {
|
||||
if !reflectValue.IsValid() || reflectValue.IsNil() {
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
@ -78,9 +84,17 @@ func Keys(mapOrStruct interface{}) (keysOrAttrs []string) {
|
||||
keysOrAttrs = append(keysOrAttrs, gconv.String(k.Interface()))
|
||||
}
|
||||
case reflect.Struct:
|
||||
reflectType := reflectValue.Type()
|
||||
var (
|
||||
fieldType reflect.StructField
|
||||
reflectType = reflectValue.Type()
|
||||
)
|
||||
for i := 0; i < reflectValue.NumField(); i++ {
|
||||
keysOrAttrs = append(keysOrAttrs, reflectType.Field(i).Name)
|
||||
fieldType = reflectType.Field(i)
|
||||
if fieldType.Anonymous {
|
||||
keysOrAttrs = append(keysOrAttrs, Keys(reflectValue.Field(i))...)
|
||||
} else {
|
||||
keysOrAttrs = append(keysOrAttrs, fieldType.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
@ -96,9 +110,15 @@ func Values(mapOrStruct interface{}) (values []interface{}) {
|
||||
return
|
||||
}
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(mapOrStruct)
|
||||
reflectKind = reflectValue.Kind()
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
)
|
||||
if v, ok := mapOrStruct.(reflect.Value); ok {
|
||||
reflectValue = v
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(mapOrStruct)
|
||||
}
|
||||
reflectKind = reflectValue.Kind()
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
@ -109,8 +129,17 @@ func Values(mapOrStruct interface{}) (values []interface{}) {
|
||||
values = append(values, reflectValue.MapIndex(k).Interface())
|
||||
}
|
||||
case reflect.Struct:
|
||||
var (
|
||||
fieldType reflect.StructField
|
||||
reflectType = reflectValue.Type()
|
||||
)
|
||||
for i := 0; i < reflectValue.NumField(); i++ {
|
||||
values = append(values, reflectValue.Field(i).Interface())
|
||||
fieldType = reflectType.Field(i)
|
||||
if fieldType.Anonymous {
|
||||
values = append(values, Values(reflectValue.Field(i))...)
|
||||
} else {
|
||||
values = append(values, reflectValue.Field(i).Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@ -15,6 +15,11 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// apiVal is used for type assert api for Val().
|
||||
type apiVal interface {
|
||||
Val() interface{}
|
||||
}
|
||||
|
||||
// apiString is used for type assert api for String().
|
||||
type apiString interface {
|
||||
String() string
|
||||
@ -36,37 +41,52 @@ func Dump(i ...interface{}) {
|
||||
// Export returns variables <i...> as a string with more manually readable.
|
||||
func Export(i ...interface{}) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for _, v := range i {
|
||||
switch r := v.(type) {
|
||||
for _, value := range i {
|
||||
switch r := value.(type) {
|
||||
case []byte:
|
||||
buffer.Write(r)
|
||||
case string:
|
||||
buffer.WriteString(r)
|
||||
default:
|
||||
var (
|
||||
rv = reflect.ValueOf(v)
|
||||
kind = rv.Kind()
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch kind {
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
v = gconv.Interfaces(v)
|
||||
value = gconv.Interfaces(value)
|
||||
case reflect.Map:
|
||||
v = gconv.Map(v)
|
||||
value = gconv.Map(value)
|
||||
case reflect.Struct:
|
||||
if r, ok := v.(apiMapStrAny); ok {
|
||||
v = r.MapStrAny()
|
||||
} else if r, ok := v.(apiString); ok {
|
||||
v = r.String()
|
||||
converted := false
|
||||
if r, ok := value.(apiVal); ok {
|
||||
if result := r.Val(); result != nil {
|
||||
value = result
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
if r, ok := value.(apiMapStrAny); ok {
|
||||
if result := r.MapStrAny(); result != nil {
|
||||
value = result
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
if r, ok := value.(apiString); ok {
|
||||
value = r.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
encoder := json.NewEncoder(buffer)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.SetIndent("", "\t")
|
||||
if err := encoder.Encode(v); err != nil {
|
||||
if err := encoder.Encode(value); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.14.3"
|
||||
const VERSION = "v1.14.5"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user