| Index: service/datastore/properties.go
|
| diff --git a/service/datastore/properties.go b/service/datastore/properties.go
|
| index dfa91515cca6432f38b102d2a119ca230e3655b5..11f62dc2939334b7581ee453aa541365b1c518cd 100644
|
| --- a/service/datastore/properties.go
|
| +++ b/service/datastore/properties.go
|
| @@ -224,27 +224,27 @@ func PropertyTypeOf(v interface{}, checkValid bool) (PropertyType, error) {
|
| // UpconvertUnderlyingType takes an object o, and attempts to convert it to
|
| // its native datastore-compatible type. e.g. int16 will convert to int64, and
|
| // `type Foo string` will convert to `string`.
|
| -func UpconvertUnderlyingType(o interface{}, t reflect.Type) (interface{}, reflect.Type) {
|
| +func UpconvertUnderlyingType(o interface{}) interface{} {
|
| + if o == nil {
|
| + return o
|
| + }
|
| +
|
| v := reflect.ValueOf(o)
|
| - switch t.Kind() {
|
| + t := v.Type()
|
| + switch v.Kind() {
|
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
| o = v.Int()
|
| - t = typeOfInt64
|
| case reflect.Bool:
|
| o = v.Bool()
|
| - t = typeOfBool
|
| case reflect.String:
|
| if t != typeOfBSKey {
|
| o = v.String()
|
| - t = typeOfString
|
| }
|
| case reflect.Float32, reflect.Float64:
|
| o = v.Float()
|
| - t = typeOfFloat64
|
| case reflect.Slice:
|
| if t != typeOfByteString && t.Elem().Kind() == reflect.Uint8 {
|
| o = v.Bytes()
|
| - t = typeOfByteSlice
|
| }
|
| case reflect.Struct:
|
| if t == typeOfTime {
|
| @@ -252,7 +252,7 @@ func UpconvertUnderlyingType(o interface{}, t reflect.Type) (interface{}, reflec
|
| o = v.Interface().(time.Time).Round(time.Microsecond)
|
| }
|
| }
|
| - return o, t
|
| + return o
|
| }
|
|
|
| // Value returns the current value held by this property. It's guaranteed to
|
| @@ -296,11 +296,9 @@ func (p *Property) Type() PropertyType { return p.propType }
|
| // a nil-valued property into a struct will set that field to the zero
|
| // value.
|
| func (p *Property) SetValue(value interface{}, is IndexSetting) (err error) {
|
| - t := reflect.Type(nil)
|
| pt := PTNull
|
| if value != nil {
|
| - t = reflect.TypeOf(value)
|
| - value, t = UpconvertUnderlyingType(value, t)
|
| + value = UpconvertUnderlyingType(value)
|
| if pt, err = PropertyTypeOf(value, true); err != nil {
|
| return
|
| }
|
| @@ -308,28 +306,15 @@ func (p *Property) SetValue(value interface{}, is IndexSetting) (err error) {
|
| p.propType = pt
|
| p.value = value
|
| p.indexSetting = is
|
| - if t == typeOfByteSlice {
|
| + if _, ok := value.([]byte); ok {
|
| p.indexSetting = NoIndex
|
| }
|
| return
|
| }
|
|
|
| -// PropertyLoadSaver may be implemented by a user type, and Interface will
|
| -// use this interface to serialize the type instead of trying to automatically
|
| -// create a serialization codec for it with helper.GetPLS.
|
| -type PropertyLoadSaver interface {
|
| - // Load takes the values from the given map and attempts to save them into
|
| - // the underlying object (usually a struct or a PropertyMap). If a fatal
|
| - // error occurs, it's returned via error. If non-fatal conversion errors
|
| - // occur, error will be a MultiError containing one or more ErrFieldMismatch
|
| - // objects.
|
| - Load(PropertyMap) error
|
| -
|
| - // Save returns the current property as a PropertyMap. if withMeta is true,
|
| - // then the PropertyMap contains all the metadata (e.g. '$meta' fields)
|
| - // which was held by this PropertyLoadSaver.
|
| - Save(withMeta bool) (PropertyMap, error)
|
| -
|
| +// MetaGetter is a subinterface of PropertyLoadSaver, but is also used to
|
| +// abstract the meta argument for RawInterface.GetMulti.
|
| +type MetaGetter interface {
|
| // GetMeta will get information about the field which has the struct tag in
|
| // the form of `gae:"$<key>[,<default>]?"`.
|
| //
|
| @@ -362,6 +347,38 @@ type PropertyLoadSaver interface {
|
| // }
|
| GetMeta(key string) (interface{}, error)
|
|
|
| + // GetMetaDefault is GetMeta, but with a default.
|
| + //
|
| + // If the metadata key is not available, or its type doesn't equal the
|
| + // homogenized type of dflt, then dflt will be returned.
|
| + //
|
| + // Type homogenization:
|
| + // signed integer types -> int64
|
| + // bool -> Toggle fields (bool)
|
| + //
|
| + // Example:
|
| + // pls.GetMetaDefault("foo", 100).(int64)
|
| + GetMetaDefault(key string, dflt interface{}) interface{}
|
| +}
|
| +
|
| +// PropertyLoadSaver may be implemented by a user type, and Interface will
|
| +// use this interface to serialize the type instead of trying to automatically
|
| +// create a serialization codec for it with helper.GetPLS.
|
| +type PropertyLoadSaver interface {
|
| + // Load takes the values from the given map and attempts to save them into
|
| + // the underlying object (usually a struct or a PropertyMap). If a fatal
|
| + // error occurs, it's returned via error. If non-fatal conversion errors
|
| + // occur, error will be a MultiError containing one or more ErrFieldMismatch
|
| + // objects.
|
| + Load(PropertyMap) error
|
| +
|
| + // Save returns the current property as a PropertyMap. if withMeta is true,
|
| + // then the PropertyMap contains all the metadata (e.g. '$meta' fields)
|
| + // which was held by this PropertyLoadSaver.
|
| + Save(withMeta bool) (PropertyMap, error)
|
| +
|
| + MetaGetter
|
| +
|
| // SetMeta allows you to set the current value of the meta-keyed field.
|
| SetMeta(key string, val interface{}) error
|
|
|
| @@ -435,6 +452,10 @@ func (pm PropertyMap) GetMeta(key string) (interface{}, error) {
|
| return v[0].Value(), nil
|
| }
|
|
|
| +func (pm PropertyMap) GetMetaDefault(key string, dflt interface{}) interface{} {
|
| + return GetMetaDefaultImpl(pm.GetMeta, key, dflt)
|
| +}
|
| +
|
| // SetMeta implements PropertyLoadSaver.SetMeta. It will only return an error
|
| // if `val` has an invalid type (e.g. not one supported by Property).
|
| func (pm PropertyMap) SetMeta(key string, val interface{}) error {
|
| @@ -450,3 +471,19 @@ func (pm PropertyMap) SetMeta(key string, val interface{}) error {
|
| func (pm PropertyMap) Problem() error {
|
| return nil
|
| }
|
| +
|
| +// GetMetaDefaultImpl is the implementation of PropertyLoadSaver.GetMetaDefault.
|
| +//
|
| +// It takes the normal GetMeta function, the key and the default, and returns
|
| +// the value according to PropertyLoadSaver.GetMetaDefault.
|
| +func GetMetaDefaultImpl(gm func(string) (interface{}, error), key string, dflt interface{}) interface{} {
|
| + dflt = UpconvertUnderlyingType(dflt)
|
| + cur, err := gm(key)
|
| + if err != nil {
|
| + return dflt
|
| + }
|
| + if dflt != nil && reflect.TypeOf(cur) != reflect.TypeOf(dflt) {
|
| + return dflt
|
| + }
|
| + return cur
|
| +}
|
|
|