I'm trying to create a generator of handlers that works for several kinds of entities. The following code works if the items slice is of a specific type (eg: var items []Person
), but I'm having problems generalizing it: specifically "ERROR: Can't get items: datastore: invalid entity type
". Any idea of how to declare items so that it have a type valid for GetAll?
func ScaffoldList(entity interface{}, collection string, templ *template.Template) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
c := appengine.NewContext(r)
T := reflect.TypeOf(entity)
q := datastore.NewQuery(T.String())
items := reflect.MakeSlice(reflect.SliceOf(T), 0, 10)
_, err := q.GetAll(c, &items)
if err != nil{
c.Errorf("Can't get items: %v", err)
http.Error(w, "Can't get items", http.StatusInternalServerError)
return
}
context.Set(r, collection, items)
ion.RenderTemplate(templ).ServeHTTP(w, r)
})
}
Here's how you get an argument of type *[]Person:
items := reflect.New(reflect.SliceOf(T))
_, err := q.GetAll(c, items.Interface())
GetAll will allocate the backing array for the slice. There's no need to preallocate the backing array with make
.
Assuming that the argument to context.Set is a value containing a slice, then you need to dereference the pointer:
context.Set(r, collection, items.Elem())
If the context does not work with reflect values, then you probably want:
context.Set(r, collection, items.Elem().Interface())
There are at least two code-generator based tools for creating type-safe parameterised code. The two I know of are
I am currently using Gen. It's not as seamless as a built-in language feature might be, but it's a good way to produce generic collection types etc. Gen templates are called typewriters. Using the provided typewriters is really quite easy. Creating your own is a bit more work.
Gen works using the Go template package. So the typewriters are written merely as strings; the compiler doesn't check them until output code is created (a minor drawback).
Conversely, Genny tries to allow you to write compilable code containing generic marker types, and then it substitutes the generic markers with specific types. This is a simpler approach but didn't always work for me.