Skip to content

Commit a13509d

Browse files
Dean KarnDean Karn
authored andcommitted
Merge pull request #141 from bluesuncorp/v6-development
Add RegisterCustomTypeFunc for easier adding of CustomTypeFunc
2 parents 048d7b8 + 442b210 commit a13509d

File tree

5 files changed

+150
-63
lines changed

5 files changed

+150
-63
lines changed

README.md

Lines changed: 25 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Package validator
22
================
33

44
[![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5-
[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487374/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
5+
[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487383/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
66
[![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6)
77
[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6)
88

@@ -138,84 +138,51 @@ Custom Field Type
138138
package main
139139

140140
import (
141-
"errors"
141+
"database/sql"
142+
"database/sql/driver"
142143
"fmt"
143144
"reflect"
144145

145-
sql "database/sql/driver"
146-
147146
"gopkg.in/bluesuncorp/validator.v6"
148147
)
149148

150-
var validate *validator.Validate
151-
152-
type valuer struct {
153-
Name string
154-
}
155-
156-
func (v valuer) Value() (sql.Value, error) {
157-
158-
if v.Name == "errorme" {
159-
return nil, errors.New("some kind of error")
160-
}
161-
162-
if v.Name == "blankme" {
163-
return "", nil
164-
}
165-
166-
if len(v.Name) == 0 {
167-
return nil, nil
168-
}
169-
170-
return v.Name, nil
171-
}
172-
173-
// ValidateValuerType implements validator.CustomTypeFunc
174-
func ValidateValuerType(field reflect.Value) interface{} {
175-
if valuer, ok := field.Interface().(sql.Valuer); ok {
176-
val, err := valuer.Value()
177-
if err != nil {
178-
// handle the error how you want
179-
return nil
180-
}
181-
182-
return val
183-
}
184-
185-
return nil
149+
// DbBackedUser User struct
150+
type DbBackedUser struct {
151+
Name sql.NullString `validate:"required"`
152+
Age sql.NullInt64 `validate:"required"`
186153
}
187154

188155
func main() {
189156

190-
customTypes := map[reflect.Type]validator.CustomTypeFunc{}
191-
customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType
192-
customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType
193-
194157
config := validator.Config{
195158
TagName: "validate",
196159
ValidationFuncs: validator.BakedInValidators,
197-
CustomTypeFuncs: customTypes,
198160
}
199161

200-
validate = validator.New(config)
162+
validate := validator.New(config)
201163

202-
validateCustomFieldType()
203-
}
164+
// register all sql.Null* types to use the ValidateValuer CustomTypeFunc
165+
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
204166

205-
func validateCustomFieldType() {
206-
val := valuer{
207-
Name: "blankme",
208-
}
167+
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
168+
errs := validate.Struct(x)
209169

210-
errs := validate.Field(val, "required")
211-
if errs != nil {
212-
fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag
213-
return
170+
if len(errs) > 0 {
171+
fmt.Printf("Errs:\n%+v\n", errs)
214172
}
215-
216-
// all ok
217173
}
218174

175+
// ValidateValuer implements validator.CustomTypeFunc
176+
func ValidateValuer(field reflect.Value) interface{} {
177+
if valuer, ok := field.Interface().(driver.Valuer); ok {
178+
val, err := valuer.Value()
179+
if err == nil {
180+
return val
181+
}
182+
// handle the error how you want
183+
}
184+
return nil
185+
}
219186
```
220187

221188
Benchmarks

examples/custom/custom.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"database/sql/driver"
6+
"fmt"
7+
"reflect"
8+
9+
"gopkg.in/bluesuncorp/validator.v6"
10+
)
11+
12+
// DbBackedUser User struct
13+
type DbBackedUser struct {
14+
Name sql.NullString `validate:"required"`
15+
Age sql.NullInt64 `validate:"required"`
16+
}
17+
18+
func main() {
19+
20+
config := validator.Config{
21+
TagName: "validate",
22+
ValidationFuncs: validator.BakedInValidators,
23+
}
24+
25+
validate := validator.New(config)
26+
27+
// register all sql.Null* types to use the ValidateValuer CustomTypeFunc
28+
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
29+
30+
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
31+
errs := validate.Struct(x)
32+
33+
if len(errs) > 0 {
34+
fmt.Printf("Errs:\n%+v\n", errs)
35+
}
36+
}
37+
38+
// ValidateValuer implements validator.CustomTypeFunc
39+
func ValidateValuer(field reflect.Value) interface{} {
40+
if valuer, ok := field.Interface().(driver.Valuer); ok {
41+
val, err := valuer.Value()
42+
if err == nil {
43+
return val
44+
}
45+
// handle the error how you want
46+
}
47+
return nil
48+
}
File renamed without changes.

validator.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,20 @@ func (v *Validate) RegisterValidation(key string, f Func) error {
157157
return nil
158158
}
159159

160+
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
161+
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
162+
163+
if v.config.CustomTypeFuncs == nil {
164+
v.config.CustomTypeFuncs = map[reflect.Type]CustomTypeFunc{}
165+
}
166+
167+
for _, t := range types {
168+
v.config.CustomTypeFuncs[reflect.TypeOf(t)] = fn
169+
}
170+
171+
v.config.hasCustomFuncs = true
172+
}
173+
160174
// Field validates a single field using tag style validation and returns ValidationErrors
161175
// NOTE: it returns ValidationErrors instead of a single FieldError because this can also
162176
// validate Array, Slice and maps fields which may contain more than one error

validator_test.go

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package validator
22

33
import (
4+
"database/sql"
5+
"database/sql/driver"
46
"errors"
57
"fmt"
68
"reflect"
79
"testing"
810
"time"
911

10-
sql "database/sql/driver"
11-
1212
. "gopkg.in/bluesuncorp/assert.v1"
1313
)
1414

@@ -126,7 +126,7 @@ type valuer struct {
126126
Name string
127127
}
128128

129-
func (v valuer) Value() (sql.Value, error) {
129+
func (v valuer) Value() (driver.Value, error) {
130130

131131
if v.Name == "errorme" {
132132
return nil, errors.New("some kind of error")
@@ -178,7 +178,7 @@ type CustomMadeUpStruct struct {
178178
}
179179

180180
func ValidateValuerType(field reflect.Value) interface{} {
181-
if valuer, ok := field.Interface().(sql.Valuer); ok {
181+
if valuer, ok := field.Interface().(driver.Valuer); ok {
182182
val, err := valuer.Value()
183183
if err != nil {
184184
// handle the error how you want
@@ -191,10 +191,68 @@ func ValidateValuerType(field reflect.Value) interface{} {
191191
return nil
192192
}
193193

194+
func TestSQLValue2Validation(t *testing.T) {
195+
196+
config := Config{
197+
TagName: "validate",
198+
ValidationFuncs: BakedInValidators,
199+
}
200+
201+
validate := New(config)
202+
validate.RegisterCustomTypeFunc(ValidateValuerType, valuer{}, (*driver.Valuer)(nil), sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
203+
validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{})
204+
validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1)
205+
206+
val := valuer{
207+
Name: "",
208+
}
209+
210+
errs := validate.Field(val, "required")
211+
NotEqual(t, errs, nil)
212+
AssertError(t, errs, "", "", "required")
213+
214+
val.Name = "Valid Name"
215+
errs = validate.Field(val, "required")
216+
Equal(t, errs, nil)
217+
218+
val.Name = "errorme"
219+
220+
PanicMatches(t, func() { errs = validate.Field(val, "required") }, "SQL Driver Valuer error: some kind of error")
221+
222+
type myValuer valuer
223+
224+
myVal := valuer{
225+
Name: "",
226+
}
227+
228+
errs = validate.Field(myVal, "required")
229+
NotEqual(t, errs, nil)
230+
AssertError(t, errs, "", "", "required")
231+
232+
cust := MadeUpCustomType{
233+
FirstName: "Joey",
234+
LastName: "Bloggs",
235+
}
236+
237+
c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2}
238+
239+
errs = validate.Struct(c)
240+
Equal(t, errs, nil)
241+
242+
c.MadeUp.FirstName = ""
243+
c.OverriddenInt = 1
244+
245+
errs = validate.Struct(c)
246+
NotEqual(t, errs, nil)
247+
Equal(t, len(errs), 2)
248+
AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "MadeUp", "required")
249+
AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "gt")
250+
}
251+
194252
func TestSQLValueValidation(t *testing.T) {
195253

196254
customTypes := map[reflect.Type]CustomTypeFunc{}
197-
customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType
255+
customTypes[reflect.TypeOf((*driver.Valuer)(nil))] = ValidateValuerType
198256
customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType
199257
customTypes[reflect.TypeOf(MadeUpCustomType{})] = ValidateCustomType
200258
customTypes[reflect.TypeOf(1)] = OverrideIntTypeForSomeReason

0 commit comments

Comments
 (0)