// Run generates model templates. func (c *generateModelCommand) Run(args []string) error { if len(args) < 1 || args[0] == "" { return fmt.Errorf("no NAME given") } name := args[0] if c.option.ORM == "" { c.option.ORM = defaultORM } mt, exists := modelTypeMap[c.option.ORM] if !exists { return fmt.Errorf("unsupported ORM: `%v'", c.option.ORM) } m := mt.FieldTypeMap() var fields []modelField for _, arg := range args[1:] { input := strings.Split(arg, ":") if len(input) != 2 { return fmt.Errorf("invalid argument format is specified: `%v'", arg) } name, t := input[0], input[1] if name == "" { return fmt.Errorf("field name isn't specified: `%v'", arg) } if t == "" { return fmt.Errorf("field type isn't specified: `%v'", arg) } ft, found := m[t] if !found { return fmt.Errorf("unsupported field type: `%v'", t) } fields = append(fields, modelField{ Name: util.ToCamelCase(name), Type: ft.Name, Column: util.ToSnakeCase(name), OptionTags: ft.OptionTags, }) } camelCaseName := util.ToCamelCase(name) snakeCaseName := util.ToSnakeCase(name) data := map[string]interface{}{ "Name": camelCaseName, "Fields": fields, } templatePath, configTemplatePath := mt.TemplatePath() if err := util.CopyTemplate(templatePath, filepath.Join("app", "model", snakeCaseName+".go"), data); err != nil { return err } initPath := filepath.Join("db", "config.go") if _, err := os.Stat(initPath); os.IsNotExist(err) { if err := util.CopyTemplate(configTemplatePath, initPath, nil); err != nil { return err } } return nil }
// Run generates the controller templates. func (c *generateControllerCommand) Run(args []string) error { if len(args) < 1 || args[0] == "" { return fmt.Errorf("no NAME given") } name := args[0] camelCaseName := util.ToCamelCase(name) snakeCaseName := util.ToSnakeCase(name) receiverName := strings.ToLower(name) if len(receiverName) > 1 { receiverName = receiverName[:2] } else { receiverName = receiverName[:1] } data := map[string]interface{}{ "Name": camelCaseName, "Receiver": receiverName, } if err := util.CopyTemplate( filepath.Join(skeletonDir("controller"), "controller.go"+util.TemplateSuffix), filepath.Join("app", "controller", snakeCaseName+".go"), data); err != nil { return err } if err := util.CopyTemplate( filepath.Join(skeletonDir("controller"), "view.html"+util.TemplateSuffix), filepath.Join("app", "view", snakeCaseName+".html"), data); err != nil { return err } return addRouteToFile(name) }
func addRouteToFile(name string) error { routeFilePath := filepath.Join("config", "routes.go") fset := token.NewFileSet() f, err := parser.ParseFile(fset, routeFilePath, nil, 0) if err != nil { return fmt.Errorf("failed to read file: %v", err) } routeStructName := util.ToCamelCase(name) routeName := util.ToSnakeCase(name) routeTableAST, err := findRouteTableAST(f) if err != nil { return err } if routeTableAST == nil { return nil } routeASTs := findRouteASTs(routeTableAST) if routeASTs == nil { return nil } if isRouteDefined(routeASTs, routeStructName) { return nil } routeFile, err := os.OpenFile(routeFilePath, os.O_RDWR, 0644) if err != nil { return fmt.Errorf("failed to open file: %v", err) } defer routeFile.Close() lastRouteAST := routeASTs[len(routeASTs)-1] offset := int64(fset.Position(lastRouteAST.End()).Offset) var buf bytes.Buffer if _, err := io.CopyN(&buf, routeFile, offset); err != nil { return fmt.Errorf("failed to read file: %v", err) } buf.WriteString(fmt.Sprintf(`, { Name: "%s", Path: "/%s", Controller: &controller.%s{}, }`, routeName, routeName, routeStructName)) if _, err := io.Copy(&buf, routeFile); err != nil { return fmt.Errorf("failed to read file: %v", err) } formatted, err := format.Source(buf.Bytes()) if err != nil { return fmt.Errorf("failed to format file: %v", err) } if _, err := routeFile.WriteAt(formatted, 0); err != nil { return fmt.Errorf("failed to update file: %v", err) } return nil }
// Run generates unit skeleton files. func (c *generateUnitCommand) Run(args []string) error { if len(args) < 1 || args[0] == "" { return fmt.Errorf("no NAME given") } name := args[0] camelCaseName := util.ToCamelCase(name) snakeCaseName := util.ToSnakeCase(name) data := map[string]interface{}{ "Name": camelCaseName, } if err := util.CopyTemplate( filepath.Join(skeletonDir("unit"), "unit.go"+util.TemplateSuffix), filepath.Join("app", "unit", snakeCaseName+".go"), data); err != nil { return err } return nil }
// Run generates migration templates. func (c *generateMigrationCommand) Run(args []string) error { if len(args) < 1 || args[0] == "" { return fmt.Errorf("no NAME given") } name := args[0] if c.option.ORM == "" { c.option.ORM = defaultORM } orm, exists := ORM[c.option.ORM] if !exists { return fmt.Errorf("unsupported ORM: `%v'", c.option.ORM) } now := _time.Now().Format("20060102150405") data := map[string]interface{}{ "Name": util.ToCamelCase(name), "TimeStamp": now, "ImportPath": orm.ImportPath(), "TxType": reflect.TypeOf(orm.TransactionType()).String(), } if err := util.CopyTemplate( filepath.Join(skeletonDir("migration"), "migration.go"+util.TemplateSuffix), filepath.Join("db", "migration", fmt.Sprintf("%v_%v.go", now, util.ToSnakeCase(name))), data, ); err != nil { return err } initPath := filepath.Join("db", "migration", "init.go") if _, err := os.Stat(initPath); os.IsNotExist(err) { appDir, err := util.FindAppDir() if err != nil { return err } if err := util.CopyTemplate( filepath.Join(skeletonDir("migration"), "init.go"+util.TemplateSuffix), initPath, map[string]interface{}{ "typeName": c.option.ORM, "tx": strings.TrimSpace(util.GoString(orm)), "dbImportPath": path.Join(appDir, "db"), }, ); err != nil { return err } } return nil }
func (params *Params) findFieldIndex(rtype reflect.Type, name string, index []int) []int { var embeddedFieldInfos []*embeddefFieldInfo for i := 0; i < rtype.NumField(); i++ { field := rtype.Field(i) if util.IsUnexportedField(field) { continue } if field.Anonymous { embeddedFieldInfos = append(embeddedFieldInfos, &embeddefFieldInfo{field, name, append(index, i)}) continue } if field.Name == util.ToCamelCase(name) { return append(index, i) } } for _, fi := range embeddedFieldInfos { if index := params.findFieldIndex(fi.field.Type, fi.name, fi.index); len(index) > 0 { return index } } return nil }
// Bind binds form values of fieldNames to obj. // obj must be a pointer of struct. If obj isn't a pointer of struct, it returns error. // Note that it in the case of errors due to a form value binding error, no error is returned. // Binding errors will set to map of returned from Controller.Errors(). func (params *Params) Bind(obj interface{}, fieldNames ...string) error { rvalue := reflect.ValueOf(obj) if rvalue.Kind() != reflect.Ptr { return fmt.Errorf("kocha: Bind: first argument must be a pointer, but %v", rvalue.Type().Kind()) } for rvalue.Kind() == reflect.Ptr { rvalue = rvalue.Elem() } if rvalue.Kind() != reflect.Struct { return fmt.Errorf("kocha: Bind: first argument must be a pointer of struct, but %T", obj) } rtype := rvalue.Type() for _, name := range fieldNames { index := params.findFieldIndex(rtype, name, nil) if len(index) < 1 { _, filename, line, _ := runtime.Caller(1) params.c.App.Logger.Warnf( "kocha: Bind: %s:%s: field name `%s' given, but %s.%s is undefined", filepath.Base(filename), line, name, rtype.Name(), util.ToCamelCase(name)) continue } fname := params.prefixedName(params.prefix, name) values, found := params.Values[fname] if !found { continue } field := rvalue.FieldByIndex(index) for field.Kind() == reflect.Ptr { field = field.Elem() } value, err := params.parse(field.Interface(), values[0]) if err != nil { params.c.Errors[name] = append(params.c.Errors[name], NewParamError(name, err)) } field.Set(reflect.ValueOf(value)) } return nil }