Moved Vuex bindings 'mvue' to external repo

This commit is contained in:
MaMe82 2018-07-29 02:37:02 +02:00
parent 04c6f0d397
commit 286b8b9b46
6 changed files with 0 additions and 412 deletions

View File

@ -1,109 +0,0 @@
package mvuex
import (
"github.com/gopherjs/gopherjs/js"
"reflect"
"errors"
"strconv"
)
func wrapGoActionFunc(reflectedGoFunc reflect.Value ) (jsFunc *js.Object, err error) {
numGoArgs := reflectedGoFunc.Type().NumIn() //Number of arguments of the Go target method
if numGoArgs < 3 || numGoArgs > 4{
return nil, eWrongActionArgCount
}
// Check if first arg 0 is of type *Store
if arg := reflectedGoFunc.Type().In(0); arg.Kind() != reflect.Ptr || arg.Elem() != jsStoreType {
return nil, eWrongFirstActionArg
}
// Check if first arg 1 is of type *ActionContext
if arg := reflectedGoFunc.Type().In(1); arg.Kind() != reflect.Ptr || arg.Elem() != jsActioContextType {
return nil, eWrongSecondActionArg
}
//Check if the remaining args are pointers to structs with *js.Object as first field
for i := 2; i < numGoArgs; i++ {
if arg:=reflectedGoFunc.Type().In(i); arg.Kind() != reflect.Ptr || !checkIfJSStruct(arg.Elem()) {
return nil, errors.New("Arg at position " + strconv.Itoa(i) +" isn't a pointer to a struct with *js.Object in first field")
}
}
goCallArgTargetTypes := make([]reflect.Type, numGoArgs) //store handler parameter types in slice
for i := 0; i < reflectedGoFunc.Type().NumIn(); i++ { goCallArgTargetTypes[i] = reflectedGoFunc.Type().In(i) }
goCallArgsTargetValues := make([]reflect.Value,numGoArgs) //create a slice for the parameter values, used in the call to the Go function
jsFunc = js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
// this: points to the store
// arg0: points to a context representation of the store
// arg1: point to an optional argument for the action (undefined otherwise)
// argument passing to the handler
// goHandler(store *Store, context *ActionContext, state *{CustomStateType} [, callArg *{CustomArgType])
// --> the store is the root store of Vuex (the handler should use the context instead)
// --> the context is a representation of the store, dedicated for this action (async access)
// --> == context.State, but casted to the Go type presented by the handler function
// --> (optional) in case the handler function takes an additional argument, arg1 from the JS call will be casted to this
/*
println("Action this:", this)
println("Action args:", arguments)
//Globalize args (context) for investigation
js.Global.Set("actionargs", arguments)
*/
storeVal,err := castToType(goCallArgTargetTypes[0], this) //cast 'this' to type of first function arg (type = *Store)
if err != nil { panic("Error converting JavaScript provided 'this' for action function to *Store: " + err.Error()) }
goCallArgsTargetValues[0] = storeVal
contextVal,err := castToType(goCallArgTargetTypes[1], arguments[0]) //cast arg0 to type of second function arg (type = *ActionContext)
if err != nil { panic("Error converting JavaScript provided first argument for action function to *ActionContext: " + err.Error()) }
goCallArgsTargetValues[1] = contextVal
//extract state from context, in order to cast it to the provided type
jsStateObj := arguments[0].Get("state")
stateVal,err := castToType(goCallArgTargetTypes[2], jsStateObj) //cast 'context.state' to type of third function arg
if err != nil { panic("Error converting JavaScript provided context.state for action function to *" + goCallArgTargetTypes[2].Elem().Name() + ": " + err.Error()) }
goCallArgsTargetValues[2] = stateVal
//Check if handler receives 4th arg
if numGoArgs == 4 {
// check if argument 1 is provided by JS
if arguments[1] == js.Undefined {
panic("The action handler awaits an argument of type " + goCallArgTargetTypes[3].Name() + " but the dispatched action doesn't provide this parameter")
}
// try to convert to target type
actionParamVal,err := castToType(goCallArgTargetTypes[3], arguments[1])
if err != nil { panic("Error converting JavaScript provided optional parameter for action function to *" + goCallArgTargetTypes[3].Elem().Name() + ": " + err.Error()) }
goCallArgsTargetValues[3] = actionParamVal
}
// Call the go function and return the result
return reflectedGoFunc.Call(goCallArgsTargetValues)
})
return jsFunc, nil
}
func Action(name string, goFunc interface{}) StoreOption {
return func(c *StoreConfig) {
//println("Creating ACTION FUNC")
if c.Actions == js.Undefined { c.Actions = o() }
reflectedGoFunc := reflect.ValueOf(goFunc)
if reflectedGoFunc.Kind() != reflect.Func { //check if the provided interface is a go function
panic("Action " + name + " is not a func")
}
//try to convert the provided function to a JavaScript function usable as Mutation
jsFunc, err := wrapGoActionFunc(reflectedGoFunc)
if err != nil {panic("Error exposing the action function '"+ name + "' to JavaScript: " + err.Error())}
c.Actions.Set(name, jsFunc)
//c.Mutations.Set(name, makeMethod(name, false, reflectGoFunc.Type(), reflectGoFunc))
}
}

