diff --git a/.gitignore b/.gitignore index ffab5d0..409a39f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ _testmain.go bee *.exe~ .goxc.local.json + +#IDEA +.idea diff --git a/apiapp.go b/apiapp.go index 8ae376e..5d24e55 100644 --- a/apiapp.go +++ b/apiapp.go @@ -42,19 +42,23 @@ on the existing database. The command 'api' creates a folder named [appname] and inside the folder deploy the following files/directories structure: + . ├── conf - │ └── app.conf + │   └── app.conf ├── controllers - │ └── object.go - │ └── user.go - ├── routers - │ └── router.go - ├── tests - │ └── default_test.go + │   ├── object_controller.go + │   └── user_controller.go + ├── docs + │   └── doc.go ├── main.go - └── models - └── object.go - └── user.go + ├── models + │   ├── object.go + │   └── user.go + ├── routers + │   └── router.go + ├── structures + └── tests + └── default_test.go `, } @@ -144,41 +148,36 @@ func init() { var apiModels = `package models import ( + "{{.Appname}}/structures" "errors" "strconv" "time" ) var ( - Objects map[string]*Object + Objects map[string]*structures.Object ) -type Object struct { - ObjectId string - Score int64 - PlayerName string -} - func init() { - Objects = make(map[string]*Object) - Objects["hjkhsbnmn123"] = &Object{"hjkhsbnmn123", 100, "astaxie"} - Objects["mjjkxsxsaa23"] = &Object{"mjjkxsxsaa23", 101, "someone"} + Objects = make(map[string]*structures.Object) + Objects["hjkhsbnmn123"] = &structures.Object{"hjkhsbnmn123", 100, "astaxie"} + Objects["mjjkxsxsaa23"] = &structures.Object{"mjjkxsxsaa23", 101, "someone"} } -func AddOne(object Object) (ObjectId string) { +func AddOne(object structures.Object) (ObjectId string) { object.ObjectId = "astaxie" + strconv.FormatInt(time.Now().UnixNano(), 10) Objects[object.ObjectId] = &object return object.ObjectId } -func GetOne(ObjectId string) (object *Object, err error) { +func GetOne(ObjectId string) (object *structures.Object, err error) { if v, ok := Objects[ObjectId]; ok { return v, nil } return nil, errors.New("ObjectId Not Exist") } -func GetAll() map[string]*Object { +func GetAll() map[string]*structures.Object { return Objects } @@ -199,53 +198,40 @@ func Delete(ObjectId string) { var apiModels2 = `package models import ( + "{{.Appname}}/structures" "errors" "strconv" "time" ) var ( - UserList map[string]*User + UserList map[string]*structures.User ) func init() { - UserList = make(map[string]*User) - u := User{"user_11111", "astaxie", "11111", Profile{"male", 20, "Singapore", "astaxie@gmail.com"}} + UserList = make(map[string]*structures.User) + u := structures.User{"user_11111", "astaxie", "11111", structures.Profile{"male", 20, "Singapore", "astaxie@gmail.com"}} UserList["user_11111"] = &u } -type User struct { - Id string - Username string - Password string - Profile Profile -} - -type Profile struct { - Gender string - Age int - Address string - Email string -} - -func AddUser(u User) string { +func AddUser(u structures.User) string { u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10) UserList[u.Id] = &u return u.Id } -func GetUser(uid string) (u *User, err error) { +func GetUser(uid string) (u *structures.User, err error) { if u, ok := UserList[uid]; ok { return u, nil } return nil, errors.New("User not exists") } -func GetAllUsers() map[string]*User { +func GetAllUsers() map[string]*structures.User { return UserList } -func UpdateUser(uid string, uu *User) (a *User, err error) { +func UpdateUser(uid string, uu *structures.User) (a *structures.User, err error) { if u, ok := UserList[uid]; ok { if uu.Username != "" { u.Username = uu.Username @@ -288,6 +274,7 @@ var apiControllers = `package controllers import ( "{{.Appname}}/models" + "{{.Appname}}/structures" "encoding/json" "github.com/astaxie/beego" @@ -305,7 +292,7 @@ type ObjectController struct { // @Failure 403 body is empty // @router / [post] func (o *ObjectController) Post() { - var ob models.Object + var ob structures.Object json.Unmarshal(o.Ctx.Input.RequestBody, &ob) objectid := models.AddOne(ob) o.Data["json"] = map[string]string{"ObjectId": objectid} @@ -351,7 +338,7 @@ func (o *ObjectController) GetAll() { // @router /:objectId [put] func (o *ObjectController) Put() { objectId := o.Ctx.Input.Param(":objectId") - var ob models.Object + var ob structures.Object json.Unmarshal(o.Ctx.Input.RequestBody, &ob) err := models.Update(objectId, ob.Score) @@ -377,10 +364,12 @@ func (o *ObjectController) Delete() { } ` + var apiControllers2 = `package controllers import ( "{{.Appname}}/models" + "{{.Appname}}/structures" "encoding/json" "github.com/astaxie/beego" @@ -398,7 +387,7 @@ type UserController struct { // @Failure 403 body is empty // @router / [post] func (u *UserController) Post() { - var user models.User + var user structures.User json.Unmarshal(u.Ctx.Input.RequestBody, &user) uid := models.AddUser(user) u.Data["json"] = map[string]string{"uid": uid} @@ -444,7 +433,7 @@ func (u *UserController) Get() { func (u *UserController) Put() { uid := u.GetString(":uid") if uid != "" { - var user models.User + var user structures.User json.Unmarshal(u.Ctx.Input.RequestBody, &user) uu, err := models.UpdateUser(uid, &user) if err != nil { @@ -538,6 +527,34 @@ func TestGet(t *testing.T) { ` +var apistructures = `package structures + +type User struct { + Id string + Username string + Password string + Profile Profile +} + +type Profile struct { + Gender string + Age int + Address string + Email string +} + +` + +var apistructures2 = `package structures + +type Object struct { + ObjectId string + Score int64 + PlayerName string +} + +` + func init() { cmdApiapp.Run = createapi cmdApiapp.Flag.Var(&tables, "tables", "specify tables to generate model") @@ -574,6 +591,8 @@ func createapi(cmd *Command, args []string) int { fmt.Println("create docs:", path.Join(apppath, "docs")) os.Mkdir(path.Join(apppath, "tests"), 0755) fmt.Println("create tests:", path.Join(apppath, "tests")) + os.Mkdir(path.Join(apppath, "helpers"), 0755) + fmt.Println("create helpers:", path.Join(apppath, "helpers")) fmt.Println("create conf app.conf:", path.Join(apppath, "conf", "app.conf")) writetofile(path.Join(apppath, "conf", "app.conf"), @@ -604,14 +623,16 @@ func createapi(cmd *Command, args []string) int { os.Mkdir(path.Join(apppath, "models"), 0755) fmt.Println("create models:", path.Join(apppath, "models")) os.Mkdir(path.Join(apppath, "routers"), 0755) - fmt.Println(path.Join(apppath, "routers") + string(path.Separator)) + fmt.Println("create routers:", path.Join(apppath, "routers")) + os.Mkdir(path.Join(apppath, "structures"), 0755) + fmt.Println("create structures:", path.Join(apppath, "structures")) - fmt.Println("create controllers object.go:", path.Join(apppath, "controllers", "object.go")) - writetofile(path.Join(apppath, "controllers", "object.go"), + fmt.Println("create controllers object_controller.go:", path.Join(apppath, "controllers", "object_controller.go")) + writetofile(path.Join(apppath, "controllers", "object_controller.go"), strings.Replace(apiControllers, "{{.Appname}}", packpath, -1)) - fmt.Println("create controllers user.go:", path.Join(apppath, "controllers", "user.go")) - writetofile(path.Join(apppath, "controllers", "user.go"), + fmt.Println("create controllers user_controller.go:", path.Join(apppath, "controllers", "user_controller.go")) + writetofile(path.Join(apppath, "controllers", "user_controller.go"), strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1)) fmt.Println("create tests default.go:", path.Join(apppath, "tests", "default_test.go")) @@ -623,10 +644,24 @@ func createapi(cmd *Command, args []string) int { strings.Replace(apirouter, "{{.Appname}}", packpath, -1)) fmt.Println("create models object.go:", path.Join(apppath, "models", "object.go")) - writetofile(path.Join(apppath, "models", "object.go"), apiModels) + writetofile(path.Join(apppath, "models", "object.go"), + strings.Replace(apiModels, "{{.Appname}}", packpath, -1)) fmt.Println("create models user.go:", path.Join(apppath, "models", "user.go")) - writetofile(path.Join(apppath, "models", "user.go"), apiModels2) + writetofile(path.Join(apppath, "models", "user.go"), + strings.Replace(apiModels2, "{{.Appname}}", packpath, -1)) + + fmt.Println("create structures user_structure.go:", path.Join(apppath, "structures", "user_structure.go")) + writetofile(path.Join(apppath, "structures", "user_structure.go"), apistructures) + + fmt.Println("create structures object_structure.go:", path.Join(apppath, "structures", "object_structure.go")) + writetofile(path.Join(apppath, "structures", "object_structure.go"), apistructures2) + + fmt.Println("create helpers user_helper.go:", path.Join(apppath, "helpers", "user_helper.go")) + writetofile(path.Join(apppath, "helpers", "user_helper.go"), "package helpers") + + fmt.Println("create helpers object_helper.go:", path.Join(apppath, "helpers", "object_helper.go")) + writetofile(path.Join(apppath, "helpers", "object_helper.go"), "package helpers") fmt.Println("create docs doc.go:", path.Join(apppath, "docs", "doc.go")) writetofile(path.Join(apppath, "docs", "doc.go"), "package docs") diff --git a/g.go b/g.go index e0eb265..f885b41 100644 --- a/g.go +++ b/g.go @@ -27,8 +27,14 @@ bee generate scaffold [scaffoldname] [-fields=""] [-driver=mysql] [-conn="root:@ -conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test example: bee generate scaffold post -fields="title:string,body:text" +bee generate structure [structurename] +bee generate structure [structurename] [-fields=""] + generate struct based + -fields: a list of table fields. Format: field:type, ... + +bee generate model [modelname] bee generate model [modelname] [-fields=""] - generate RESTFul model based on fields + generate RESTFul model based -fields: a list of table fields. Format: field:type, ... bee generate controller [controllerfile] @@ -44,6 +50,9 @@ bee generate migration [migrationfile] [-fields=""] bee generate docs generate swagger doc file +bee generate helper [filename] + generate helper file + bee generate test [routerfile] generate testcase @@ -170,6 +179,15 @@ func generateCode(cmd *Command, args []string) int { downsql = `m.SQL("DROP TABLE ` + "`" + mname + "`" + `")` } generateMigration(mname, upsql, downsql, curpath) + case "helper": + if len(args) == 2 { + cname := args[1] + generateHelper(cname, curpath) + } else { + ColorLog("[ERRO] Wrong number of arguments\n") + ColorLog("[HINT] Usage: bee generate helper [helpername]\n") + os.Exit(2) + } case "controller": if len(args) == 2 { cname := args[1] @@ -179,25 +197,52 @@ func generateCode(cmd *Command, args []string) int { ColorLog("[HINT] Usage: bee generate controller [controllername]\n") os.Exit(2) } - case "model": - if len(args) < 2 { + case "structure": + sname := args[1] + switch len(args) { + case 2: + generateStructure(sname, "", curpath) + case 3: + cmd.Flag.Parse(args[2:]) + if fields == "" { + ColorLog("[ERRO] Wrong number of arguments\n") + ColorLog("[HINT] Usage: bee generate structure [structurename] [-fields=\"title:string,body:text\"]\n") + os.Exit(2) + } + sname := args[1] + ColorLog("[INFO] Using '%s' as structure name\n", sname) + generateStructure(sname, fields.String(), curpath) + default: ColorLog("[ERRO] Wrong number of arguments\n") + ColorLog("[HINT] Usage: bee generate structure [structurename]\n") + ColorLog("[HINT] Usage: bee generate structure [structurename] [-fields=\"title:string,body:text\"]\n") + os.Exit(2) + + } + case "model": + mname := args[1] + switch len(args) { + case 2: + generateModel(mname, "", curpath) + case 3: + cmd.Flag.Parse(args[2:]) + if fields == "" { + ColorLog("[ERRO] Wrong number of arguments\n") + ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"title:string,body:text\"]\n") + os.Exit(2) + } + ColorLog("[INFO] Using '%s' as model name\n", mname) + generateModel(mname, fields.String(), curpath) + default: + ColorLog("[ERRO] Wrong number of arguments\n") + ColorLog("[HINT] Usage: bee generate model [modelname]\n") ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"\"]\n") os.Exit(2) } - cmd.Flag.Parse(args[2:]) - if fields == "" { - ColorLog("[ERRO] Wrong number of arguments\n") - ColorLog("[HINT] Usage: bee generate model [modelname] [-fields=\"title:string,body:text\"]\n") - os.Exit(2) - } - sname := args[1] - ColorLog("[INFO] Using '%s' as model name\n", sname) - generateModel(sname, fields.String(), curpath) case "view": if len(args) == 2 { - cname := args[1] - generateView(cname, curpath) + vname := args[1] + generateView(vname, curpath) } else { ColorLog("[ERRO] Wrong number of arguments\n") ColorLog("[HINT] Usage: bee generate view [viewpath]\n") diff --git a/g_appcode.go b/g_appcode.go index 7860912..9139cb5 100644 --- a/g_appcode.go +++ b/g_appcode.go @@ -32,6 +32,7 @@ const ( O_MODEL byte = 1 << iota O_CONTROLLER O_ROUTER + O_STRUCTURE ) // DbTransformer has method to reverse engineer a database schema to restful api code @@ -60,6 +61,7 @@ type MvcPath struct { ModelPath string ControllerPath string RouterPath string + StructurePath string } // typeMapping maps SQL data type to corresponding Go data type @@ -263,7 +265,7 @@ func generateAppcode(driver, connStr, level, tables, currpath string) { case "2": mode = O_MODEL | O_CONTROLLER case "3": - mode = O_MODEL | O_CONTROLLER | O_ROUTER + mode = O_MODEL | O_CONTROLLER | O_ROUTER | O_STRUCTURE default: ColorLog("[ERRO] Invalid 'level' option: %s\n", level) ColorLog("[HINT] Level must be either 1, 2 or 3\n") @@ -307,6 +309,7 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, cu mvcPath.ModelPath = path.Join(currpath, "models") mvcPath.ControllerPath = path.Join(currpath, "controllers") mvcPath.RouterPath = path.Join(currpath, "routers") + mvcPath.StructurePath = path.Join(currpath, "structures") createPaths(mode, mvcPath) pkgPath := getPackagePath(currpath) writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) @@ -710,15 +713,22 @@ func createPaths(mode byte, paths *MvcPath) { if (mode & O_ROUTER) == O_ROUTER { os.Mkdir(paths.RouterPath, 0777) } + if (mode & O_STRUCTURE) == O_STRUCTURE { + os.Mkdir(paths.StructurePath, 0777) + } } // writeSourceFiles generates source files for model/controller/router // It will wipe the following directories and recreate them:./models, ./controllers, ./routers // Newly geneated files will be inside these folders. func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { + if (O_STRUCTURE & mode) == O_STRUCTURE { + ColorLog("[INFO] Creating structure files...\n") + writeStructureFiles(tables, paths.StructurePath, selectedTables) + } if (O_MODEL & mode) == O_MODEL { ColorLog("[INFO] Creating model files...\n") - writeModelFiles(tables, paths.ModelPath, selectedTables) + writeModelFiles(tables, paths.ModelPath, selectedTables, pkgPath) } if (O_CONTROLLER & mode) == O_CONTROLLER { ColorLog("[INFO] Creating controller files...\n") @@ -731,7 +741,7 @@ func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath } // writeModelFiles generates model files -func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) { +func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool, pkgPath string) { for _, tb := range tables { // if selectedTables map is not nil and this table is not selected, ignore it if selectedTables != nil { @@ -768,7 +778,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo } else { template = MODEL_TPL } - fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1) + fileStr := strings.Replace(template, "{{pkgPath}}", pkgPath, -1) fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1) // if table contains time field, import time.Time package @@ -790,6 +800,61 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo } } +// writeStructureFiles generates structure files +func writeStructureFiles(tables []*Table, mPath string, selectedTables map[string]bool) { + for _, tb := range tables { + // if selectedTables map is not nil and this table is not selected, ignore it + if selectedTables != nil { + if _, selected := selectedTables[tb.Name]; !selected { + continue + } + } + filename := getFileName(tb.Name) + fpath := path.Join(mPath, filename+"_structure.go") + var f *os.File + var err error + if isExist(fpath) { + ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath) + if askForConfirmation() { + f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) + if err != nil { + ColorLog("[WARN] %v\n", err) + continue + } + } else { + ColorLog("[WARN] skip create file\n") + continue + } + } else { + f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) + if err != nil { + ColorLog("[WARN] %v\n", err) + continue + } + } + template := STRUCT_TPL + fileStr := strings.Replace(template, "{{Struct}}", tb.String(), 1) + fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) + fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1) + // if table contains time field, import time.Time package + timePkg := "" + importTimePkg := "" + if tb.ImportTimePkg { + timePkg = "\"time\"\n" + importTimePkg = "import \"time\"\n" + } + fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) + fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) + if _, err := f.WriteString(fileStr); err != nil { + ColorLog("[ERRO] Could not write model file to %s\n", fpath) + os.Exit(2) + } + f.Close() + ColorLog("[INFO] structure => %s\n", fpath) + formatSourceCode(fpath) + } +} + // writeControllerFiles generates controller files func writeControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath string) { for _, tb := range tables { @@ -803,7 +868,7 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri continue } filename := getFileName(tb.Name) - fpath := path.Join(cPath, filename+".go") + fpath := path.Join(cPath, filename+"_controller.go") var f *os.File var err error if isExist(fpath) { @@ -1001,14 +1066,21 @@ func getPackagePath(curpath string) (packpath string) { } const ( - STRUCT_MODEL_TPL = `package models + STRUCT_TPL = `package structures {{importTimePkg}} -{{modelStruct}} +{{Struct}} + +func (t *{{modelName}}) TableName() string { + return "{{tableName}}" +} +` + STRUCT_MODEL_TPL = `package models ` MODEL_TPL = `package models import ( + "{{pkgPath}}/structures" "errors" "fmt" "reflect" @@ -1017,19 +1089,13 @@ import ( "github.com/astaxie/beego/orm" ) -{{modelStruct}} - -func (t *{{modelName}}) TableName() string { - return "{{tableName}}" -} - func init() { - orm.RegisterModel(new({{modelName}})) + orm.RegisterModel(new(structures.{{modelName}})) } -// Add{{modelName}} insert a new {{modelName}} into database and returns +// Add {{modelName}} insert a new {{modelName}} into database and returns // last inserted Id on success. -func Add{{modelName}}(m *{{modelName}}) (id int64, err error) { +func Add{{modelName}}(m *structures.{{modelName}}) (id int64, err error) { o := orm.NewOrm() id, err = o.Insert(m) return @@ -1037,9 +1103,9 @@ func Add{{modelName}}(m *{{modelName}}) (id int64, err error) { // Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if // Id doesn't exist -func Get{{modelName}}ById(id int) (v *{{modelName}}, err error) { +func Get{{modelName}}ById(id int) (v *structures.{{modelName}}, err error) { o := orm.NewOrm() - v = &{{modelName}}{Id: id} + v = &structures.{{modelName}}{Id: id} if err = o.Read(v); err == nil { return v, nil } @@ -1051,7 +1117,7 @@ func Get{{modelName}}ById(id int) (v *{{modelName}}, err error) { func GetAll{{modelName}}(query map[string]string, fields []string, sortby []string, order []string, offset int64, limit int64) (ml []interface{}, err error) { o := orm.NewOrm() - qs := o.QueryTable(new({{modelName}})) + qs := o.QueryTable(new(structures.{{modelName}})) // query k=v for k, v := range query { // rewrite dot-notation to Object__Attribute @@ -1097,7 +1163,7 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri } } - var l []{{modelName}} + var l []structures.{{modelName}} qs = qs.OrderBy(sortFields...) if _, err := qs.Limit(limit, offset).All(&l, fields...); err == nil { if len(fields) == 0 { @@ -1122,9 +1188,9 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri // Update{{modelName}} updates {{modelName}} by Id and returns error if // the record to be updated doesn't exist -func Update{{modelName}}ById(m *{{modelName}}) (err error) { +func Update{{modelName}}ById(m *structures.{{modelName}}) (err error) { o := orm.NewOrm() - v := {{modelName}}{Id: m.Id} + v := structures.{{modelName}}{Id: m.Id} // ascertain id exists in the database if err = o.Read(&v); err == nil { var num int64 @@ -1139,11 +1205,11 @@ func Update{{modelName}}ById(m *{{modelName}}) (err error) { // the record to be deleted doesn't exist func Delete{{modelName}}(id int) (err error) { o := orm.NewOrm() - v := {{modelName}}{Id: id} + v := structures.{{modelName}}{Id: id} // ascertain id exists in the database if err = o.Read(&v); err == nil { var num int64 - if num, err = o.Delete(&{{modelName}}{Id: id}); err == nil { + if num, err = o.Delete(&structures.{{modelName}}{Id: id}); err == nil { fmt.Println("Number of records deleted in database:", num) } } @@ -1153,6 +1219,7 @@ func Delete{{modelName}}(id int) (err error) { CTRL_TPL = `package controllers import ( + "{{pkgPath}}/structures" "{{pkgPath}}/models" "encoding/json" "errors" @@ -1177,12 +1244,12 @@ func (c *{{ctrlName}}Controller) URLMapping() { // @Title Post // @Description create {{ctrlName}} -// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content" -// @Success 201 {int} models.{{ctrlName}} +// @Param body body structures.{{ctrlName}} true "body for {{ctrlName}} content" +// @Success 201 {int} structures.{{ctrlName}} // @Failure 403 body is empty // @router / [post] func (c *{{ctrlName}}Controller) Post() { - var v models.{{ctrlName}} + var v structures.{{ctrlName}} if err := json.Unmarshal(c.Ctx.Input.RequestBody, &v); err == nil { if _, err := models.Add{{ctrlName}}(&v); err == nil { c.Ctx.Output.SetStatus(201) @@ -1199,7 +1266,7 @@ func (c *{{ctrlName}}Controller) Post() { // @Title Get // @Description get {{ctrlName}} by id // @Param id path string true "The key for staticblock" -// @Success 200 {object} models.{{ctrlName}} +// @Success 200 {object} structures.{{ctrlName}} // @Failure 403 :id is empty // @router /:id [get] func (c *{{ctrlName}}Controller) GetOne() { @@ -1222,7 +1289,7 @@ func (c *{{ctrlName}}Controller) GetOne() { // @Param order query string false "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..." // @Param limit query string false "Limit the size of result set. Must be an integer" // @Param offset query string false "Start position of result set. Must be an integer" -// @Success 200 {object} models.{{ctrlName}} +// @Success 200 {object} structures.{{ctrlName}} // @Failure 403 // @router / [get] func (c *{{ctrlName}}Controller) GetAll() { @@ -1279,14 +1346,14 @@ func (c *{{ctrlName}}Controller) GetAll() { // @Title Update // @Description update the {{ctrlName}} // @Param id path string true "The id you want to update" -// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content" -// @Success 200 {object} models.{{ctrlName}} +// @Param body body structures.{{ctrlName}} true "body for {{ctrlName}} content" +// @Success 200 {object} structures.{{ctrlName}} // @Failure 403 :id is not int // @router /:id [put] func (c *{{ctrlName}}Controller) Put() { idStr := c.Ctx.Input.Param(":id") id, _ := strconv.Atoi(idStr) - v := models.{{ctrlName}}{Id: id} + v := structures.{{ctrlName}}{Id: id} if err := json.Unmarshal(c.Ctx.Input.RequestBody, &v); err == nil { if err := models.Update{{ctrlName}}ById(&v); err == nil { c.Data["json"] = "OK" diff --git a/g_controllers.go b/g_controller.go similarity index 91% rename from g_controllers.go rename to g_controller.go index 49b5fd9..bc1c532 100644 --- a/g_controllers.go +++ b/g_controller.go @@ -41,7 +41,7 @@ func generateController(cname, crupath string) { os.Exit(2) } } - fpath := path.Join(fp, strings.ToLower(controllerName)+".go") + fpath := path.Join(fp, strings.ToLower(controllerName)+"_controller.go") if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { defer f.Close() modelPath := path.Join(crupath, "models", strings.ToLower(controllerName)+".go") @@ -87,8 +87,8 @@ func (c *{{controllerName}}Controller) URLMapping() { // @Title Post // @Description create {{controllerName}} -// @Param body body models.{{controllerName}} true "body for {{controllerName}} content" -// @Success 201 {object} models.{{controllerName}} +// @Param body body structures.{{controllerName}} true "body for {{controllerName}} content" +// @Success 201 {object} structures.{{controllerName}} // @Failure 403 body is empty // @router / [post] func (c *{{controllerName}}Controller) Post() { @@ -98,7 +98,7 @@ func (c *{{controllerName}}Controller) Post() { // @Title Get // @Description get {{controllerName}} by id // @Param id path string true "The key for staticblock" -// @Success 200 {object} models.{{controllerName}} +// @Success 200 {object} structures.{{controllerName}} // @Failure 403 :id is empty // @router /:id [get] func (c *{{controllerName}}Controller) GetOne() { @@ -113,7 +113,7 @@ func (c *{{controllerName}}Controller) GetOne() { // @Param order query string false "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..." // @Param limit query string false "Limit the size of result set. Must be an integer" // @Param offset query string false "Start position of result set. Must be an integer" -// @Success 200 {object} models.{{controllerName}} +// @Success 200 {object} structures.{{controllerName}} // @Failure 403 // @router / [get] func (c *{{controllerName}}Controller) GetAll() { @@ -124,7 +124,7 @@ func (c *{{controllerName}}Controller) GetAll() { // @Description update the {{controllerName}} // @Param id path string true "The id you want to update" // @Param body body models.{{controllerName}} true "body for {{controllerName}} content" -// @Success 200 {object} models.{{controllerName}} +// @Success 200 {object} structures.{{controllerName}} // @Failure 403 :id is not int // @router /:id [put] func (c *{{controllerName}}Controller) Put() { @@ -146,6 +146,7 @@ var controllerModelTpl = `package {{packageName}} import ( "{{pkgPath}}/models" + "{{pkgPath}}/structures" "encoding/json" "errors" "strconv" @@ -169,12 +170,12 @@ func (c *{{controllerName}}Controller) URLMapping() { // @Title Post // @Description create {{controllerName}} -// @Param body body models.{{controllerName}} true "body for {{controllerName}} content" -// @Success 201 {int} models.{{controllerName}} +// @Param body body structures.{{controllerName}} true "body for {{controllerName}} content" +// @Success 201 {int} structures.{{controllerName}} // @Failure 403 body is empty // @router / [post] func (c *{{controllerName}}Controller) Post() { - var v models.{{controllerName}} + var v structures.{{controllerName}} json.Unmarshal(c.Ctx.Input.RequestBody, &v) if _, err := models.Add{{controllerName}}(&v); err == nil { c.Ctx.Output.SetStatus(201) @@ -188,7 +189,7 @@ func (c *{{controllerName}}Controller) Post() { // @Title Get // @Description get {{controllerName}} by id // @Param id path string true "The key for staticblock" -// @Success 200 {object} models.{{controllerName}} +// @Success 200 {object} structures.{{controllerName}} // @Failure 403 :id is empty // @router /:id [get] func (c *{{controllerName}}Controller) GetOne() { @@ -211,7 +212,7 @@ func (c *{{controllerName}}Controller) GetOne() { // @Param order query string false "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..." // @Param limit query string false "Limit the size of result set. Must be an integer" // @Param offset query string false "Start position of result set. Must be an integer" -// @Success 200 {object} models.{{controllerName}} +// @Success 200 {object} structures.{{controllerName}} // @Failure 403 // @router / [get] func (c *{{controllerName}}Controller) GetAll() { @@ -269,13 +270,13 @@ func (c *{{controllerName}}Controller) GetAll() { // @Description update the {{controllerName}} // @Param id path string true "The id you want to update" // @Param body body models.{{controllerName}} true "body for {{controllerName}} content" -// @Success 200 {object} models.{{controllerName}} +// @Success 200 {object} structures.{{controllerName}} // @Failure 403 :id is not int // @router /:id [put] func (c *{{controllerName}}Controller) Put() { idStr := c.Ctx.Input.Param(":id") id, _ := strconv.ParseInt(idStr, 0, 64) - v := models.{{controllerName}}{Id: id} + v := structures.{{controllerName}}{Id: id} json.Unmarshal(c.Ctx.Input.RequestBody, &v) if err := models.Update{{controllerName}}ById(&v); err == nil { c.Data["json"] = "OK" diff --git a/g_helper.go b/g_helper.go new file mode 100644 index 0000000..0817117 --- /dev/null +++ b/g_helper.go @@ -0,0 +1,50 @@ +package main + +import ( + "os" + "path" + "strings" +) + +func generateHelper(cname, crupath string) { + p, f := path.Split(cname) + helperName := strings.Title(f) + packageName := "helpers" + if p != "" { + i := strings.LastIndex(p[:len(p)-1], "/") + packageName = p[i+1 : len(p)-1] + } + ColorLog("[INFO] Using '%s' as helpers name\n", helperName) + ColorLog("[INFO] Using '%s' as package name\n", packageName) + fp := path.Join(crupath, "helpers", p) + if _, err := os.Stat(fp); os.IsNotExist(err) { + // create controller directory + if err := os.MkdirAll(fp, 0777); err != nil { + ColorLog("[ERRO] Could not create helpers directory: %s\n", err) + os.Exit(2) + } + } + fpath := path.Join(fp, strings.ToLower(helperName)+"_helper.go") + if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { + defer f.Close() + content := strings.Replace(BASE_HELPER_TPL, "{{packageName}}", packageName, -1) + content = strings.Replace(content, "{{helperName}}", helperName, -1) + f.WriteString(content) + // gofmt generated source code + formatSourceCode(fpath) + ColorLog("[INFO] helpers file generated: %s\n", fpath) + } else { + // error creating file + ColorLog("[ERRO] Could not create helper file: %s\n", err) + os.Exit(2) + } +} + +const ( + BASE_HELPER_TPL = `package {{packageName}} + + func {{helperName}}() { + + } + ` +) diff --git a/g_hproseappcode.go b/g_hproseappcode.go index 86e3d02..e8d605b 100644 --- a/g_hproseappcode.go +++ b/g_hproseappcode.go @@ -31,7 +31,7 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) { var mode byte switch level { case "1": - mode = O_MODEL + mode = O_MODEL | O_STRUCTURE case "2": mode = O_MODEL | O_CONTROLLER case "3": @@ -77,6 +77,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo tables := getTableObjects(tableNames, db, trans) mvcPath := new(MvcPath) mvcPath.ModelPath = path.Join(currpath, "models") + mvcPath.StructurePath = path.Join(currpath, "structures") createPaths(mode, mvcPath) pkgPath := getPackagePath(currpath) writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) @@ -92,12 +93,16 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { if (O_MODEL & mode) == O_MODEL { ColorLog("[INFO] Creating model files...\n") - writeHproseModelFiles(tables, paths.ModelPath, selectedTables) + writeHproseModelFiles(tables, paths.ModelPath, selectedTables, pkgPath) + } + if (O_MODEL & mode) == O_MODEL { + ColorLog("[INFO] Creating structure files...\n") + writeHproseStructureFiles(tables, paths.StructurePath, selectedTables) } } // writeHproseModelFiles generates model files -func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) { +func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool, pkgPath string) { for _, tb := range tables { // if selectedTables map is not nil and this table is not selected, ignore it if selectedTables != nil { @@ -135,8 +140,9 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str template = HPROSE_MODEL_TPL hproseAddFunctions = append(hproseAddFunctions, strings.Replace(HPROSE_ADDFUNCTION, "{{modelName}}", camelCase(tb.Name), -1)) } - fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1) + fileStr := strings.Replace(template, "{{pkgPath}}", pkgPath, -1) fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) + fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1) // if table contains time field, import time.Time package timePkg := "" importTimePkg := "" @@ -156,6 +162,59 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str } } +// writeHproseStructureFiles generates structure files +func writeHproseStructureFiles(tables []*Table, mPath string, selectedTables map[string]bool) { + for _, tb := range tables { + // if selectedTables map is not nil and this table is not selected, ignore it + if selectedTables != nil { + if _, selected := selectedTables[tb.Name]; !selected { + continue + } + } + filename := getFileName(tb.Name) + fpath := path.Join(mPath, filename+"_structure.go") + var f *os.File + var err error + if isExist(fpath) { + ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath) + if askForConfirmation() { + f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) + if err != nil { + ColorLog("[WARN] %v\n", err) + continue + } + } else { + ColorLog("[WARN] skip create file\n") + continue + } + } else { + f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) + if err != nil { + ColorLog("[WARN] %v\n", err) + continue + } + } + template := HPROSE_STRUCT_TPL + fileStr := strings.Replace(template, "{{Struct}}", tb.String(), 1) + // if table contains time field, import time.Time package + timePkg := "" + importTimePkg := "" + if tb.ImportTimePkg { + timePkg = "\"time\"\n" + importTimePkg = "import \"time\"\n" + } + fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) + fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) + if _, err := f.WriteString(fileStr); err != nil { + ColorLog("[ERRO] Could not write model file to %s\n", fpath) + os.Exit(2) + } + f.Close() + ColorLog("[INFO] structure => %s\n", fpath) + formatSourceCode(fpath) + } +} + const ( HPROSE_ADDFUNCTION = ` // publish about {{modelName}} function @@ -167,13 +226,16 @@ const ( ` HPROSE_STRUCT_MODEL_TPL = `package models +` + HPROSE_STRUCT_TPL = `package structures {{importTimePkg}} -{{modelStruct}} +{{Struct}} ` HPROSE_MODEL_TPL = `package models import ( + "{{pkgPath}}/structures" "errors" "fmt" "reflect" @@ -182,15 +244,13 @@ import ( "github.com/astaxie/beego/orm" ) -{{modelStruct}} - func init() { - orm.RegisterModel(new({{modelName}})) + orm.RegisterModel(new(structures.{{modelName}})) } // Add{{modelName}} insert a new {{modelName}} into database and returns // last inserted Id on success. -func Add{{modelName}}(m *{{modelName}}) (id int64, err error) { +func Add{{modelName}}(m *structures.{{modelName}}) (id int64, err error) { o := orm.NewOrm() id, err = o.Insert(m) return @@ -198,9 +258,9 @@ func Add{{modelName}}(m *{{modelName}}) (id int64, err error) { // Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if // Id doesn't exist -func Get{{modelName}}ById(id int) (v *{{modelName}}, err error) { +func Get{{modelName}}ById(id int) (v *structures.{{modelName}}, err error) { o := orm.NewOrm() - v = &{{modelName}}{Id: id} + v = &structures.{{modelName}}{Id: id} if err = o.Read(v); err == nil { return v, nil } @@ -212,7 +272,7 @@ func Get{{modelName}}ById(id int) (v *{{modelName}}, err error) { func GetAll{{modelName}}(query map[string]string, fields []string, sortby []string, order []string, offset int64, limit int64) (ml []interface{}, err error) { o := orm.NewOrm() - qs := o.QueryTable(new({{modelName}})) + qs := o.QueryTable(new(structures.{{modelName}})) // query k=v for k, v := range query { // rewrite dot-notation to Object__Attribute @@ -258,7 +318,7 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri } } - var l []{{modelName}} + var l []structures.{{modelName}} qs = qs.OrderBy(sortFields...) if _, err := qs.Limit(limit, offset).All(&l, fields...); err == nil { if len(fields) == 0 { @@ -283,9 +343,9 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri // Update{{modelName}} updates {{modelName}} by Id and returns error if // the record to be updated doesn't exist -func Update{{modelName}}ById(m *{{modelName}}) (err error) { +func Update{{modelName}}ById(m *structures.{{modelName}}) (err error) { o := orm.NewOrm() - v := {{modelName}}{Id: m.Id} + v := structures.{{modelName}}{Id: m.Id} // ascertain id exists in the database if err = o.Read(&v); err == nil { var num int64 @@ -300,11 +360,11 @@ func Update{{modelName}}ById(m *{{modelName}}) (err error) { // the record to be deleted doesn't exist func Delete{{modelName}}(id int) (err error) { o := orm.NewOrm() - v := {{modelName}}{Id: id} + v := structures.{{modelName}}{Id: id} // ascertain id exists in the database if err = o.Read(&v); err == nil { var num int64 - if num, err = o.Delete(&{{modelName}}{Id: id}); err == nil { + if num, err = o.Delete(&structures.{{modelName}}{Id: id}); err == nil { fmt.Println("Number of records deleted in database:", num) } } diff --git a/g_model.go b/g_model.go index 859164f..e6b7ff0 100644 --- a/g_model.go +++ b/g_model.go @@ -1,7 +1,6 @@ package main import ( - "errors" "os" "path" "strings" @@ -15,11 +14,6 @@ func generateModel(mname, fields, crupath string) { i := strings.LastIndex(p[:len(p)-1], "/") packageName = p[i+1 : len(p)-1] } - modelStruct, err, hastime := getStruct(modelName, fields) - if err != nil { - ColorLog("[ERRO] Could not genrate models struct: %s\n", err) - os.Exit(2) - } ColorLog("[INFO] Using '%s' as model name\n", modelName) ColorLog("[INFO] Using '%s' as package name\n", packageName) fp := path.Join(crupath, "models", p) @@ -33,14 +27,16 @@ func generateModel(mname, fields, crupath string) { fpath := path.Join(fp, strings.ToLower(modelName)+".go") if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { defer f.Close() - content := strings.Replace(modelTpl, "{{packageName}}", packageName, -1) - content = strings.Replace(content, "{{modelName}}", modelName, -1) - content = strings.Replace(content, "{{modelStruct}}", modelStruct, -1) - if hastime { - content = strings.Replace(content, "{{timePkg}}", `"time"`, -1) + var content string + if fields != "" { + generateStructure(modelName,fields,crupath) + content = strings.Replace(CRUD_MODEL_TPL, "{{packageName}}", packageName, -1) + pkgPath := getPackagePath(crupath) + content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1) } else { - content = strings.Replace(content, "{{timePkg}}", "", -1) + content = strings.Replace(BASE_MODEL_TPL, "{{packageName}}", packageName, -1) } + content = strings.Replace(content, "{{modelName}}", modelName, -1) f.WriteString(content) // gofmt generated source code formatSourceCode(fpath) @@ -52,204 +48,172 @@ func generateModel(mname, fields, crupath string) { } } -func getStruct(structname, fields string) (string, error, bool) { - if fields == "" { - return "", errors.New("fields can't empty"), false - } - hastime := false - structStr := "type " + structname + " struct{\n" - fds := strings.Split(fields, ",") - for i, v := range fds { - kv := strings.SplitN(v, ":", 2) - if len(kv) != 2 { - return "", errors.New("the filds format is wrong. should key:type,key:type " + v), false - } - typ, tag, hastimeinner := getType(kv[1]) - if typ == "" { - return "", errors.New("the filds format is wrong. should key:type,key:type " + v), false - } - if i == 0 && strings.ToLower(kv[0]) != "id" { - structStr = structStr + "Id int64 `orm:\"auto\"`\n" - } - if hastimeinner { - hastime = true - } - structStr = structStr + camelString(kv[0]) + " " + typ + " " + tag + "\n" - } - structStr += "}\n" - return structStr, nil, hastime -} +const ( + BASE_MODEL_TPL = `package {{packageName}} -// fields support type -// http://beego.me/docs/mvc/model/models.md#mysql -func getType(ktype string) (kt, tag string, hasTime bool) { - kv := strings.SplitN(ktype, ":", 2) - switch kv[0] { - case "string": - if len(kv) == 2 { - return "string", "`orm:\"size(" + kv[1] + ")\"`", false + // Add{{modelName}} insert a new {{modelName}} into database and returns + // last inserted Id on success. + func Add{{modelName}}() () { + + } + + // Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if + // Id doesn't exist + func Get{{modelName}}ById() () { + + } + + // GetAll{{modelName}} retrieves all {{modelName}} matches certain condition. Returns empty list if + // no records exist + func GetAll{{modelName}}() () { + + } + + // Update{{modelName}} updates {{modelName}} by Id and returns error if + // the record to be updated doesn't exist + func Update{{modelName}}ById() () { + + } + + // Delete{{modelName}} deletes {{modelName}} by Id and returns error if + // the record to be deleted doesn't exist + func Delete{{modelName}}() () { + + } + ` + CRUD_MODEL_TPL = `package {{packageName}} + + import ( + "{{pkgPath}}/structures" + "errors" + "fmt" + "reflect" + "strings" + + "github.com/astaxie/beego/orm" + ) + + // Add{{modelName}} insert a new {{modelName}} into database and returns + // last inserted Id on success. + func Add{{modelName}}(m *structures.{{modelName}}) (id int64, err error) { + o := orm.NewOrm() + id, err = o.Insert(m) + return + } + + // Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if + // Id doesn't exist + func Get{{modelName}}ById(id int64) (v *structures.{{modelName}}, err error) { + o := orm.NewOrm() + v = &structures.{{modelName}}{Id: id} + if err = o.Read(v); err == nil { + return v, nil + } + return nil, err + } + + // GetAll{{modelName}} retrieves all {{modelName}} matches certain condition. Returns empty list if + // no records exist + func GetAll{{modelName}}(query map[string]string, fields []string, sortby []string, order []string, + offset int64, limit int64) (ml []interface{}, err error) { + o := orm.NewOrm() + qs := o.QueryTable(new(structures.{{modelName}})) + // query k=v + for k, v := range query { + // rewrite dot-notation to Object__Attribute + k = strings.Replace(k, ".", "__", -1) + qs = qs.Filter(k, v) + } + // order by: + var sortFields []string + if len(sortby) != 0 { + if len(sortby) == len(order) { + // 1) for each sort field, there is an associated order + for i, v := range sortby { + orderby := "" + if order[i] == "desc" { + orderby = "-" + v + } else if order[i] == "asc" { + orderby = v + } else { + return nil, errors.New("Error: Invalid order. Must be either [asc|desc]") + } + sortFields = append(sortFields, orderby) + } + qs = qs.OrderBy(sortFields...) + } else if len(sortby) != len(order) && len(order) == 1 { + // 2) there is exactly one order, all the sorted fields will be sorted by this order + for _, v := range sortby { + orderby := "" + if order[0] == "desc" { + orderby = "-" + v + } else if order[0] == "asc" { + orderby = v + } else { + return nil, errors.New("Error: Invalid order. Must be either [asc|desc]") + } + sortFields = append(sortFields, orderby) + } + } else if len(sortby) != len(order) && len(order) != 1 { + return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1") + } } else { - return "string", "`orm:\"size(128)\"`", false + if len(order) != 0 { + return nil, errors.New("Error: unused 'order' fields") + } } - case "text": - return "string", "`orm:\"type(longtext)\"`", false - case "auto": - return "int64", "`orm:\"auto\"`", false - case "pk": - return "int64", "`orm:\"pk\"`", false - case "datetime": - return "time.Time", "`orm:\"type(datetime)\"`", true - case "int", "int8", "int16", "int32", "int64": - fallthrough - case "uint", "uint8", "uint16", "uint32", "uint64": - fallthrough - case "bool": - fallthrough - case "float32", "float64": - return kv[0], "", false - case "float": - return "float64", "", false + + var l []structures.{{modelName}} + qs = qs.OrderBy(sortFields...) + if _, err := qs.Limit(limit, offset).All(&l, fields...); err == nil { + if len(fields) == 0 { + for _, v := range l { + ml = append(ml, v) + } + } else { + // trim unused fields + for _, v := range l { + m := make(map[string]interface{}) + val := reflect.ValueOf(v) + for _, fname := range fields { + m[fname] = val.FieldByName(fname).Interface() + } + ml = append(ml, m) + } + } + return ml, nil + } + return nil, err } - return "", "", false -} -var modelTpl = `package {{packageName}} + // Update{{modelName}} updates {{modelName}} by Id and returns error if + // the record to be updated doesn't exist + func Update{{modelName}}ById(m *structures.{{modelName}}) (err error) { + o := orm.NewOrm() + v := structures.{{modelName}}{Id: m.Id} + // ascertain id exists in the database + if err = o.Read(&v); err == nil { + var num int64 + if num, err = o.Update(m); err == nil { + fmt.Println("Number of records updated in database:", num) + } + } + return + } -import ( - "errors" - "fmt" - "reflect" - "strings" - {{timePkg}} - "github.com/astaxie/beego/orm" + // Delete{{modelName}} deletes {{modelName}} by Id and returns error if + // the record to be deleted doesn't exist + func Delete{{modelName}}(id int64) (err error) { + o := orm.NewOrm() + v := structures.{{modelName}}{Id: id} + // ascertain id exists in the database + if err = o.Read(&v); err == nil { + var num int64 + if num, err = o.Delete(&structures.{{modelName}}{Id: id}); err == nil { + fmt.Println("Number of records deleted in database:", num) + } + } + return + } + ` ) - -{{modelStruct}} - -func init() { - orm.RegisterModel(new({{modelName}})) -} - -// Add{{modelName}} insert a new {{modelName}} into database and returns -// last inserted Id on success. -func Add{{modelName}}(m *{{modelName}}) (id int64, err error) { - o := orm.NewOrm() - id, err = o.Insert(m) - return -} - -// Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if -// Id doesn't exist -func Get{{modelName}}ById(id int64) (v *{{modelName}}, err error) { - o := orm.NewOrm() - v = &{{modelName}}{Id: id} - if err = o.Read(v); err == nil { - return v, nil - } - return nil, err -} - -// GetAll{{modelName}} retrieves all {{modelName}} matches certain condition. Returns empty list if -// no records exist -func GetAll{{modelName}}(query map[string]string, fields []string, sortby []string, order []string, - offset int64, limit int64) (ml []interface{}, err error) { - o := orm.NewOrm() - qs := o.QueryTable(new({{modelName}})) - // query k=v - for k, v := range query { - // rewrite dot-notation to Object__Attribute - k = strings.Replace(k, ".", "__", -1) - qs = qs.Filter(k, v) - } - // order by: - var sortFields []string - if len(sortby) != 0 { - if len(sortby) == len(order) { - // 1) for each sort field, there is an associated order - for i, v := range sortby { - orderby := "" - if order[i] == "desc" { - orderby = "-" + v - } else if order[i] == "asc" { - orderby = v - } else { - return nil, errors.New("Error: Invalid order. Must be either [asc|desc]") - } - sortFields = append(sortFields, orderby) - } - qs = qs.OrderBy(sortFields...) - } else if len(sortby) != len(order) && len(order) == 1 { - // 2) there is exactly one order, all the sorted fields will be sorted by this order - for _, v := range sortby { - orderby := "" - if order[0] == "desc" { - orderby = "-" + v - } else if order[0] == "asc" { - orderby = v - } else { - return nil, errors.New("Error: Invalid order. Must be either [asc|desc]") - } - sortFields = append(sortFields, orderby) - } - } else if len(sortby) != len(order) && len(order) != 1 { - return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1") - } - } else { - if len(order) != 0 { - return nil, errors.New("Error: unused 'order' fields") - } - } - - var l []{{modelName}} - qs = qs.OrderBy(sortFields...) - if _, err := qs.Limit(limit, offset).All(&l, fields...); err == nil { - if len(fields) == 0 { - for _, v := range l { - ml = append(ml, v) - } - } else { - // trim unused fields - for _, v := range l { - m := make(map[string]interface{}) - val := reflect.ValueOf(v) - for _, fname := range fields { - m[fname] = val.FieldByName(fname).Interface() - } - ml = append(ml, m) - } - } - return ml, nil - } - return nil, err -} - -// Update{{modelName}} updates {{modelName}} by Id and returns error if -// the record to be updated doesn't exist -func Update{{modelName}}ById(m *{{modelName}}) (err error) { - o := orm.NewOrm() - v := {{modelName}}{Id: m.Id} - // ascertain id exists in the database - if err = o.Read(&v); err == nil { - var num int64 - if num, err = o.Update(m); err == nil { - fmt.Println("Number of records updated in database:", num) - } - } - return -} - -// Delete{{modelName}} deletes {{modelName}} by Id and returns error if -// the record to be deleted doesn't exist -func Delete{{modelName}}(id int64) (err error) { - o := orm.NewOrm() - v := {{modelName}}{Id: id} - // ascertain id exists in the database - if err = o.Read(&v); err == nil { - var num int64 - if num, err = o.Delete(&{{modelName}}{Id: id}); err == nil { - fmt.Println("Number of records deleted in database:", num) - } - } - return -} -` diff --git a/g_structure.go b/g_structure.go new file mode 100644 index 0000000..3879010 --- /dev/null +++ b/g_structure.go @@ -0,0 +1,160 @@ +// Copyright 2016 Dylan LYU (mingzong.lyu@gmail.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package main + +import ( + "errors" + "os" + "path" + "strings" +) + +func generateStructure(cname, fields, crupath string) { + p, f := path.Split(cname) + structureName := strings.Title(f) + packageName := "structures" + if p != "" { + i := strings.LastIndex(p[:len(p)-1], "/") + packageName = p[i+1 : len(p)-1] + } + ColorLog("[INFO] Using '%s' as structure name\n", structureName) + ColorLog("[INFO] Using '%s' as package name\n", packageName) + fp := path.Join(crupath, packageName, p) + if _, err := os.Stat(fp); os.IsNotExist(err) { + // create controller directory + if err := os.MkdirAll(fp, 0777); err != nil { + ColorLog("[ERRO] Could not create structures directory: %s\n", err) + os.Exit(2) + } + } + fpath := path.Join(fp, strings.ToLower(structureName)+"_structure.go") + if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { + defer f.Close() + var content string + + if fields != "" { + structStruct, err, hastime := getStruct(structureName, fields) + if err != nil { + ColorLog("[ERRO] Could not genrate struct: %s\n", err) + os.Exit(2) + } + content = strings.Replace(STRUCTURE_TPL, "{{packageName}}", packageName, -1) + content = strings.Replace(content, "{{structStruct}}", structStruct, -1) + if hastime { + content = strings.Replace(content, "{{timePkg}}", `"time"`, -1) + } else { + content = strings.Replace(content, "{{timePkg}}", "", -1) + } + + } else { + content = strings.Replace(BAST_STRUCTURE_TPL, "{{packageName}}", packageName, -1) + } + content = strings.Replace(content, "{{structureName}}", structureName, -1) + f.WriteString(content) + // gofmt generated source code + formatSourceCode(fpath) + ColorLog("[INFO] structure file generated: %s\n", fpath) + } else { + // error creating file + ColorLog("[ERRO] Could not create structure file: %s\n", err) + os.Exit(2) + } + +} + +func getStruct(structname, fields string) (string, error, bool) { + if fields == "" { + return "", errors.New("fields can't empty"), false + } + hastime := false + structStr := "type " + structname + " struct{\n" + fds := strings.Split(fields, ",") + for i, v := range fds { + kv := strings.SplitN(v, ":", 2) + if len(kv) != 2 { + return "", errors.New("the filds format is wrong. should key:type,key:type " + v), false + } + typ, tag, hastimeinner := getType(kv[1]) + if typ == "" { + return "", errors.New("the filds format is wrong. should key:type,key:type " + v), false + } + if i == 0 && strings.ToLower(kv[0]) != "id" { + structStr = structStr + "Id int64 `orm:\"auto\"`\n" + } + if hastimeinner { + hastime = true + } + structStr = structStr + camelString(kv[0]) + " " + typ + " " + tag + "\n" + } + structStr += "}\n" + return structStr, nil, hastime +} + +// fields support type +// http://beego.me/docs/mvc/model/models.md#mysql +func getType(ktype string) (kt, tag string, hasTime bool) { + kv := strings.SplitN(ktype, ":", 2) + switch kv[0] { + case "string": + if len(kv) == 2 { + return "string", "`orm:\"size(" + kv[1] + ")\"`", false + } else { + return "string", "`orm:\"size(128)\"`", false + } + case "text": + return "string", "`orm:\"type(longtext)\"`", false + case "auto": + return "int64", "`orm:\"auto\"`", false + case "pk": + return "int64", "`orm:\"pk\"`", false + case "datetime": + return "time.Time", "`orm:\"type(datetime)\"`", true + case "int", "int8", "int16", "int32", "int64": + fallthrough + case "uint", "uint8", "uint16", "uint32", "uint64": + fallthrough + case "bool": + fallthrough + case "float32", "float64": + return kv[0], "", false + case "float": + return "float64", "", false + } + return "", "", false +} + +const ( + BAST_STRUCTURE_TPL = `package {{packageName}} + + type {{structureName}} struct { + + } + ` + + STRUCTURE_TPL = `package {{packageName}} + + import( + "github.com/astaxie/beego/orm" + + {{timePkg}} + ) + + {{structStruct}} + + func init() { + orm.RegisterModel(new({{structureName}})) + } + ` +) diff --git a/hproseapp.go b/hproseapp.go index 5167c31..d5d7dd3 100644 --- a/hproseapp.go +++ b/hproseapp.go @@ -45,11 +45,15 @@ In the current path, will create a folder named [appname] In the appname folder has the follow struct: ├── conf - │ └── app.conf + │   └── app.conf + ├── helpers ├── main.go - └── models - └── object.go - └── user.go + ├── models + │   ├── object.go + │   └── user.go + └── structures + ├── object_structure.go + └── user_structure.go `, } @@ -276,6 +280,8 @@ func createhprose(cmd *Command, args []string) int { fmt.Println("create conf app.conf:", path.Join(apppath, "conf", "app.conf")) writetofile(path.Join(apppath, "conf", "app.conf"), strings.Replace(hproseconf, "{{.Appname}}", args[0], -1)) + os.Mkdir(path.Join(apppath, "helpers"), 0755) + fmt.Println("create helpers:", path.Join(apppath, "helpers")) if conn != "" { ColorLog("[INFO] Using '%s' as 'driver'\n", driver) @@ -300,14 +306,25 @@ func createhprose(cmd *Command, args []string) int { ), ) } else { + os.Mkdir(path.Join(apppath, "structures"), 0755) + fmt.Println("create structures:", path.Join(apppath, "structures")) + + fmt.Println("create structures user_structure.go:", path.Join(apppath, "structures", "user_structure.go")) + writetofile(path.Join(apppath, "structures", "user_structure.go"), apistructures) + + fmt.Println("create structures object_structure.go:", path.Join(apppath, "structures", "object_structure.go")) + writetofile(path.Join(apppath, "structures", "object_structure.go"), apistructures2) + os.Mkdir(path.Join(apppath, "models"), 0755) fmt.Println("create models:", path.Join(apppath, "models")) fmt.Println("create models object.go:", path.Join(apppath, "models", "object.go")) - writetofile(path.Join(apppath, "models", "object.go"), apiModels) + writetofile(path.Join(apppath, "models", "object.go"), + strings.Replace(apiModels, "{{.Appname}}", packpath, -1)) fmt.Println("create models user.go:", path.Join(apppath, "models", "user.go")) - writetofile(path.Join(apppath, "models", "user.go"), apiModels2) + writetofile(path.Join(apppath, "models", "user.go"), + strings.Replace(apiModels2, "{{.Appname}}", packpath, -1)) fmt.Println("create main.go:", path.Join(apppath, "main.go")) writetofile(path.Join(apppath, "main.go"), diff --git a/new.go b/new.go index bba644d..ceda69e 100644 --- a/new.go +++ b/new.go @@ -30,22 +30,25 @@ Creates a Beego application for the given app name in the current directory. The command 'new' creates a folder named [appname] and inside the folder deploy the following files/directories structure: - |- main.go - |- conf - |- app.conf - |- controllers - |- default.go - |- models - |- routers - |- router.go - |- tests - |- default_test.go - |- static - |- js - |- css - |- img - |- views - index.tpl + . + ├── conf + │   └── app.conf + ├── controllers + │   └── index_controller.go + ├── helpers + ├── main.go + ├── models + ├── routers + │   └── router.go + ├── static + │   ├── css + │   ├── img + │   └── js + ├── structure + ├── tests + │   └── default_test.go + └── views + └── index.tpl `, } @@ -116,12 +119,16 @@ func createApp(cmd *Command, args []string) int { fmt.Println(path.Join(apppath, "conf") + string(path.Separator)) os.Mkdir(path.Join(apppath, "controllers"), 0755) fmt.Println(path.Join(apppath, "controllers") + string(path.Separator)) + os.Mkdir(path.Join(apppath, "helpers"), 0755) + fmt.Println(path.Join(apppath, "helpers") + string(path.Separator)) os.Mkdir(path.Join(apppath, "models"), 0755) fmt.Println(path.Join(apppath, "models") + string(path.Separator)) os.Mkdir(path.Join(apppath, "routers"), 0755) fmt.Println(path.Join(apppath, "routers") + string(path.Separator)) os.Mkdir(path.Join(apppath, "tests"), 0755) fmt.Println(path.Join(apppath, "tests") + string(path.Separator)) + os.Mkdir(path.Join(apppath, "structures"), 0755) + fmt.Println(path.Join(apppath, "structures") + string(path.Separator)) os.Mkdir(path.Join(apppath, "static"), 0755) fmt.Println(path.Join(apppath, "static") + string(path.Separator)) os.Mkdir(path.Join(apppath, "static", "js"), 0755) @@ -135,8 +142,8 @@ func createApp(cmd *Command, args []string) int { fmt.Println(path.Join(apppath, "conf", "app.conf")) writetofile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", args[0], -1)) - fmt.Println(path.Join(apppath, "controllers", "default.go")) - writetofile(path.Join(apppath, "controllers", "default.go"), controllers) + fmt.Println(path.Join(apppath, "controllers", "index_controller.go")) + writetofile(path.Join(apppath, "controllers", "index_controller.go"), controllers) fmt.Println(path.Join(apppath, "views", "index.tpl")) writetofile(path.Join(apppath, "views", "index.tpl"), indextpl)