Index: service/datastore/pls.go |
diff --git a/service/datastore/pls.go b/service/datastore/pls.go |
index 63a07eb25191256722200721e89195307c22434a..35eb42dc3c03a94be6313915ea5ed85f40b631b7 100644 |
--- a/service/datastore/pls.go |
+++ b/service/datastore/pls.go |
@@ -8,24 +8,106 @@ import ( |
"reflect" |
) |
-// GetPLS resolves o into a PropertyLoadSaver. o must be a pointer to a |
-// struct of some sort. |
-func GetPLS(o interface{}) PropertyLoadSaver { |
- v := reflect.ValueOf(o) |
+// GetPLS resolves obj into a PropertyLoadSaver. |
+// |
+// obj must be a non-nil pointer to a struct of some sort. |
+// |
+// By default, exported fields will be serialized to/from the datastore. If the |
+// field is not exported, it will be skipped by the serialization routines. |
+// |
+// If a field is of a non-supported type (see Property for the list of supported |
+// property types), the resulting PropertyLoadSaver will have a non-nil |
+// Problem(). Other problems include duplicate field names (due to tagging), |
+// recursively defined structs, nested structures with multiple slices (e.g. |
+// slices of slices, either directly `[][]type` or indirectly `[]Embedded` where |
+// Embedded contains a slice.) |
+// |
+// GetPLS supports the following struct tag syntax: |
+// `gae:"fieldName[,noindex]"` -- an alternate fieldname for an exportable |
+// field. When the struct is serialized or deserialized, fieldName will be |
+// associated with the struct field instead of the field's Go name. This is |
+// useful when writing Go code which interfaces with appengine code written |
+// in other languages (like python) which use lowercase as their default |
+// datastore field names. |
+// |
+// A fieldName of "-" means that gae will ignore the field for all |
+// serialization/deserialization. |
+// |
+// if noindex is specified, then this field will not be indexed in the |
+// datastore, even if it was an otherwise indexable type. If fieldName is |
+// blank, and noindex is specifed, then fieldName will default to the |
+// field's actual name. Note that by default, all fields (with indexable |
+// types) are indexed. |
+// |
+// `gae:"$metaKey[,<value>]` -- indicates a field is metadata. Metadata |
+// can be used to control filter behavior, or to store key data when using |
+// the Interface.KeyForObj* methods. The supported field types are: |
+// - Key |
+// - int64 |
+// - string |
+// - Toggle (GetMeta and SetMeta treat the field as if it were bool) |
+// Additionally, int64, string and Toggle allow setting a default value |
+// in the struct field tag (the "<value>" portion). |
+// |
+// Only exported fields allow SetMeta, but all fields of appropriate type |
+// allow tagged defaults. See Examples. |
+// |
+// Example "special" structure. This is supposed to be some sort of datastore |
+// singleton object. |
+// struct secretFoo { |
+// // _id and _kind are not exported, so setting their values will not be |
+// // reflected by GetMeta. |
+// _id int64 `gae:"$id,1"` |
+// _kind string `gae:"$kind,InternalFooSingleton"` |
+// |
+// // Value is exported, so can be read and written by the PropertyLoadSaver, |
+// // but secretFoo is shared with a python appengine module which has |
+// // stored this field as 'value' instead of 'Value'. |
+// Value int64 `gae:"value"` |
+// } |
+// |
+// Example "normal" structure that you might use in a go-only appengine app. |
+// struct User { |
+// ID string `gae:"$id"` |
+// // "kind" is automatically implied by the struct name: "User" |
+// // "parent" is nil... Users are root entities |
+// |
+// // 'Name' will serialized to the datastore in the field 'Name' |
+// Name string |
+// } |
+// |
+// struct Comment { |
+// ID int64 `gae:"$id"` |
+// // "kind" is automatically implied by the struct name: "Comment" |
+// |
+// // Parent will be enforced by the application to be a User key. |
+// Parent Key `gae:"$parent"` |
+// |
+// // 'Lines' will serialized to the datastore in the field 'Lines' |
+// Lines []string |
+// } |
+func GetPLS(obj interface{}) PropertyLoadSaver { |
+ v := reflect.ValueOf(obj) |
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { |
return &structPLS{c: &structCodec{problem: ErrInvalidEntityType}} |
} |
+ if v.IsNil() { |
+ return &structPLS{c: &structCodec{problem: ErrInvalidEntityType}} |
+ } |
v = v.Elem() |
- t := v.Type() |
+ c := getCodec(v.Type()) |
+ return &structPLS{v, c} |
+} |
+func getCodec(structType reflect.Type) *structCodec { |
structCodecsMutex.RLock() |
- if c, ok := structCodecs[t]; ok { |
- structCodecsMutex.RUnlock() |
- return &structPLS{v, c} |
- } |
+ c, ok := structCodecs[structType] |
structCodecsMutex.RUnlock() |
+ if ok { |
+ return c |
+ } |
structCodecsMutex.Lock() |
defer structCodecsMutex.Unlock() |
- return &structPLS{v, getStructCodecLocked(t)} |
+ return getStructCodecLocked(structType) |
} |