Skip to content

Commit

Permalink
reflection.DefaultValuer
Browse files Browse the repository at this point in the history
  • Loading branch information
ungerik committed May 3, 2014
1 parent c5cbb4d commit 9f634b0
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 126 deletions.
File renamed without changes.
4 changes: 2 additions & 2 deletions doc.go
Expand Up @@ -84,7 +84,7 @@ Example of a dynamic view:
}
if i.Err() != nil {
return nil, i.Err()
}
}
return &List{ // List = higher level abstraction, UL() = shortcut
Class: "my-ol",
Ordered: true,
Expand Down Expand Up @@ -170,7 +170,7 @@ struct type:
Password2 model.Password `view:"label=Repeat password"`
}
func (self *SignupFormModel) Validate(metaData *model.MetaData) []*model.ValidationError {
func (self *SignupFormModel) Validate(metaData *model.MetaData) error {
if self.Password1 != self.Password2 {
return model.NewValidationErrors(os.NewError("Passwords don't match"), metaData)
}
Expand Down
2 changes: 1 addition & 1 deletion media/backend.go
Expand Up @@ -27,7 +27,7 @@ type Backend interface {
BlobIterator() model.Iterator

// CountBlobRefs counts all BlobRef occurrences with blobID
// in all known databases.
// in all known databases
CountBlobRefs(blobID string) (count int, err error)

// RemoveAllBlobRefs removes all BlobRef occurrences with blobID
Expand Down
94 changes: 94 additions & 0 deletions media/filesystem/backend.go
@@ -0,0 +1,94 @@
package filesystem

import (
"io"
"mime"
"os"
"path"

"github.com/ungerik/go-start/media"
"github.com/ungerik/go-start/model"
)

type Backend struct {
BaseDir string
}

func (backend *Backend) FileWriter(filename, contentType string) (writer io.WriteCloser, id string, err error) {
writer, err = os.Create(path.Join(backend.BaseDir, filename))
return writer, filename, err
}

// Returns ErrNotFound if no file with id is found.
func (backend *Backend) FileReader(id string) (reader io.ReadCloser, filename, contentType string, err error) {
reader, err = os.OpenFile(path.Join(backend.BaseDir, filename), os.O_RDONLY, 0660)
return reader, id, mime.TypeByExtension(id), err
}

// Returns ErrNotFound if no file with id is found.
func (backend *Backend) DeleteFile(id string) error {
return os.Remove(path.Join(backend.BaseDir, id))
}

func (backend *Backend) LoadBlob(id string) (*media.Blob, error) {
panic("not implemented")
}

func (backend *Backend) SaveBlob(blob *media.Blob) error {
panic("not implemented")
}

// DeleteBlob does not delete the file associated with it, also use DeleteFile().
func (backend *Backend) DeleteBlob(blob *media.Blob) error {
panic("not implemented")
}

// BlobIterator returns an iterator that iterates
// all blobs as Blob structs.
func (backend *Backend) BlobIterator() model.Iterator {
panic("not implemented")
}

// CountBlobRefs counts all BlobRef occurrences with blobID
// in all known databases.
func (backend *Backend) CountBlobRefs(blobID string) (count int, err error) {
panic("not implemented")
}

// RemoveAllBlobRefs removes all BlobRef occurrences with blobID
// in all known databases.
func (backend *Backend) RemoveAllBlobRefs(blobID string) (count int, err error) {
panic("not implemented")
}

// Returns ErrNotFound if no image with id is found.
func (backend *Backend) LoadImage(id string) (*media.Image, error) {
panic("not implemented")
}

// SaveImage saves image and updates its ID if it is empty.
func (backend *Backend) SaveImage(image *media.Image) error {
panic("not implemented")
}

func (backend *Backend) DeleteImage(image *media.Image) error {
panic("not implemented")
}

// ImageIterator returns an iterator that iterates
// all images as Image structs.
func (backend *Backend) ImageIterator() model.Iterator {
panic("not implemented")
}

// CountImageRefs counts all ImageRef occurrences with imageID
// in all known databases.
func (backend *Backend) CountImageRefs(imageID string) (count int, err error) {
panic("not implemented")
}

// RemoveAllImageRefs removes all ImageRef occurrences with imageID
// in all known databases.
func (backend *Backend) RemoveAllImageRefs(imageID string) (count int, err error) {
panic("not implemented")
}
10 changes: 5 additions & 5 deletions model/dynamicchoice.go
Expand Up @@ -17,14 +17,14 @@ type DynamicChoice struct {
// Implements reflection.DontVisitStruct
func (DynamicChoice) DontVisitStruct() {}

// Implements reflection.FakeZero
func (self *DynamicChoice) IsZero() bool {
// Implements reflection.DefaultValue
func (self *DynamicChoice) IsDefault() bool {
return self.index == 0
}

// Implements reflection.FakeZero
func (self *DynamicChoice) ZeroValue() interface{} {
return DynamicChoice{index: 0, options: self.options}
// Implements reflection.DefaultValue
func (self *DynamicChoice) GetDefault() interface{} {
return DynamicChoice{options: self.options}
}

func (self *DynamicChoice) Index() int {
Expand Down
4 changes: 2 additions & 2 deletions model/functions.go
@@ -1,7 +1,7 @@
package model

import (
"github.com/ungerik/go-start/utils"
"github.com/ungerik/go-start/reflection"
)

// CopyFields copies matching struct/array/slice fields
Expand Down Expand Up @@ -72,7 +72,7 @@ func SetAllSliceLengths(document interface{}, length int) {
Visit(document, VisitorFunc(
func(data *MetaData) error {
if data.Kind == SliceKind && data.Value.Len() != length {
data.Value.Set(utils.SetSliceLengh(data.Value, length))
data.Value.Set(reflection.SetSliceLengh(data.Value, length))
}
return nil
},
Expand Down
93 changes: 93 additions & 0 deletions reflection/defaultvaluer.go
@@ -0,0 +1,93 @@
package reflection

import (
"reflect"
)

type DefaultValuer interface {
IsDefault() bool
GetDefault() interface{}
}

var DefaultValuerType = reflect.TypeOf((*DefaultValuer)(nil)).Elem()

func SmartCastDefaultValuer(v reflect.Value) (DefaultValuer, bool) {
// Having a pointer to a type is the most common case for methods
if v.CanAddr() && v.Addr().Type().Implements(DefaultValuerType) {
return v.Addr().Interface().(DefaultValuer), true
}
// Less common is not using a pointer but the type by value for methods
if v.Type().Implements(DefaultValuerType) {
return v.Interface().(DefaultValuer), true
}
// Or the type implements the methods by value but we have a pointer to it
if v.Kind() == reflect.Ptr && v.Elem().Type().Implements(DefaultValuerType) {
return v.Elem().Interface().(DefaultValuer), true
}
return nil, false
}

// IsDefaultValue calls SmartCastDefaultValuer to tedect if v in any kind implements
// DefaultValuer. If this is the case, then DefaultValuer.IsDefault will be returned.
// If v does not implement DefaultValuer, then the result of a comparison with the
// zero value of the type will be returned.
func IsDefaultValue(v reflect.Value) bool {
if defaultValuer, ok := SmartCastDefaultValuer(v); ok {
return defaultValuer.IsDefault()
}

switch v.Kind() {
case reflect.String:
return v.Len() == 0

case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0

case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint() == 0

case reflect.Float32, reflect.Float64:
return v.Float() == 0

case reflect.Complex64, reflect.Complex128:
return v.Complex() == complex(0, 0)

case reflect.Bool:
return v.Bool() == false

case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Slice, reflect.Map:
return v.IsNil()

case reflect.Struct:
// Todo own deep is default
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}

panic("never reached")
}

// IsDefault return true if value is nil,
// or the result of IsDefaultValue for the reflect.Value of value.
func IsDefault(value interface{}) bool {
if value == nil {
return true
}
return IsDefaultValue(reflect.ValueOf(value))
}

// GetDefaultValue returns the result of the GetDefault() method
// if v or v pointer implements DefaultValuer.
// Else the zero value of v.Type() will be returned.
// Used to clone types that have internal state that represents
// a default value that is not deep zero.
func GetDefaultValue(v reflect.Value) reflect.Value {
if defaultValuer, ok := v.Interface().(DefaultValuer); ok {
return reflect.ValueOf(defaultValuer.GetDefault())
}
if v.CanAddr() {
if defaultValuer, ok := v.Addr().Interface().(DefaultValuer); ok {
return reflect.ValueOf(defaultValuer.GetDefault())
}
}
return reflect.Zero(v.Type())
}
6 changes: 0 additions & 6 deletions reflection/fakezero.go

This file was deleted.

31 changes: 0 additions & 31 deletions reflection/functions.go
Expand Up @@ -172,37 +172,6 @@ func NewInstance(prototype interface{}) interface{} {
// return results[0], nil
// }

func IsDefaultValue(value interface{}) bool {
if value == nil {
return true
}

switch v := reflect.ValueOf(value); v.Kind() {
case reflect.String:
return v.Len() == 0

case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0

case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint() == 0

case reflect.Float32, reflect.Float64:
return v.Float() == 0

case reflect.Bool:
return v.Bool() == false

case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Slice, reflect.Map:
return v.IsNil()

case reflect.Struct:
return reflect.DeepEqual(value, reflect.Zero(v.Type()).Interface())
}

panic(fmt.Errorf("Unknown value kind %T", value))
}

// IsNilOrWrappedNil returns if i is nil, or wraps a nil pointer
// in a non nil interface.
func IsNilOrWrappedNil(i interface{}) bool {
Expand Down
63 changes: 63 additions & 0 deletions reflection/slices.go
@@ -0,0 +1,63 @@
package reflection

import (
"fmt"
"reflect"
)

// AppendDefaultSliceElement appends a field to slice with the
// value returned by GetDefaultValue for the former last slice
// element, or the zero value of the type if the slice was empty.
func AppendDefaultSliceElement(slice reflect.Value) reflect.Value {
if slice.Kind() != reflect.Slice {
panic(fmt.Errorf("Expected slice type, got %T", slice.Interface()))
}
var newField reflect.Value
if slice.Len() > 0 {
newField = GetDefaultValue(slice.Index(slice.Len() - 1))
} else {
newField = reflect.Zero(slice.Type().Elem())
}
return reflect.Append(slice, newField)
}

// SetSliceLengh sets the length of a slice by sub-slicing a slice that's too long,
// or appending empty fields with the result of AppendDefaultSliceElement.
func SetSliceLengh(slice reflect.Value, length int) reflect.Value {
if length > slice.Len() {
for i := slice.Len(); i < length; i++ {
slice = AppendDefaultSliceElement(slice)
}
} else if length < slice.Len() {
slice = slice.Slice(0, length)
}
return slice
}

// DeleteDefaultSliceElementsVal deletes slice elements where IsDefaultValue
// returns true.
func DeleteDefaultSliceElementsVal(slice reflect.Value) reflect.Value {
if slice.Kind() != reflect.Slice {
panic(fmt.Errorf("Expected slice type, got %T", slice.Interface()))
}
for i := slice.Len() - 1; i >= 0; i-- {
if IsDefaultValue(slice.Index(i)) {
fmt.Println("Found default", i)
before := slice.Slice(0, i)
if i == slice.Len()-1 {
slice = before
} else {
after := slice.Slice(i+1, slice.Len())
slice = reflect.AppendSlice(before, after)
}
i--
}
}
return slice
}

// DeleteDefaultSliceElements deletes slice elements where IsDefaultValue
// returns true.
func DeleteDefaultSliceElements(slice interface{}) interface{} {
return DeleteDefaultSliceElementsVal(reflect.ValueOf(slice)).Interface()
}

0 comments on commit 9f634b0

Please sign in to comment.