View File

@ -1,17 +0,0 @@
package mvuex
import "github.com/gopherjs/gopherjs/js"
// Actions use a context instead of the store itself: https://vuex.vuejs.org/guide/actions.html
type ActionContext struct { //Don't use Context as name to avoid conflicts
*js.Object
Getters *js.Object `js:"getters"`
Commit func(...interface{}) *js.Object `js:"commit"`
Dispatch func(...interface{}) *js.Object `js:"dispatch"`
State *js.Object `js:"state"`
RootGetters *js.Object `js:"rootGetters"`
RootState *js.Object `js:"rootState"`
}

View File

@ -1,117 +0,0 @@
package mvuex
//ToDo: check for Vuex in js.Global scope and panic if missing
import (
"github.com/gopherjs/gopherjs/js"
"reflect"
"errors"
)
var (
eTooFewMutationArgs = errors.New("Mutation function has too few arguments (min 2)")
eTooManyMutationArgs = errors.New("Mutation function has too many arguments (max 3)")
eWrongActionArgCount = errors.New("Wrong argument count! An action handler takes 3 or 4 args: actionHandler(store *Store, context *ActionContext, state *{CustomStateType} [, callArg *{CustomArgType])")
eTooFewMutationArgsOnCall = errors.New("Mutation function called with too few arguments from JavaScrip")
eWrongFirstMutationArg = errors.New("Mutation function has to have *Store as first argument type")
eWrongFirstActionArg = errors.New("Mutation function has to have *Store as first argument type")
eWrongSecondActionArg = errors.New("Mutation function has to have *ActionContext as second argument type")
eWrongSecondMutationArg = errors.New("The second argument of the mutation function has to be a pointer to a struct of the type used for state")
eFirstFieldIsNotPtrJsObject = errors.New("The first field of the struct has to be of type *js.Object")
jsObjectType = reflect.TypeOf(js.Object{})
jsStoreType = reflect.TypeOf(Store{})
jsActioContextType = reflect.TypeOf(ActionContext{})
)
func o() *js.Object { return js.Global.Get("Object").New() } //Helper to create *js.Object
func castToType(targetType reflect.Type, sourceVal *js.Object) (result reflect.Value, err error) {
switch kind := targetType.Kind(); kind {
case reflect.Int:
//try to convert sourceVal to int before generating reflect.Value
result = reflect.ValueOf(sourceVal.Int())
case reflect.Int8:
result = reflect.ValueOf(int8(sourceVal.Int64()))
case reflect.Int16:
result = reflect.ValueOf(int16(sourceVal.Int64()))
case reflect.Int32:
result = reflect.ValueOf(int32(sourceVal.Int64()))
case reflect.Int64:
result = reflect.ValueOf(sourceVal.Int64())
case reflect.Float64:
result = reflect.ValueOf(sourceVal.Float())
case reflect.Float32:
result = reflect.ValueOf(float32(sourceVal.Float()))
case reflect.Bool:
result = reflect.ValueOf(sourceVal.Bool())
case reflect.Uint:
result = reflect.ValueOf(uint(sourceVal.Uint64()))
case reflect.Uint64:
result = reflect.ValueOf(sourceVal.Uint64())
case reflect.Uint32:
result = reflect.ValueOf(uint32(sourceVal.Uint64()))
case reflect.Uint16:
result = reflect.ValueOf(uint16(sourceVal.Uint64()))
case reflect.Uint8:
result = reflect.ValueOf(uint8(sourceVal.Uint64()))
case reflect.Uintptr:
result = reflect.ValueOf(sourceVal.Unsafe())
case reflect.String:
result = reflect.ValueOf(sourceVal.String())
case reflect.Struct:
//WE ASSUME THAT THE FIRST FIELD OF THE STRUCT IS OF TYPE *js.Object
//check if first field is *js.Object
if !checkIfJSStruct(targetType) {
return result, eFirstFieldIsNotPtrJsObject
}
//create a pointer to a new instance of this struct
pStructInstance := reflect.New(targetType)
//Assign the sourceValue to the first field of the struct, which is assume to be *js.Object
// fN := pStructInstance.Elem().Type().Name()
// println("Assigning to '" + fN + "': ", reflect.TypeOf(sourceVal).Elem().Name(), sourceVal)
pStructInstance.Elem().Field(0).Set(reflect.ValueOf(sourceVal))
result = pStructInstance.Elem()
case reflect.Ptr:
//follow pointer one level
derefType := targetType.Elem()
//recursive call
derefVal,err := castToType(derefType, sourceVal)
if err != nil { return result, err}
//println("dereferenced Value of type ", derefType.Kind().String(), ": ", derefVal)
//create a pointer to the dereferenced value after it has been created itself
result = derefVal.Addr()
case reflect.Interface:
result = reflect.ValueOf(sourceVal.Interface())
default:
// ToDo: func parsing
println("No conversion for following type implemented", kind.String() , " from ", sourceVal)
}
return result, nil
}
// checks if the obj given is a struct, with *js.Object type in first field
func checkIfJSStruct(objType reflect.Type) bool {
//check if Struct
if objType.Kind() != reflect.Struct { return false } //not a struct
// fetch first field
typeField0 := objType.Field(0).Type
//check if first field is pointer
if typeField0.Kind() != reflect.Ptr { return false } //not a pointer
// dereference ptr and check if equal to type js.Object
if typeField0.Elem() != jsObjectType { return false } // not pointing to js.Object
return true
}

View File

@ -1,102 +0,0 @@
package mvuex
import (
"reflect"
"github.com/gopherjs/gopherjs/js"
)
func wrapGoMutationFunc(reflectedGoFunc reflect.Value ) (jsFunc *js.Object, err error) {
// A mutationfunction is assumed to have this prototype
// func(
// store *Store, //first argument is of type store
// ptrToStateStruct struct{}, //ptr to a struct with same type as the struct provided as state
// additionalArgs ...interface{} //optional arguments for the mutation (Go types)
// )
numGoArgs := reflectedGoFunc.Type().NumIn() //Number of arguments of the Go target method
if numGoArgs < 2 {
return nil, eTooFewMutationArgs
}
if numGoArgs > 3 {
return nil, eTooManyMutationArgs
}
if goArg0 := reflectedGoFunc.Type().In(0); goArg0.Kind() != reflect.Ptr || goArg0.Elem() != jsStoreType {
return nil, eWrongFirstMutationArg
}
if goArg1 := reflectedGoFunc.Type().In(1); goArg1.Kind() != reflect.Ptr || goArg1.Elem().Kind() != reflect.Struct {
return nil, eWrongSecondMutationArg
}
// Here we know, the goFunc has at least two args, with first arg being *Store type
// and second arg being a custom data struct.
// The JavaScript call received, should provide two args at minimum:
// - arg 0: state data instance
// - arg 1..n: arguments handed in when the respective mutation function is called via commit, in case the
// mutation function is called without arguments, arg 1 is of type "undefined"
// Additionally, the "this" argument provides the store instance, which we hand to the Go function as first argument
// following two lines moved out of the inner function to avoid rerunning
goCallArgTargetTypes := make([]reflect.Type, numGoArgs)
goCallArgsTargetValues := make([]reflect.Value,numGoArgs) //create call args slice, containing the store arg
for i := 0; i < reflectedGoFunc.Type().NumIn(); i++ {
goCallArgTargetTypes[i] = reflectedGoFunc.Type().In(i)
}
jsFunc = js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
//check if js provides enough args
if len(arguments) < numGoArgs - 1 {
panic(eTooFewMutationArgsOnCall.Error())
}
//Note: All the logic in MakeFunc ends up in the final JS function and reruns every tim the function is triggered
storeVal,err := castToType(goCallArgTargetTypes[0], this) //cast 'this' to type of first function arg (type = *Store)
if err != nil { panic("Error converting JavaScript provided argument for mutation function to *Store: " + err.Error()) }
goCallArgsTargetValues[0] = storeVal
//add the remaining args
for idx,jsArg := range arguments {
//If the target function in Go has less arguments than we got provided from JavaScript, we gonna ignore the rest
targetIdx := idx+1 //offset by one, as we started with *Store as first arg for the Go function
if targetIdx >= numGoArgs { break }
//get method argument type at this poistion
goTargetArgT := goCallArgTargetTypes[targetIdx]
castedArg, err := castToType(goTargetArgT, jsArg)
if err != nil { panic("Error converting JS object to " + goTargetArgT.Kind().String()) }
goCallArgsTargetValues[targetIdx] = castedArg
}
results := reflectedGoFunc.Call(goCallArgsTargetValues)
return results
})
return jsFunc, nil
}
func Mutation(name string, goFunc interface{}) StoreOption {
return func(c *StoreConfig) {
//println("Creating MUTATION FUNC")
if c.Mutations == js.Undefined { c.Mutations = o() }
reflectedGoFunc := reflect.ValueOf(goFunc)
if reflectedGoFunc.Kind() != reflect.Func { //check if the provided interface is a go function
panic("Mutation " + name + " is not a func")
}
//try to convert the provided function to a JavaScript function usable as Mutation
jsFunc, err := wrapGoMutationFunc(reflectedGoFunc)
if err != nil {panic("Error exposing the mutation function '"+ name + "' to JavaScript: " + err.Error())}
c.Mutations.Set(name, jsFunc)
//c.Mutations.Set(name, makeMethod(name, false, reflectGoFunc.Type(), reflectGoFunc))
}
}

