templates

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2019 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var BindingTemplate = template.Must(template.New("binding").Funcs(funcMap).Parse(
	`// Code generated by ObjectBox; DO NOT EDIT. 
// Learn more about defining entities and generating this file - visit https://golang.objectbox.io/entity-annotations

{{define "property-getter"}}{{/* used in Load*/}}
	{{- if .Converter}}{{.Converter}}ToEntityProperty(
	{{- else if .CastOnWrite}}{{.CastOnWrite}}({{end}}
		{{- if eq .GoType "bool"}} table.GetBoolSlot({{.FbvTableOffset}}, false)
    	{{- else if eq .GoType "int"}} int(table.GetUint64Slot({{.FbvTableOffset}}, 0))
    	{{- else if eq .GoType "uint"}} uint(table.GetUint64Slot({{.FbvTableOffset}}, 0))
		{{- else if eq .GoType "rune"}} rune(table.GetInt32Slot({{.FbvTableOffset}}, 0))
		{{- else if eq .FbType "UOffsetT"}} fbutils.Get{{.ObType}}Slot(table, {{.FbvTableOffset}})
    	{{- else}} table.Get{{.GoType | StringTitle}}Slot({{.FbvTableOffset}}, 0)
    	{{- end}}
	{{- if or .Converter .CastOnWrite}}){{end}}
{{- end -}}

{{define "property-converter-encode"}}{{/* used in Flatten*/ -}}
	{{- if .Converter}}{{.Converter}}ToDatabaseValue(obj.{{.Path}})
	{{- else if .CastOnRead}}{{.CastOnRead}}(obj.{{.Path}})
	{{- else}}obj.{{.Path}}{{end}}
{{- end -}}


package {{.Binding.Package.Name}}

import (
	"github.com/google/flatbuffers/go"
	"github.com/objectbox/objectbox-go/objectbox"
	"github.com/objectbox/objectbox-go/objectbox/fbutils"
	{{range $path := .Binding.Imports -}}
		"{{$path}}"
	{{end}}
)

{{range $entity := .Binding.Entities -}}
{{$entityNameCamel := $entity.Name | StringCamel -}}
type {{$entityNameCamel}}_EntityInfo struct {
	objectbox.Entity
	Uid uint64
}

var {{$entity.Name}}Binding = {{$entityNameCamel}}_EntityInfo {
	Entity: objectbox.Entity{
		Id: {{$entity.Id}},
	}, 
	Uid: {{$entity.Uid}},
}

// {{$entity.Name}}_ contains type-based Property helpers to facilitate some common operations such as Queries. 
var {{$entity.Name}}_ = struct {
	{{range $property := $entity.Properties -}}
    	{{$property.Name}} *objectbox.{{with $property.Relation}}RelationToOne{{else}}Property{{$property.GoType | TypeIdentifier}}{{end}}
    {{end -}}
	{{range $relation := $entity.Relations -}}
    	{{$relation.Name}} *objectbox.RelationToMany
	{{end -}}
}{
	{{range $property := $entity.Properties -}}
    {{$property.Name}}: &objectbox.
		{{- with $property.Relation}}RelationToOne{
			Property:
		{{- else}}Property{{$property.GoType | TypeIdentifier}}{
			BaseProperty:
		{{- end -}} 
		&objectbox.BaseProperty{
			Id: {{$property.Id}},
			Entity: &{{$entity.Name}}Binding.Entity,
		},{{with $property.Relation}}
		Target: &{{.Target.Name}}Binding.Entity,{{end}}
	},
    {{end -}}
	{{range $relation := $entity.Relations -}}
    	{{$relation.Name}}: &objectbox.RelationToMany{
			Id: {{$relation.Id}},
			Source: &{{$entity.Name}}Binding.Entity,
			Target: &{{$relation.Target.Name}}Binding.Entity,
		},
    {{end -}}
}

// GeneratorVersion is called by ObjectBox to verify the compatibility of the generator used to generate this code	
func ({{$entityNameCamel}}_EntityInfo) GeneratorVersion() int {
	return {{$.GeneratorVersion}}
}

// AddToModel is called by ObjectBox during model build
func ({{$entityNameCamel}}_EntityInfo) AddToModel(model *objectbox.Model) {
    model.Entity("{{$entity.Name}}", {{$entity.Id}}, {{$entity.Uid}})
    {{range $property := $entity.Properties -}}
    model.Property("{{$property.ObName}}", objectbox.PropertyType_{{$property.ObType}}, {{$property.Id}}, {{$property.Uid}})
    {{if len $property.ObFlags -}}
        model.PropertyFlags(
        {{- range $key, $flag := $property.ObFlags -}}
            {{if gt $key 0}} | {{end}}objectbox.PropertyFlags_{{$flag}}
        {{- end}})
    {{end -}}
	{{if $property.Relation}}model.PropertyRelation("{{$property.Relation.Target.Name}}", {{$property.Index.Id}}, {{$property.Index.Uid}})
	{{else if $property.Index}}model.PropertyIndex({{$property.Index.Id}}, {{$property.Index.Uid}})
    {{end -}}
    {{end -}}
    model.EntityLastPropertyId({{$entity.LastPropertyId.GetId}}, {{$entity.LastPropertyId.GetUid}})
	{{range $relation := $entity.Relations -}}
    model.Relation({{$relation.Id}}, {{$relation.Uid}}, {{$relation.Target.Name}}Binding.Id, {{$relation.Target.Name}}Binding.Uid)
    {{end -}}
}

// GetId is called by ObjectBox during Put operations to check for existing ID on an object
func ({{$entityNameCamel}}_EntityInfo) GetId(object interface{}) (uint64, error) {
	{{- if $.Options.ByValue}}
		if obj, ok := object.(*{{$entity.Name}}); ok {
			return {{template "property-converter-encode" $entity.IdProperty}}, nil
		} else {
			return {{if $entity.IdProperty.Converter}}{{$entity.IdProperty.Converter}}ToDatabaseValue({{end -}}
					object.({{$entity.Name}}).{{$entity.IdProperty.Path}}{{if $entity.IdProperty.Converter}}){{end}}, nil
		}
	{{- else -}}
		return {{if $entity.IdProperty.Converter}}{{$entity.IdProperty.Converter}}ToDatabaseValue({{end -}}
				object.(*{{$entity.Name}}).{{$entity.IdProperty.Path}}{{if $entity.IdProperty.Converter}}){{end}}, nil
	{{- end}}
}

// SetId is called by ObjectBox during Put to update an ID on an object that has just been inserted
func ({{$entityNameCamel}}_EntityInfo) SetId(object interface{}, id uint64) {
	{{- if $.Options.ByValue}}
		if obj, ok := object.(*{{$entity.Name}}); ok {
			obj.{{$entity.IdProperty.Path}} =   
				{{- if $entity.IdProperty.Converter}}{{$entity.IdProperty.Converter}}ToEntityProperty({{end}}id{{if $entity.IdProperty.Converter}}){{end}}
		} else {
			// NOTE while this can't update, it will at least behave consistently (panic in case of a wrong type)
			_ = object.({{$entity.Name}}).{{$entity.IdProperty.Path}}
		}
	{{- else -}}
		object.(*{{$entity.Name}}).{{$entity.IdProperty.Path}} =  
			{{- if $entity.IdProperty.Converter}}{{$entity.IdProperty.Converter}}ToEntityProperty({{end}}id{{if $entity.IdProperty.Converter}}){{end}}
	{{- end}}
}

// PutRelated is called by ObjectBox to put related entities before the object itself is flattened and put
func ({{$entityNameCamel}}_EntityInfo) PutRelated(txn *objectbox.Transaction, object interface{}, id uint64) error {
	{{- block "put-relations" $entity}}
	{{- /* TODO ideally we should be using BoxForTarget() with a manually assigned txn */}}
	{{- range $field := .Fields}}
		{{- if $field.SimpleRelation}}
			if rel := {{if not $field.IsPointer}}&{{end}}object.(*{{$field.Entity.Name}}).{{$field.Name}}; rel != nil {
				rId, err := {{$field.SimpleRelation.Target.Name}}Binding.GetId(rel)
				if err != nil {
					return err
				} else if rId == 0 {
					if err := txn.RunWithCursor({{$field.SimpleRelation.Target.Name}}Binding.Id, func(targetCursor *objectbox.Cursor) error {
						_, err := targetCursor.Put(rel) // NOTE Put/PutAsync() has a side-effect of setting the rel.ID
						return err
					}); err != nil {
						return err
					}
				}
			}
		{{- else if $field.StandaloneRelation}}
			if err := txn.RunWithCursor({{$field.Entity.Name}}Binding.Id, func(cursor *objectbox.Cursor) error {
				return cursor.RelationReplace({{$field.StandaloneRelation.Id}}, {{$field.StandaloneRelation.Target.Name}}Binding.Id, id, object, object.(*{{$field.Entity.Name}}).{{$field.Name}})
			}); err != nil {
				return err
			}
		{{- else}}{{/* recursively visit fields in embedded structs */}}{{template "put-relations" $field}}
		{{- end}}
	{{- end}}{{end}}
	return nil
}

// Flatten is called by ObjectBox to transform an object to a FlatBuffer
func ({{$entityNameCamel}}_EntityInfo) Flatten(object interface{}, fbb *flatbuffers.Builder, id uint64) error {
    {{if $entity.HasNonIdProperty -}}
		{{- if not $.Options.ByValue}}obj := object.(*{{$entity.Name}}) 
		{{- else -}}
		var obj *{{$entity.Name}}
		if objPtr, ok := object.(*{{$entity.Name}}); ok {
			obj = objPtr 
		} else {
			objVal := object.({{$entity.Name}})
			obj = &objVal
		}
		{{end}}
	{{- end -}}

    {{- range $property := $entity.Properties}}{{if eq $property.FbType "UOffsetT"}}
    var offset{{$property.Name}} = fbutils.Create{{$property.ObType}}Offset(fbb, {{template "property-converter-encode" $property}})
	{{- end}}{{end}}

	{{- block "store-relations" $entity}}
	{{- /* store relations, TODO ideally we should be using BoxForTarget() with a manually assigned txn */}}
	{{- range $field := .Fields}}
		{{if $field.SimpleRelation}}
			var rId{{$field.Property.Name}} uint64
			if rel := {{if not $field.IsPointer}}&{{end}}obj.{{$field.Name}}; rel != nil {
				if rId, err := {{$field.SimpleRelation.Target.Name}}Binding.GetId(rel); err != nil {
					return err
				} else {
					rId{{$field.Property.Name}} = rId
				}
			}
		{{- else if $field.Property}}{{if $field.Property.Relation}}{{/* manual relation links (just ID)*/}}
			var rId{{$field.Property.Name}} = {{template "property-converter-encode" $field.Property}}{{- end}}
		{{- else}}{{/* recursively visit fields in embedded structs */}}{{template "store-relations" $field}}
		{{end}}
	{{end}}{{end}}

    // build the FlatBuffers object
    fbb.StartObject({{$entity.LastPropertyId.GetId}})
    {{range $property := $entity.Properties -}}
    fbutils.Set{{$property.FbType}}Slot(fbb, {{$property.FbSlot}},
		{{- if $property.Relation}}rId{{$property.Name}})
        {{- else if eq $property.FbType "UOffsetT"}} offset{{$property.Name}})
        {{- else if eq $property.Name $entity.IdProperty.Name}} id)
        {{- else if eq $property.GoType "int"}} int64({{template "property-converter-encode" $property}}))
        {{- else if eq $property.GoType "uint"}} uint64({{template "property-converter-encode" $property}}))
        {{- else}} {{template "property-converter-encode" $property}})
        {{- end}}
    {{end -}}
	return nil
}

// Load is called by ObjectBox to load an object from a FlatBuffer 
func ({{$entityNameCamel}}_EntityInfo) Load(txn *objectbox.Transaction, bytes []byte) (interface{}, error) {
	var table = &flatbuffers.Table{
		Bytes: bytes,
		Pos:   flatbuffers.GetUOffsetT(bytes),
	}
	var id = table.Get{{$entity.IdProperty.GoType | StringTitle}}Slot({{$entity.IdProperty.FbvTableOffset}}, 0)
	
	{{- block "load-relations" $entity}}
	{{- /* TODO ideally we should be using BoxForTarget() with a manually assigned txn */}}
	{{- range $field := .Fields}}
		{{if $field.SimpleRelation -}}
			var rel{{$field.Name}} *{{$field.Type}}
			if rId := {{template "property-getter" $field.Property}}; rId > 0 {
				if err := txn.RunWithCursor({{$field.SimpleRelation.Target.Name}}Binding.Id, func(targetCursor *objectbox.Cursor) error {
					if relObject, err := targetCursor.Get(rId); err != nil {
						return err 
					} else if relObj, ok := relObject.(*{{$field.Type}}); ok {
						rel{{$field.Name}} = relObj
					} else {
						var relObj = relObject.({{$field.Type}})
						rel{{$field.Name}} = &relObj
					}
					return nil
				}); err != nil {
					return nil, err
				}
			{{if not $field.IsPointer -}} 
			} else {
				rel{{$field.Name}} = &{{$field.Type}}{}
			{{end -}}
			}
		{{else if $field.StandaloneRelation -}}
			var rel{{$field.Name}} {{$field.Type}} 
			if err := txn.RunWithCursor({{$field.Entity.Name}}Binding.Id, func(cursor *objectbox.Cursor) error {
				if rSlice, err := cursor.RelationGetAll({{$field.StandaloneRelation.Id}}, {{$field.StandaloneRelation.Target.Name}}Binding.Id, id); err != nil {
					return err
				} else {
					rel{{$field.Name}} = {{if $field.IsPointer}}&{{end}}rSlice.({{$field.Type}})
					return nil
				}
			}); err != nil {
				return nil, err
			}
			
		{{else}}{{/* recursively visit fields in embedded structs */}}{{template "load-relations" $field}}
		{{- end}}
	{{end}}{{end}}

	return &{{$entity.Name}}{
	{{- block "fields-initializer" $entity}}
		{{- range $field := .Fields}}
			{{$field.Name}}: 
				{{- if or $field.SimpleRelation}}{{if not $field.IsPointer}}*{{end}}rel{{$field.Name}}
				{{- else if $field.StandaloneRelation}}rel{{$field.Name}}
        		{{- else if $field.IsId}}{{with $field.Property}}
					{{- if .Converter}}{{.Converter}}ToEntityProperty(
					{{- else if .CastOnWrite}}{{.CastOnWrite}}({{end -}}
					id
					{{- if or .Converter .CastOnWrite}}){{end}}{{end}}
				{{- else if $field.Property}}{{template "property-getter" $field.Property}}
				{{- else}}{{if $field.IsPointer}}&{{end}}{{$field.Type}}{ {{template "fields-initializer" $field}} }{{end}},
		{{- end}}
	{{end}}
	}, nil
}

// MakeSlice is called by ObjectBox to construct a new slice to hold the read objects  
func ({{$entityNameCamel}}_EntityInfo) MakeSlice(capacity int) interface{} {
	return make([]{{if not $.Options.ByValue}}*{{end}}{{$entity.Name}}, 0, capacity)
}

// AppendToSlice is called by ObjectBox to fill the slice of the read objects
func ({{$entityNameCamel}}_EntityInfo) AppendToSlice(slice interface{}, object interface{}) interface{} {
	return append(slice.([]{{if not $.Options.ByValue}}*{{end}}{{$entity.Name}}), {{if $.Options.ByValue}}*{{end}}object.(*{{$entity.Name}}))
}

// Box provides CRUD access to {{$entity.Name}} objects
type {{$entity.Name}}Box struct {
	*objectbox.Box
}

// BoxFor{{$entity.Name}} opens a box of {{$entity.Name}} objects 
func BoxFor{{$entity.Name}}(ob *objectbox.ObjectBox) *{{$entity.Name}}Box {
	return &{{$entity.Name}}Box{
		Box: ob.InternalBox({{$entity.Id}}),
	}
}

// Put synchronously inserts/updates a single object.
// In case the {{$entity.IdProperty.Path}} is not specified, it would be assigned automatically (auto-increment).
// When inserting, the {{$entity.Name}}.{{$entity.IdProperty.Path}} property on the passed object will be assigned the new ID as well.
func (box *{{$entity.Name}}Box) Put(object *{{$entity.Name}}) (uint64, error) {
	return box.Box.Put(object)
}

// PutAsync asynchronously inserts/updates a single object.
// When inserting, the {{$entity.Name}}.{{$entity.IdProperty.Path}} property on the passed object will be assigned the new ID as well.
// 
// It's executed on a separate internal thread for better performance.
//
// There are two main use cases:
//
// 1) "Put & Forget:" you gain faster puts as you don't have to wait for the transaction to finish.
//
// 2) Many small transactions: if your write load is typically a lot of individual puts that happen in parallel,
// this will merge small transactions into bigger ones. This results in a significant gain in overall throughput.
//
//
// In situations with (extremely) high async load, this method may be throttled (~1ms) or delayed (<1s).
// In the unlikely event that the object could not be enqueued after delaying, an error will be returned.
//
// Note that this method does not give you hard durability guarantees like the synchronous Put provides.
// There is a small time window (typically 3 ms) in which the data may not have been committed durably yet.
func (box *{{$entity.Name}}Box) PutAsync(object *{{$entity.Name}}) (uint64, error) {
	return box.Box.PutAsync(object)
}

// PutAll inserts multiple objects in single transaction.
// In case {{$entity.IdProperty.Path}}s are not set on the objects, they would be assigned automatically (auto-increment).
// 
// Returns: IDs of the put objects (in the same order).
// When inserting, the {{$entity.Name}}.{{$entity.IdProperty.Path}} property on the objects in the slice will be assigned the new IDs as well.
//
// Note: In case an error occurs during the transaction, some of the objects may already have the {{$entity.Name}}.{{$entity.IdProperty.Path}} assigned    
// even though the transaction has been rolled back and the objects are not stored under those IDs.
//
// Note: The slice may be empty or even nil; in both cases, an empty IDs slice and no error is returned.
func (box *{{$entity.Name}}Box) PutAll(objects []{{if not $.Options.ByValue}}*{{end}}{{$entity.Name}}) ([]uint64, error) {
	return box.Box.PutAll(objects)
}

// Get reads a single object.
//
// Returns nil (and no error) in case the object with the given ID doesn't exist.
func (box *{{$entity.Name}}Box) Get(id uint64) (*{{$entity.Name}}, error) {
	object, err := box.Box.Get(id)
	if err != nil {
		return nil, err
	} else if object == nil {
		return nil, nil
	}
	return object.(*{{$entity.Name}}), nil
}

// Get reads all stored objects
func (box *{{$entity.Name}}Box) GetAll() ([]{{if not $.Options.ByValue}}*{{end}}{{$entity.Name}}, error) {
	objects, err := box.Box.GetAll()
	if err != nil {
		return nil, err
	}
	return objects.([]{{if not $.Options.ByValue}}*{{end}}{{$entity.Name}}), nil
}

// Remove deletes a single object
func (box *{{$entity.Name}}Box) Remove(object *{{$entity.Name}}) (err error) {
	return box.Box.Remove({{if $entity.IdProperty.Converter}}{{$entity.IdProperty.Converter}}ToDatabaseValue({{end -}}
					object.{{$entity.IdProperty.Path}}{{if $entity.IdProperty.Converter}}){{end}})
}

// Creates a query with the given conditions. Use the fields of the {{$entity.Name}}_ struct to create conditions.
// Keep the *{{$entity.Name}}Query if you intend to execute the query multiple times.
// Note: this function panics if you try to create illegal queries; e.g. use properties of an alien type.
// This is typically a programming error. Use QueryOrError instead if you want the explicit error check.
func (box *{{$entity.Name}}Box) Query(conditions ...objectbox.Condition) *{{$entity.Name}}Query {
	return &{{$entity.Name}}Query{
		box.Box.Query(conditions...),
	}
}

// Creates a query with the given conditions. Use the fields of the {{$entity.Name}}_ struct to create conditions.
// Keep the *{{$entity.Name}}Query if you intend to execute the query multiple times.
func (box *{{$entity.Name}}Box) QueryOrError(conditions ...objectbox.Condition) (*{{$entity.Name}}Query, error) {
	if query, err := box.Box.QueryOrError(conditions...); err != nil {
		return nil, err
	} else {
		return &{{$entity.Name}}Query{query}, nil
	}
}

// Query provides a way to search stored objects
//
// For example, you can find all {{$entity.Name}} which {{$entity.IdProperty.Name}} is either 42 or 47:
// 		box.Query({{$entity.Name}}_.{{$entity.IdProperty.Name}}.In(42, 47)).Find()
type {{$entity.Name}}Query struct {
	*objectbox.Query
}

// Find returns all objects matching the query
func (query *{{$entity.Name}}Query) Find() ([]{{if not $.Options.ByValue}}*{{end}}{{$entity.Name}}, error) {
	objects, err := query.Query.Find()
	if err != nil {
		return nil, err
	}
	return objects.([]{{if not $.Options.ByValue}}*{{end}}{{$entity.Name}}), nil
}

// Offset defines the index of the first object to process (how many objects to skip)
func (query *{{$entity.Name}}Query) Offset(offset uint64) *{{$entity.Name}}Query {
	query.Query.Offset(offset)
	return query
}

// Limit sets the number of elements to process by the query
func (query *{{$entity.Name}}Query) Limit(limit uint64) *{{$entity.Name}}Query {
	query.Query.Limit(limit)
	return query
}
{{end -}}`))
View Source
var ModelTemplate = template.Must(template.New("model").Funcs(funcMap).Parse(
	`// Code generated by ObjectBox; DO NOT EDIT.

package {{.Model.Package}}

import (
	"github.com/objectbox/objectbox-go/objectbox"
)

// ObjectBoxModel declares and builds the model from all the entities in the package. 
// It is usually used when setting-up ObjectBox as an argument to the Builder.Model() function.
func ObjectBoxModel() *objectbox.Model {
	model := objectbox.NewModel()
	model.GeneratorVersion({{.GeneratorVersion}})

	{{range $entity := .Model.Entities -}}
	model.RegisterBinding({{$entity.Name}}Binding)
	{{end -}}
	model.LastEntityId({{.Model.LastEntityId.GetId}}, {{.Model.LastEntityId.GetUid}})
	{{if .Model.LastIndexId}}model.LastIndexId({{.Model.LastIndexId.GetId}}, {{.Model.LastIndexId.GetUid}}){{end}}
	{{if .Model.LastRelationId}}model.LastRelationId({{.Model.LastRelationId.GetId}}, {{.Model.LastRelationId.GetUid}}){{end}}

	return model
}`))

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL