コード例 #1
0
ファイル: context.go プロジェクト: DaviWei/utils-1
/*
AfterTransaction will call f directly if self is not currently running a transaction,
otherwise it will be appended to a slice of funcs run after every transaction is finished.
*/
func (self *DefaultContext) AfterTransaction(f interface{}) (err error) {
	// validate that f take one argument of whatever type and returns an error
	if err = utils.ValidateFuncInput(f, []reflect.Type{
		reflect.TypeOf((*interface{})(nil)).Elem(),
	}); err != nil {
		return
	}
	if err = utils.ValidateFuncOutput(f, []reflect.Type{
		reflect.TypeOf((*error)(nil)).Elem(),
	}); err != nil {
		return
	}
	// validate that whatever argument f took, a GAEContext would implement it
	if !reflect.TypeOf((*GAEContext)(nil)).Elem().AssignableTo(reflect.TypeOf(f).In(0)) {
		err = errors.Errorf("%v does not take an argument that is satisfied by %v", f, self)
		return
	}
	// create our after func
	afterFunc := func(c GAEContext) (err error) {
		if errVal := reflect.ValueOf(f).Call([]reflect.Value{reflect.ValueOf(c)})[0]; !errVal.IsNil() {
			err = errVal.Interface().(error)
		}
		return
	}
	if self.inTransaction {
		self.afterTransaction = append(self.afterTransaction, afterFunc)
	} else {
		if err = afterFunc(self); err != nil {
			return
		}
	}
	return
}
コード例 #2
0
ファイル: context.go プロジェクト: DaviWei/utils-1
/*
CallTransactionFunction will validate that f takes something that implements a GAEContext,
and that it returns an error.
Then it will call f with c and return the error.
*/
func CallTransactionFunction(c GAEContext, f interface{}) (err error) {
	if err = utils.ValidateFuncInput(f, []reflect.Type{
		reflect.TypeOf((*GAEContext)(nil)).Elem(),
	}); err != nil {
		err = errors.Wrap(err, 1)
		return
	}
	if err = utils.ValidateFuncOutput(f, []reflect.Type{
		reflect.TypeOf((*error)(nil)).Elem(),
	}); err != nil {
		err = errors.Wrap(err, 1)
		return
	}
	if errVal := reflect.ValueOf(f).Call([]reflect.Value{reflect.ValueOf(c)})[0]; !errVal.IsNil() {
		return errVal.Interface().(error)
	}
	return nil
}
コード例 #3
0
ファイル: context.go プロジェクト: DaviWei/utils-1
/*
MarshalJSON will recursively run any found `BeforeMarshal` functions on the content with arg and a stack of container instances, and then json marshal it.

It will not recurse down further after a BeforeMarshal function has been found, but it will run all top level BeforeMarshal functions that it finds.
*/
func (self *DefaultJSONContext) MarshalJSON(c interface{}, body interface{}, arg interface{}) (result []byte, err error) {
	// declare a function that recursively will run itself
	var runRecursive func(reflect.Value, reflect.Value) error

	cVal := reflect.ValueOf(c)
	contextType := reflect.TypeOf((*JSONContextLogger)(nil)).Elem()
	// initialize an empty container stack
	stackType := reflect.TypeOf([]interface{}{})

	// implement the function
	runRecursive = func(val reflect.Value, stack reflect.Value) error {
		// push this instance to the stack
		stack = reflect.Append(stack, val)

		// Try run BeforeMarshal
		fun := val.MethodByName("BeforeMarshal")
		if fun.IsValid() && !utils.IsNil(val.Interface()) {
			// make sure we don't run BeforeMarshal on any other things at the same time, at least in this context.
			return self.marshalSyncLock.Sync(val.Interface(), func() (err error) {
				// Validate BeforeMarshal takes something that implements JSONContextLogger
				if err = utils.ValidateFuncInput(fun.Interface(), []reflect.Type{contextType, stackType}); err != nil {
					if err = utils.ValidateFuncInput(fun.Interface(), []reflect.Type{contextType, stackType, reflect.TypeOf(arg)}); err != nil {
						return fmt.Errorf("BeforeMarshal needs to take an JSONContextLogger")
					}
				}

				// Validate BeforeMarshal returns an error
				if err = utils.ValidateFuncOutput(fun.Interface(), []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}); err != nil {
					return fmt.Errorf("BeforeMarshal needs to return an error")
				}

				args := []reflect.Value{cVal, stack}
				if fun.Type().NumIn() == 3 {
					args = append(args, reflect.ValueOf(arg))
				}
				timer := time.Now()

				// run the actual BeforeMarshal
				res := fun.Call(args)

				if time.Now().Sub(timer) > (500 * time.Millisecond) {
					self.Infof("BeforeMarshal for %s is slow, took: %v", val.Type(), time.Now().Sub(timer))
				}

				if !res[0].IsNil() {
					err = res[0].Interface().(error)
				}
				return
			})
		}

		// Try do recursion on these types, if we didn't find a BeforeMarshal func on the val itself
		switch val.Kind() {
		case reflect.Ptr, reflect.Interface:
			if val.IsNil() {
				return nil
			}
			return runRecursive(val.Elem(), stack)

		case reflect.Slice:
			for i := 0; i < val.Len(); i++ {
				if err := runRecursive(val.Index(i).Addr(), stack); err != nil {
					return err
				}
			}
			break

		case reflect.Struct:
			for i := 0; i < val.NumField(); i++ {
				if val.Type().Field(i).PkgPath == "" {
					if err := runRecursive(val.Field(i), stack); err != nil {
						return err
					}
				}
			}
			break
		}
		return nil
	}

	// Run recursive reflection on self.Body that executes BeforeMarshal on every object possible.
	stack := []interface{}{}
	if err = runRecursive(reflect.ValueOf(body), reflect.ValueOf(stack)); err != nil {
		return
	}

	// This makes sure that replies that returns a slice that is empty returns a '[]' instad of 'null'
	bodyVal := reflect.ValueOf(body)
	if bodyVal.Kind() == reflect.Slice && bodyVal.IsNil() {
		reflect.ValueOf(&body).Elem().Set(reflect.MakeSlice(bodyVal.Type(), 0, 0))
	}

	if result, err = json.MarshalIndent(body, "", "  "); err != nil {
		return
	}

	return
}