Skip to content

Commit b9105b5

Browse files
Dean KarnDean Karn
authored andcommitted
Merge pull request #96 from bluesuncorp/v5-development
Updated README
2 parents 82abe38 + e5ebe76 commit b9105b5

File tree

4 files changed

+225
-6
lines changed

4 files changed

+225
-6
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ _testmain.go
2323
*.test
2424
*.prof
2525
*.test
26-
*.out
26+
*.out
27+
cover.html
28+
README.html

README.md

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ Package validator
55
[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v5?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v5)
66

77
Package validator implements value validations for structs and individual fields based on tags.
8-
It is also capable of Cross Field and Cross Struct validations.
8+
9+
It has the following **unique** features:
10+
11+
- Cross Field and Cross Struct validations.
12+
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
13+
- Handles type interface by determining it's underlying type prior to validation.
914

1015
Installation
11-
============
16+
------------
1217

1318
Use go get.
1419

@@ -23,12 +28,114 @@ Then import the validator package into your own code.
2328
import "gopkg.in/bluesuncorp/validator.v5"
2429

2530
Usage and documentation
26-
=======================
31+
------
2732

2833
Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v5 for detailed usage docs.
2934

35+
##### Example:
36+
```go
37+
package main
38+
39+
import (
40+
"fmt"
41+
42+
"gopkg.in/bluesuncorp/validator.v5"
43+
)
44+
45+
// User contains user information
46+
type User struct {
47+
FirstName string `validate:"required"`
48+
LastName string `validate:"required"`
49+
Age uint8 `validate:"gte=0,lte=130"`
50+
Email string `validate:"required,email"`
51+
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
52+
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
53+
}
54+
55+
// Address houses a users address information
56+
type Address struct {
57+
Street string `validate:"required"`
58+
City string `validate:"required"`
59+
Planet string `validate:"required"`
60+
Phone string `validate:"required"`
61+
}
62+
63+
var validate *validator.Validate
64+
65+
func main() {
66+
67+
validate = validator.New("validate", validator.BakedInValidators)
68+
69+
address := &Address{
70+
Street: "Eavesdown Docks",
71+
Planet: "Persphone",
72+
Phone: "none",
73+
}
74+
75+
user := &User{
76+
FirstName: "Badger",
77+
LastName: "Smith",
78+
Age: 135,
79+
80+
FavouriteColor: "#000",
81+
Addresses: []*Address{address},
82+
}
83+
84+
// returns nil or *StructErrors
85+
errs := validate.Struct(user)
86+
87+
if errs != nil {
88+
89+
// err will be of type *FieldError
90+
err := errs.Errors["Age"]
91+
fmt.Println(err.Error()) // output: Field validation for "Age" failed on the "lte" tag
92+
fmt.Println(err.Field) // output: Age
93+
fmt.Println(err.Tag) // output: lte
94+
fmt.Println(err.Kind) // output: uint8
95+
fmt.Println(err.Type) // output: uint8
96+
fmt.Println(err.Param) // output: 130
97+
fmt.Println(err.Value) // output: 135
98+
99+
// or if you prefer you can use the Flatten function
100+
// NOTE: I find this usefull when using a more hard static approach of checking field errors.
101+
// The above, is best for passing to some generic code to say parse the errors. i.e. I pass errs
102+
// to a routine which loops through the errors, creates and translates the error message into the
103+
// users locale and returns a map of map[string]string // field and error which I then use
104+
// within the HTML rendering.
105+
106+
flat := errs.Flatten()
107+
fmt.Println(flat) // output: map[Age:Field validation for "Age" failed on the "lte" tag Addresses[0].Address.City:Field validation for "City" failed on the "required" tag]
108+
err = flat["Addresses[0].Address.City"]
109+
fmt.Println(err.Field) // output: City
110+
fmt.Println(err.Tag) // output: required
111+
fmt.Println(err.Kind) // output: string
112+
fmt.Println(err.Type) // output: string
113+
fmt.Println(err.Param) // output:
114+
fmt.Println(err.Value) // output:
115+
116+
// from here you can create your own error messages in whatever language you wish
117+
return
118+
}
119+
120+
// save user to database
121+
}
122+
```
123+
124+
Benchmarks
125+
------
126+
###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3
127+
```go
128+
$ go test -cpu=4 -bench=. -benchmem=true
129+
PASS
130+
BenchmarkValidateField-4 3000000 436 ns/op 192 B/op 2 allocs/op
131+
BenchmarkValidateStructSimple-4 500000 2863 ns/op 784 B/op 13 allocs/op
132+
BenchmarkTemplateParallelSimple-4 500000 3044 ns/op 784 B/op 13 allocs/op
133+
BenchmarkValidateStructLarge-4 100000 15226 ns/op 4853 B/op 74 allocs/op
134+
BenchmarkTemplateParallelLarge-4 100000 14637 ns/op 4856 B/op 74 allocs/op
135+
```
136+
30137
How to Contribute
31-
=================
138+
------
32139