View File

@ -1,22 +0,0 @@
package mvuex
import (
"github.com/gopherjs/gopherjs/js"
"reflect"
)
func State(value interface{}) StoreOption {
// Check if value is struct with *js.Object in first field
if !checkIfJSStruct(reflect.TypeOf(value)) {
panic(eFirstFieldIsNotPtrJsObject)
}
return func(c *StoreConfig) {
if c.State != js.Undefined {
//if state has been defined before
panic("Cannot use mvuex.Sate more than once")
}
c.Object.Set("state", value)
}
}

View File

@ -1,45 +0,0 @@
package mvuex
import (
"github.com/gopherjs/gopherjs/js"
"reflect"
)
type StoreOption func(*StoreConfig)
type Store struct {
*js.Object
Getters *js.Object `js:"getters"`
Commit func(...interface{}) *js.Object `js:"commit"`
Dispatch func(...interface{}) *js.Object `js:"dispatch"`
Strict bool `js:"strict"`
}
// StoreConfig is the config object for NewStore.
type StoreConfig struct {
*js.Object
State *js.Object `js:"state"`
Mutations *js.Object `js:"mutations"`
Actions *js.Object `js:"actions"`
stateValue reflect.Value
}
// Option sets the options specified.
func (c *StoreConfig) Option(opts ...StoreOption) {
for _, opt := range opts {
opt(c)
}
}
func NewStore(opts ...StoreOption) *Store {
c := &StoreConfig{Object: o()}
c.Option(opts...)
store := &Store{Object: js.Global.Get("Vuex").Get("Store").New(c)}
return store
}