func singularize(plural string) string { singular := inflector.Singularize(plural) if singular == plural { singular += "Item" } return singular }
func generateWhere(t *models.Table, adapter string) (string, error) { placeholder := getPlaceholder(adapter) title := strings.Title(t.Name) firstChar := strings.ToLower(string(t.Name[0])) funcPlaceholderText := FormatFunction(title, "Where", "column string, arg interface{}", fmt.Sprintf("([]*%s, error)", title)) queryText := `"select * from %s where %s = %s;"` if placeholder == "?" { queryText = fmt.Sprintf(queryText, t.Name, placeholder, placeholder) } else { queryText = fmt.Sprintf(queryText, t.Name, placeholder, "$2") } typeName := inflector.Singularize(strings.Title(t.Name)) srcText := fmt.Sprintf(` %s := make([]*%s, 0) rows, err := db.Queryx(%s, column, arg) if err != nil { return "", err } for rows.Next() { %s := &%s{} err = rows.StructScan(%s) if err != nil { return nil, err } %s = append(%s, %s) } return %s, nil`, firstChar+"s", typeName, queryText, firstChar, typeName, firstChar, firstChar+"s", firstChar+"s", firstChar, firstChar+"s") functionText := fmt.Sprintf(funcPlaceholderText, srcText) return functionText, nil }
// PgLoadTables loads the table definitions from the database, writing the // resulting templates to typeMap and returning the created templates.TableTemplates. func PgLoadTables(args *internal.ArgType, db *sql.DB, typeMap map[string]*bytes.Buffer) (map[string]*templates.TableTemplate, error) { var err error // load columns cols, err := models.ColumnsByRelkindSchema(db, "r", args.Schema) if err != nil { return nil, err } // process columns fieldMap := map[string]map[string]bool{} tableMap := map[string]*templates.TableTemplate{} for _, c := range cols { if _, ok := fieldMap[c.TableName]; !ok { fieldMap[c.TableName] = map[string]bool{} } // set col info c.Field = snaker.SnakeToCamel(c.ColumnName) c.Len, c.NilType, c.Type = PgParseType(args, c.DataType, c.IsNullable) // set value in table map if not present if _, ok := tableMap[c.TableName]; !ok { tableMap[c.TableName] = &templates.TableTemplate{ Type: inflector.Singularize(snaker.SnakeToCamel(c.TableName)), TableSchema: args.Schema, TableName: c.TableName, Fields: []*models.Column{}, } } // set primary key if c.IsPrimaryKey { tableMap[c.TableName].PrimaryKey = c.ColumnName tableMap[c.TableName].PrimaryKeyField = c.Field tableMap[c.TableName].PrimaryKeyType = c.Type } // append col to template fields if _, ok := fieldMap[c.TableName][c.ColumnName]; !ok { tableMap[c.TableName].Fields = append(tableMap[c.TableName].Fields, c) } fieldMap[c.TableName][c.ColumnName] = true } // generate table templates for _, t := range tableMap { buf := GetBuf(typeMap, strings.ToLower(t.Type)) err = templates.Tpls["postgres.model.go.tpl"].Execute(buf, t) if err != nil { return nil, err } } return tableMap, nil }
func main() { for _, s := range singulars { fmt.Printf("Plural of %v = %v\n", s, inflector.Pluralize(s)) } fmt.Println() for _, s := range plurals { fmt.Printf("Singular of %v = %v\n", s, inflector.Singularize(s)) } }
// PgLoadEnums reads the enums from the database, writing the values to the // typeMap and returning the created EnumTemplates. func PgLoadEnums(args *internal.ArgType, db *sql.DB, typeMap map[string]*bytes.Buffer) (map[string]*templates.EnumTemplate, error) { var err error // load enums enums, err := models.EnumsBySchema(db, args.Schema) if err != nil { return nil, err } // process enums enumMap := map[string]*templates.EnumTemplate{} for _, e := range enums { // calc go type and add to known types goType := inflector.Singularize(snaker.SnakeToCamel(e.EnumType)) templates.KnownTypeMap[goType] = true // calculate val, chopping off redundant type name if applicable val := snaker.SnakeToCamel(strings.ToLower(e.EnumValue)) if strings.HasSuffix(strings.ToLower(val), strings.ToLower(goType)) { v := val[:len(val)-len(goType)] if len(v) > 0 { val = v } } // copy values back into model e.Value = val e.Type = goType // set value in enum map if not present typ := strings.ToLower(goType) if _, ok := enumMap[typ]; !ok { enumMap[typ] = &templates.EnumTemplate{ Type: goType, EnumType: e.EnumType, Values: []*models.Enum{}, } } // append enum to template vals enumMap[typ].Values = append(enumMap[typ].Values, e) } // generate enum templates for _, e := range enumMap { buf := GetBuf(typeMap, strings.ToLower(e.Type)) err = templates.Tpls["postgres.enum.go.tpl"].Execute(buf, e) if err != nil { return nil, err } } return enumMap, nil }
func generateWhereWithMap(t *models.Table, adapter string) (string, error) { placeholder := getPlaceholder(adapter) title := strings.Title(t.Name) firstChar := strings.ToLower(string(t.Name[0])) funcPlaceholderText := FormatFunction(title, "WhereWithMap", "args map[string]interface{}", fmt.Sprintf("([]*%s, error)", title)) typeName := inflector.Singularize(strings.Title(t.Name)) srcText := strings.Replace( fmt.Sprintf(` %s := make([]*%s, 0) placeholder := "%s" queryString := "select * from %s where " argsSlice := make([]interface{}, 0) curNum := 1 if placeholder == "$1" { for k, v := range args { if curNum / 2 <= len(args) { queryString += fmt.Sprintf("$#{d}\" = \"$#{d}\" and ", curNum, curNum +1) } else { queryString += fmt.Sprintf("$#{d}\" = \"$#{d}\";", curNum, curNum +1) } argsSlice = append(argsSlice, k, v) curNum +=2 } } else { for k, v := range args { if curNum / 2 <= len(args) { queryString += "? = ? and " } else { queryString += "? = ?;" } argsSlice = append(argsSlice, k, v) curNum +=2 } } rows, err := db.Queryx(queryString, args) if err != nil { return "", err } for rows.Next() { %s := &%s{} err = rows.StructScan(%s) if err != nil { return nil, err } %s = append(%s, %s) } return %s, nil`, firstChar+"s", typeName, placeholder, t.Name, firstChar, typeName, firstChar, firstChar+"s", firstChar+"s", firstChar, firstChar+"s"), "#{d}", "%d", -1) functionText := fmt.Sprintf(funcPlaceholderText, srcText) return functionText, nil }
func generateWherein(t *models.Table, adapter string) (string, error) { placeholder := getPlaceholder(adapter) title := strings.Title(t.Name) firstChar := strings.ToLower(string(t.Name[0])) funcPlaceholderText := FormatFunction(title, "WhereIn", "column string, args ...interface{}", fmt.Sprintf("([]*%s, error)", title)) typeName := inflector.Singularize(strings.Title(t.Name)) srcText := strings.Replace(fmt.Sprintf(` if len(args) == 0 { return nil, errors.New("Must provide at least one argument") } %s := make([]*%s, 0) queryString := "" placeholder := "%s" buffer := bytes.NewBuffer([]byte{}) if placeholder == "$1" { queryString = "select * from %s where $1 in (" for i, _ := range args { if i < len(args) { buffer.WriteString("$" + fmt.Sprintf("#{d},", i+2)) } else { buffer.WriteString("$" + fmt.Sprintf("#{d});", i+2)) } } } else { queryString = "select * from %s where ? in (" for i, _ := range args { if i < len(args) { buffer.WriteString("?,") } else { buffer.WriteString("?);") } } } queryString += buffer.String() rows, err := db.Queryx(queryString, args) if err != nil { return "", err } for rows.Next() { %s := &%s{} err = rows.StructScan(%s) if err != nil { return nil, err } %s = append(%s, %s) } return %s, nil`, firstChar+"s", typeName, placeholder, t.Name, t.Name, firstChar, title, firstChar, firstChar+"s", firstChar+"s", firstChar, firstChar+"s"), "#{d}", "%d", -1) functionText := fmt.Sprintf(funcPlaceholderText, srcText) return functionText, nil }
func GenerateModels(s *models.Schema) (map[string][]byte, error) { modelSrc := make(map[string][]byte) for _, table := range s.Tables { buffer := bytes.NewBuffer([]byte{}) structName := inflector.Singularize(strings.Title(table.Name)) buffer.WriteString(fmt.Sprintf("\n// %s is a generated struct from a %s database. See github.com/alistanis/stg for more information.", structName, s.Adapter)) buffer.WriteString(fmt.Sprintf("\ntype %s struct {\n", structName)) for _, row := range table.Rows { buffer.WriteString(fmt.Sprintf(" %s", strings.Title(row.Field))) t := row.Type switch { case strings.Contains(t, "int"), strings.Contains(t, "numeric"): buffer.WriteString(fmt.Sprintf(" int `db:\"%s\"` \n", row.Field)) case strings.Contains(t, "varchar"), strings.Contains(t, "text"), strings.Contains(t, "char"): buffer.WriteString(fmt.Sprintf(" string `db:\"%s\"` \n", row.Field)) case strings.Contains(t, "real"), strings.Contains(t, "float"): buffer.WriteString(fmt.Sprintf(" float64 `db:\"%s\"` \n", row.Field)) case strings.Contains(t, "bool"): buffer.WriteString(fmt.Sprintf(" bool `db:\"%s\"` \n", row.Field)) case strings.Contains(t, "enum"): buffer.WriteString(fmt.Sprintf(" interface{} `db:\"%s\"` \n", row.Field)) case strings.Contains(t, "blob"), strings.Contains(t, "binary"): buffer.WriteString(fmt.Sprintf(" []byte `db:\"%s\"` \n", row.Field)) default: return nil, errors.New("Unsupported type") } } buffer.WriteString("\n}\n") buffer.WriteString(fmt.Sprintf(`var ( %s = &%s{} ) `, inflector.Pluralize(strings.Title(table.Name)), strings.Title(table.Name))) methods, err := GenerateMethods(table, s.Adapter) if err != nil { return nil, err } for _, m := range methods { buffer.WriteString(m) } src, err := format.Source(buffer.Bytes()) if err != nil { return nil, err } buffer.Reset() modelSrc[strings.ToLower(table.Name)] = src } return modelSrc, nil }
func structString(schemas []ColumnSchema, table string) string { idColumnSchemas := primaryKey(schemas, table) idColumnSchema := idColumnSchemas[0] // debug idColumnType := idColumnSchema.GoType out := "type " + formatName(table) + " struct{\n" linkMap := make(map[string]int) for i, cs := range schemas { if cs.TableName == table { // fmt.Println(cs) if cs.ColumForeignColumn == "" { out = out + "\t" + formatName(cs.ColumnName) + " " + cs.GoType if len(config.TagLabel) > 0 { out = out + "\t`" + config.TagLabel + ":\"" + cs.ColumnName + "\" json:\"" + formatNameLower(cs.ColumnName) + "\"`" } out = out + "\n" } else { // fmt.Println(cs, ">"+cs.ColumForeignColumn+"<") // out = out + "\t" + formatName(cs.ColumnName) + " >>>> " + cs.GoType linkMap[cs.ColumnForeignTable] = i } } } out += "\tLinks " + formatName(table) + "Links `json:\"_links\" db:-`\n" out = out + "}\n\n" out += "type " + formatName(table) + "Links struct {\n" out += "\tSelf string `db:\"self\" json:\"_self\"`\n" for fk, _ := range linkMap { typeName := formatName(fk) if strings.HasSuffix(typeName, "Cachegroup") { typeName = "Cachegroup" } out += "\t\t" + formatName(fk) + "Link " + typeName + "Link `json:\"" + fk + "\" db:-`\n" } out += "} \n\n" for index, cs := range schemas { if cs.ColumnForeignTable == table { out += "type " + formatName(table) + "Link struct { \n" out += "\tID " + idColumnType + " `db:\"" + inflector.Singularize(table) + "\" json:\"" + schemas[index].ColumForeignColumn + "\"`\n" out += "\tRef string `db:\"" + schemas[index].ColumnForeignTable + "_" + schemas[index].ColumForeignColumn + "_ref\" json:\"_ref\"`\n" out += "}\n\n" break } } return out }
func generateFind(t *models.Table, adapter string) (string, error) { placeholder := getPlaceholder(adapter) title := strings.Title(t.Name) firstChar := strings.ToLower(string(t.Name[0])) funcPlaceholderText := FormatFunction(title, "Find", "id int", fmt.Sprintf("(*%s, error)", title)) queryText := fmt.Sprintf(`"select * from %s where id = %s;"`, t.Name, placeholder) typeName := inflector.Singularize(strings.Title(t.Name)) srcText := fmt.Sprintf(` %s := &%s{} err := db.Select(%s, %s, id) if err != nil { return "", err } return %s, nil`, firstChar, typeName, firstChar, queryText, firstChar) functionText := fmt.Sprintf(funcPlaceholderText, srcText) return functionText, nil }
// LoadEnums loads schema enums. func (tl TypeLoader) LoadEnums(args *ArgType) (map[string]*Enum, error) { var err error // not supplied, so bail if tl.EnumList == nil { return nil, nil } // load enums enumList, err := tl.EnumList(args.DB, args.Schema) if err != nil { return nil, err } // process enums enumMap := map[string]*Enum{} for _, e := range enumList { enumTpl := &Enum{ Name: inflector.Singularize(SnakeToIdentifier(e.EnumName)), Schema: args.Schema, Values: []*EnumValue{}, Enum: e, ReverseConstNames: args.UseReversedEnumConstNames, } err = tl.LoadEnumValues(args, enumTpl) if err != nil { return nil, err } enumMap[enumTpl.Name] = enumTpl args.KnownTypeMap[enumTpl.Name] = true } // generate enum templates for _, e := range enumMap { err = args.ExecuteTemplate(EnumTemplate, e.Name, "", e) if err != nil { return nil, err } } return enumMap, nil }
func generateWhereFirst(t *models.Table, adapter string) (string, error) { placeholder := getPlaceholder(adapter) title := strings.Title(t.Name) firstChar := strings.ToLower(string(t.Name[0])) funcPlaceholderText := FormatFunction(title, "WhereFirst", "column string, arg interface{}", fmt.Sprintf("(*%s, error)", title)) queryText := `"select * from %s where %s = %s;"` if placeholder == "?" { queryText = fmt.Sprintf(queryText, t.Name, placeholder, placeholder) } else { queryText = fmt.Sprintf(queryText, t.Name, placeholder, "$2") } typeName := inflector.Singularize(strings.Title(t.Name)) srcText := fmt.Sprintf(` %s := &%s{} err := db.Select(%s, %s, column, arg) if err != nil { return "", err } return %s, nil`, firstChar, typeName, firstChar, queryText, firstChar) functionText := fmt.Sprintf(funcPlaceholderText, srcText) return functionText, nil }
// LoadRelkind loads a schema table/view definition. func (tl TypeLoader) LoadRelkind(args *ArgType, relType RelType) (map[string]*Type, error) { var err error // load tables tableList, err := tl.TableList(args.DB, args.Schema, tl.Relkind(relType)) if err != nil { return nil, err } // tables tableMap := make(map[string]*Type) for _, ti := range tableList { // create template typeTpl := &Type{ Name: inflector.Singularize(SnakeToIdentifier(ti.TableName)), Schema: args.Schema, RelType: relType, Fields: []*Field{}, Table: ti, } // process columns err = tl.LoadColumns(args, typeTpl) if err != nil { return nil, err } tableMap[ti.TableName] = typeTpl } // generate table templates for _, t := range tableMap { err = args.ExecuteTemplate(TypeTemplate, t.Name, "", t) if err != nil { return nil, err } } return tableMap, nil }
func handleString(schemas []ColumnSchema, table string) string { pk := primaryKey(schemas, table) updateLastUpdated := hasLastUpdated(schemas, table) out := "" out += "// @Title get" + formatName(table) + "ById\n" out += "// @Description retrieves the " + table + " information for a certain id\n" out += "// @Accept application/json\n" out += "// @Param id path int false \"The row id\"\n" out += "// @Success 200 {array} " + formatName(table) + "\n" out += "// @Resource /api/2.0\n" out += "// @Router /api/2.0/" + table + "/{id} [get]\n" out += "func get" + inflector.Singularize(formatName(table)) + "(" + getPkGoFuncParamString(pk) + ", db *sqlx.DB) (interface{}, error) {\n" out += " ret := []" + formatName(table) + "{}\n" out += " arg := " + formatName(table) + "{}\n" out += setStructPkFields(pk) out += " queryStr := \"select *, " + selfQueryStr(pk, table) + "\"\n" out += setFkHALQueryStr(schemas, table) out += " queryStr += \" from " + table + " " + pkWhereStr(pk) + "\"\n" out += " nstmt, err := db.PrepareNamed(queryStr)\n" out += " err = nstmt.Select(&ret, arg)\n" out += " if err != nil {\n" out += " log.Println(err)\n" out += " return nil, err\n" out += " }\n" out += " nstmt.Close()\n" out += " return ret, nil\n" out += "}\n\n" out += "// @Title get" + formatName(table) + "s\n" out += "// @Description retrieves the " + table + "\n" out += "// @Accept application/json\n" out += "// @Success 200 {array} " + formatName(table) + "\n" out += "// @Resource /api/2.0\n" out += "// @Router /api/2.0/" + table + " [get]\n" out += "func get" + inflector.Pluralize(formatName(table)) + "(db *sqlx.DB) (interface{}, error) {\n" out += " ret := []" + formatName(table) + "{}\n" out += " queryStr := \"select *, " + selfQueryStr(pk, table) + "\"\n" out += setFkHALQueryStr(schemas, table) out += "queryStr += \" from " + table + "\"\n" out += " err := db.Select(&ret, queryStr)\n" out += " if err != nil {\n" out += " log.Println(err)\n" out += " return nil, err\n" out += " }\n" out += " return ret, nil\n" out += "}\n\n" out += "// @Title post" + formatName(table) + "\n" out += "// @Description enter a new " + table + "\n" out += "// @Accept application/json\n" out += "// @Param Body body " + formatName(table) + " true \"" + formatName(table) + " object that should be added to the table\"\n" out += "// @Success 200 {object} output_format.ApiWrapper\n" out += "// @Resource /api/2.0\n" out += "// @Router /api/2.0/" + table + " [post]\n" out += "func post" + inflector.Singularize(formatName(table)) + "(payload []byte, db *sqlx.DB) (interface{}, error) {\n" out += " var v " + formatName(table) + "\n" out += " err := json.Unmarshal(payload, &v)\n" out += " if err != nil {\n" out += " log.Println(err)\n" out += " return nil, err\n" out += " }\n" out += genInsertVarLines(schemas, table) out += " result, err := db.NamedExec(sqlString, v)\n" out += " if err != nil {\n" out += " log.Println(err)\n" out += " return nil, err\n" out += " }\n" out += " return result, err\n" out += "}\n\n" out += "// @Title put" + formatName(table) + "\n" out += "// @Description modify an existing " + table + "entry\n" out += "// @Accept application/json\n" out += "// @Param id path int true \"The row id\"\n" out += "// @Param Body body " + formatName(table) + " true \"" + formatName(table) + " object that should be added to the table\"\n" out += "// @Success 200 {object} output_format.ApiWrapper\n" out += "// @Resource /api/2.0\n" out += "// @Router /api/2.0/" + table + "/{id} [put]\n" out += "func put" + inflector.Singularize(formatName(table)) + "(" + getPkGoFuncParamString(pk) + ", payload []byte, db *sqlx.DB) (interface{}, error) {\n" out += " var arg " + formatName(table) + "\n" out += " err := json.Unmarshal(payload, &arg)\n" out += setStructPkFields(pk) out += " if err != nil {\n" out += " log.Println(err)\n" out += " return nil, err\n" out += " }\n" if updateLastUpdated { out += " arg.LastUpdated = time.Now()\n" } out += genUpdateVarLines(schemas, table) out += " result, err := db.NamedExec(sqlString, arg)\n" out += " if err != nil {\n" out += " log.Println(err)\n" out += " return nil, err\n" out += " }\n" out += " return result, err\n" out += "}\n\n" out += "// @Title del" + formatName(table) + "ById\n" out += "// @Description deletes " + table + " information for a certain id\n" out += "// @Accept application/json\n" out += "// @Param id path int false \"The row id\"\n" out += "// @Success 200 {array} " + formatName(table) + "\n" out += "// @Resource /api/2.0\n" out += "// @Router /api/2.0/" + table + "/{id} [delete]\n" out += "func del" + inflector.Singularize(formatName(table)) + "(" + getPkGoFuncParamString(pk) + ", db *sqlx.DB) (interface{}, error) {\n" out += " arg := " + formatName(table) + "{}\n" out += setStructPkFields(pk) out += " result, err := db.NamedExec(\"DELETE FROM " + table + " " + pkWhereStr(pk) + "\", arg)\n" out += " if err != nil {\n" out += " log.Println(err)\n" out += " return nil, err\n" out += " }\n" out += " return result, err\n" out += "}\n\n" return out }
func singularize(unit string) string { return inflector.Singularize(unit) }
func (n *Naming) Singularize() { n.ResourceName = inflector.Singularize(n.ResourceName) }
func (h HasMany) Model() string { return inflector.Singularize(h.Func()) }
func (b BelongsTo) Model() string { return inflector.Singularize(b.Func()) }