33140
There will always be a development branch for each version i.e. `v1-development`. In order to contribute,
34141
please make your pull requests against those branches.
@@ -41,5 +148,5 @@ I strongly encourage everyone whom creates a custom validation function to contr
41148
help make this package even better.
42149

43150
License
44-
=======
151+
------
45152
Distributed under MIT License, please see license file in code for more details.

examples/simple.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"gopkg.in/bluesuncorp/validator.v5"
7+
)
8+
9+
// User contains user information
10+
type User struct {
11+
FirstName string `validate:"required"`
12+
LastName string `validate:"required"`
13+
Age uint8 `validate:"gte=0,lte=130"`
14+
Email string `validate:"required,email"`
15+
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
16+
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
17+
}
18+
19+
// Address houses a users address information
20+
type Address struct {
21+
Street string `validate:"required"`
22+
City string `validate:"required"`
23+
Planet string `validate:"required"`
24+
Phone string `validate:"required"`
25+
}
26+
27+
var validate *validator.Validate
28+
29+
func main() {
30+
31+
validate = validator.New("validate", validator.BakedInValidators)
32+
33+
address := &Address{
34+
Street: "Eavesdown Docks",
35+
Planet: "Persphone",
36+
Phone: "none",
37+
}
38+
39+
user := &User{
40+
FirstName: "Badger",
41+
LastName: "Smith",
42+
Age: 135,
43+
44+
FavouriteColor: "#000",
45+
Addresses: []*Address{address},
46+
}
47+
48+
// returns nil or *StructErrors
49+
errs := validate.Struct(user)
50+
51+
if errs != nil {
52+
53+
// err will be of type *FieldError
54+
err := errs.Errors["Age"]
55+
fmt.Println(err.Error()) // output: Field validation for "Age" failed on the "lte" tag
56+
fmt.Println(err.Field) // output: Age
57+
fmt.Println(err.Tag) // output: lte
58+
fmt.Println(err.Kind) // output: uint8
59+
fmt.Println(err.Type) // output: uint8
60+
fmt.Println(err.Param) // output: 130
61+
fmt.Println(err.Value) // output: 135
62+
63+
// or if you prefer you can use the Flatten function
64+
// NOTE: I find this usefull when using a more hard static approach of checking field errors.
65+
// The above, is best for passing to some generic code to say parse the errors. i.e. I pass errs
66+
// to a routine which loops through the errors, creates and translates the error message into the
67+
// users locale and returns a map of map[string]string // field and error which I then use
68+
// within the HTML rendering.
69+
70+
flat := errs.Flatten()
71+
fmt.Println(flat) // output: map[Age:Field validation for "Age" failed on the "lte" tag Addresses[0].Address.City:Field validation for "City" failed on the "required" tag]
72+
err = flat["Addresses[0].Address.City"]
73+
fmt.Println(err.Field) // output: City
74+
fmt.Println(err.Tag) // output: required
75+
fmt.Println(err.Kind) // output: string
76+
fmt.Println(err.Type) // output: string
77+
fmt.Println(err.Param) // output:
78+
fmt.Println(err.Value) // output:
79+
80+
// from here you can create your own error messages in whatever language you wish
81+
return
82+
}
83+
84+
// save user to database
85+
}

validator_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import (
1414
// - Run "gocov test | gocov report" to report on test converage by file
1515
// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
1616
//
17+
// or
18+
//
19+
// -- may be a good idea to change to output path to somewherelike /tmp
20+
// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
21+
//
1722
//
1823
// go test -cpuprofile cpu.out
1924
// ./validator.test -test.bench=. -test.cpuprofile=cpu.prof
@@ -3765,3 +3770,23 @@ func TestInvalidValidatorFunction(t *testing.T) {
37653770

37663771
PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, fmt.Sprintf("Undefined validation function on field %s", ""))
37673772
}
3773+
3774+
func TestPoolObjectMaxSizeValidation(t *testing.T) {
3775+
// this will ensure that the pool objects are let go
3776+
// when the pool is saturated
3777+
validate.SetMaxStructPoolSize(0)
3778+
3779+
tSuccess := &TestSlice{
3780+
Required: []int{1},
3781+
Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
3782+
Min: []int{1, 2},
3783+
Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
3784+
MinMax: []int{1, 2, 3, 4, 5},
3785+
OmitEmpty: []int{},
3786+
}
3787+
3788+
for i := 0; i < 2; i++ {
3789+
err := validate.Struct(tSuccess)
3790+
Equal(t, err, nil)
3791+
}
3792+
}

0 commit comments

Comments
 (0)