Merge pull request #73 from bluesuncorp/v5

Merge latest changes from v5
pull/115/head
Dean Karn 10 years ago
commit 049d448603
  1. 5
      doc.go
  2. 68
      validator.go
  3. 12
      validator_test.go

@ -143,6 +143,11 @@ NOTE: Baked In Cross field validation only compares fields on the same struct,
if cross field + cross struct validation is needed your own custom validator if cross field + cross struct validation is needed your own custom validator
should be implemented. should be implemented.
NOTE2: comma is the default separator of validation tags, if you wish to have a comma
included within the parameter i.e. excludesall=, you will need to use the UTF-8 hex
representation 0x2C, which is replaced in the code as a comma, so the above will
become excludesall=0x2C
Here is a list of the current built in validators: Here is a list of the current built in validators:
- -

@ -20,6 +20,7 @@ import (
) )
const ( const (
utf8HexComma = "0x2C"
tagSeparator = "," tagSeparator = ","
orSeparator = "|" orSeparator = "|"
noValidationTag = "-" noValidationTag = "-"
@ -30,6 +31,48 @@ const (
structErrMsg = "Struct:%s\n" structErrMsg = "Struct:%s\n"
) )
var structPool *pool
// Pool holds a channelStructErrors.
type pool struct {
pool chan *StructErrors
}
// NewPool creates a new pool of Clients.
func newPool(max int) *pool {
return &pool{
pool: make(chan *StructErrors, max),
}
}
// Borrow a StructErrors from the pool.
func (p *pool) Borrow() *StructErrors {
var c *StructErrors
select {
case c = <-p.pool:
default:
c = &StructErrors{
Errors: map[string]*FieldError{},
StructErrors: map[string]*StructErrors{},
}
}
return c
}
// Return returns a StructErrors to the pool.
func (p *pool) Return(c *StructErrors) {
// c.Struct = ""
select {
case p.pool <- c:
default:
// let it go, let it go...
}
}
type cachedTags struct { type cachedTags struct {
keyVals [][]string keyVals [][]string
isOrVal bool isOrVal bool
@ -187,6 +230,9 @@ type Validate struct {
// New creates a new Validate instance for use. // New creates a new Validate instance for use.
func New(tagName string, funcs map[string]Func) *Validate { func New(tagName string, funcs map[string]Func) *Validate {
structPool = newPool(10)
return &Validate{ return &Validate{
tagName: tagName, tagName: tagName,
validationFuncs: funcs, validationFuncs: funcs,
@ -200,6 +246,16 @@ func (v *Validate) SetTag(tagName string) {
v.tagName = tagName v.tagName = tagName
} }
// SetStructPoolMax sets the struct pools max size. this may be usefull for fine grained
// performance tuning towards your application, however, the default should be fine for
// nearly all cases. only increase if you have a deeply nested struct structure.
// NOTE: this method is not thread-safe
// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed
// and the max pool size will be passed into the New function
func (v *Validate) SetMaxStructPoolSize(max int) {
structPool = newPool(max)
}
// AddFunction adds a validation Func to a Validate's map of validators denoted by the key // AddFunction adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced. // NOTE: if the key already exists, it will get replaced.
// NOTE: this method is not thread-safe // NOTE: this method is not thread-safe
@ -259,11 +315,8 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
structCache.Set(structType, cs) structCache.Set(structType, cs)
} }
validationErrors := &StructErrors{ validationErrors := structPool.Borrow()
Struct: structName, validationErrors.Struct = structName
Errors: map[string]*FieldError{},
StructErrors: map[string]*StructErrors{},
}
for i := 0; i < numFields; i++ { for i := 0; i < numFields; i++ {
@ -359,6 +412,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
} }
if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 { if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
structPool.Return(validationErrors)
return nil return nil
} }
@ -428,7 +482,7 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
cField.tags = append(cField.tags, cTag) cField.tags = append(cField.tags, cTag)
for i, val := range orVals { for i, val := range orVals {
vals := strings.Split(val, tagKeySeparator) vals := strings.SplitN(val, tagKeySeparator, 2)
key := strings.TrimSpace(vals[0]) key := strings.TrimSpace(vals[0])
@ -438,7 +492,7 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
param := "" param := ""
if len(vals) > 1 { if len(vals) > 1 {
param = vals[1] param = strings.Replace(vals[1], utf8HexComma, ",", -1)
} }
cTag.keyVals[i] = []string{key, param} cTag.keyVals[i] = []string{key, param}

@ -281,6 +281,16 @@ func TestExcludesAllValidation(t *testing.T) {
err := validate.Field(username, "excludesall=@ ") err := validate.Field(username, "excludesall=@ ")
NotEqual(t, err, nil) NotEqual(t, err, nil)
excluded := ","
err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?")
NotEqual(t, err, nil)
excluded = "="
err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?")
NotEqual(t, err, nil)
} }
func TestExcludesValidation(t *testing.T) { func TestExcludesValidation(t *testing.T) {
@ -2034,6 +2044,8 @@ func TestFlattening(t *testing.T) {
func TestStructStringValidation(t *testing.T) { func TestStructStringValidation(t *testing.T) {
validate.SetMaxStructPoolSize(11)
tSuccess := &TestString{ tSuccess := &TestString{
Required: "Required", Required: "Required",
Len: "length==10", Len: "length==10",

Loading…
Cancel
Save