diff --git a/autorouter.go b/autorouter.go deleted file mode 100644 index 259f5da..0000000 --- a/autorouter.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2013 bee authors -// -// 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 ( - "bytes" - "errors" - "fmt" - "go/ast" - gobuild "go/build" - "go/doc" - "go/parser" - "go/token" - "io" - "io/ioutil" - "os" - "path" - "runtime" - "strings" - "time" -) - -var cmdRouter = &Command{ - UsageLine: "router", - Short: "auto-generate routers for the app controllers", - Long: ` - -`, -} - -func init() { - cmdRouter.Run = autoRouter -} - -func autoRouter(cmd *Command, args []string) int { - fmt.Println("[INFO] Starting auto-generating routers...") - return 0 -} - -// getControllerInfo returns controllers that embeded "beego.controller" -// and their methods of package in given path. -func getControllerInfo(path string) (map[string][]string, error) { - now := time.Now() - path = strings.TrimSuffix(path, "/") - dir, err := os.Open(path) - if err != nil { - return nil, err - } - - fis, err := dir.Readdir(0) - if err != nil { - return nil, err - } - - files := make([]*source, 0, len(fis)) - for _, fi := range fis { - // Only load Go files - if strings.HasSuffix(fi.Name(), ".go") { - f, err := os.Open(path + "/" + fi.Name()) - if err != nil { - return nil, err - } - - p := make([]byte, fi.Size()) - _, err = f.Read(p) - if err != nil { - return nil, err - } - - f.Close() - files = append(files, &source{ - name: path + "/" + fi.Name(), - data: p, - }) - } - } - - rw := &routerWalker{ - pdoc: &Package{ - ImportPath: path, - }, - } - - cm := make(map[string][]string) - pdoc, err := rw.build(files) - for _, t := range pdoc.Types { - // Check if embeded "beego.Controller". - if strings.Index(t.Decl, "beego.Controller") > -1 { - for _, f := range t.Methods { - cm[t.Name] = append(cm[t.Name], f.Name) - } - } - } - fmt.Println(time.Since(now)) - return cm, nil -} - -// source represents a source code file. -type source struct { - name string - data []byte -} - -func (s *source) Name() string { return s.name } -func (s *source) Size() int64 { return int64(len(s.data)) } -func (s *source) Mode() os.FileMode { return 0 } -func (s *source) ModTime() time.Time { return time.Time{} } -func (s *source) IsDir() bool { return false } -func (s *source) Sys() interface{} { return nil } - -// routerWalker holds the state used when building the documentation. -type routerWalker struct { - pdoc *Package - srcs map[string]*source // Source files - fset *token.FileSet - buf []byte // scratch space for printNode method -} - -// Package represents full information and documentation for a package. -type Package struct { - ImportPath string - Types []*Type // Top-level declarations -} - -// Type represents structs and interfaces. -type Type struct { - Name string // Type name - Decl string - Methods []*Func // Exported methods -} - -// Func represents functions -type Func struct { - Name string -} - -// build generates data from source files. -func (w *routerWalker) build(srcs []*source) (*Package, error) { - // Add source files to walker, I skipped references here - w.srcs = make(map[string]*source) - for _, src := range srcs { - w.srcs[src.name] = src - } - - w.fset = token.NewFileSet() - - // Find the package and associated files - ctxt := gobuild.Context{ - GOOS: runtime.GOOS, - GOARCH: runtime.GOARCH, - CgoEnabled: true, - JoinPath: path.Join, - IsAbsPath: path.IsAbs, - SplitPathList: func(list string) []string { return strings.Split(list, ":") }, - IsDir: func(path string) bool { panic("unexpected") }, - HasSubdir: func(root, dir string) (rel string, ok bool) { panic("unexpected") }, - ReadDir: func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) }, - OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, - Compiler: "gc", - } - - bpkg, err := ctxt.ImportDir(w.pdoc.ImportPath, 0) - // Continue if there are no Go source files; we still want the directory info - _, nogo := err.(*gobuild.NoGoError) - if err != nil { - if nogo { - err = nil - } else { - return nil, errors.New("routerWalker.build -> " + err.Error()) - } - } - - // Parse the Go files - files := make(map[string]*ast.File) - for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) { - file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments) - if err != nil { - return nil, errors.New("routerWalker.build -> parse go files: " + err.Error()) - } - files[name] = file - } - - apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil) - - mode := doc.Mode(0) - if w.pdoc.ImportPath == "builtin" { - mode |= doc.AllDecls - } - - pdoc := doc.New(apkg, w.pdoc.ImportPath, mode) - - w.pdoc.Types = w.types(pdoc.Types) - - return w.pdoc, err -} - -func (w *routerWalker) funcs(fdocs []*doc.Func) []*Func { - var result []*Func - for _, d := range fdocs { - result = append(result, &Func{ - Name: d.Name, - }) - } - return result -} - -func (w *routerWalker) types(tdocs []*doc.Type) []*Type { - var result []*Type - for _, d := range tdocs { - result = append(result, &Type{ - Decl: w.printDecl(d.Decl), - Name: d.Name, - Methods: w.funcs(d.Methods), - }) - } - return result -} - -func (w *routerWalker) printDecl(decl ast.Node) string { - var d Code - d, w.buf = printDecl(decl, w.fset, w.buf) - return d.Text -} - -func (w *routerWalker) readDir(dir string) ([]os.FileInfo, error) { - if dir != w.pdoc.ImportPath { - panic("unexpected") - } - fis := make([]os.FileInfo, 0, len(w.srcs)) - for _, src := range w.srcs { - fis = append(fis, src) - } - return fis, nil -} - -func (w *routerWalker) openFile(path string) (io.ReadCloser, error) { - if strings.HasPrefix(path, w.pdoc.ImportPath+"/") { - if src, ok := w.srcs[path[len(w.pdoc.ImportPath)+1:]]; ok { - return ioutil.NopCloser(bytes.NewReader(src.data)), nil - } - } - panic("unexpected") -} - -func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { - pkg := imports[path] - if pkg == nil { - // Guess the package name without importing it. Start with the last - // element of the path. - name := path[strings.LastIndex(path, "/")+1:] - - // Trim commonly used prefixes and suffixes containing illegal name - // runes. - name = strings.TrimSuffix(name, ".go") - name = strings.TrimSuffix(name, "-go") - name = strings.TrimPrefix(name, "go.") - name = strings.TrimPrefix(name, "go-") - name = strings.TrimPrefix(name, "biogo.") - - // It's also common for the last element of the path to contain an - // extra "go" prefix, but not always. - // TODO: examine unresolved ids to detect when trimming the "go" prefix is appropriate. - pkg = ast.NewObj(ast.Pkg, name) - pkg.Data = ast.NewScope(nil) - imports[path] = pkg - } - return pkg, nil -} diff --git a/autorouter_test.go b/autorouter_test.go deleted file mode 100644 index 437f3ee..0000000 --- a/autorouter_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "testing" -) - -func TestGetControllerInfo(t *testing.T) { - getControllerInfo("testdata/router/") -} diff --git a/bee.go b/bee.go deleted file mode 100644 index 690bc07..0000000 --- a/bee.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2013 bee authors -// -// 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. - -// Bee is a tool for developing applications based on beego framework. -package main - -import ( - "flag" - "fmt" - "io" - "log" - "os" - "strings" - "text/template" -) - -const version = "1.8.0" - -// Command is the unit of execution -type Command struct { - // Run runs the command. - // The args are the arguments after the command name. - Run func(cmd *Command, args []string) int - - // PreRun performs an operation before running the command - PreRun func(cmd *Command, args []string) - - // UsageLine is the one-line usage message. - // The first word in the line is taken to be the command name. - UsageLine string - - // Short is the short description shown in the 'go help' output. - Short string - - // Long is the long message shown in the 'go help ' output. - Long string - - // Flag is a set of flags specific to this command. - Flag flag.FlagSet - - // CustomFlags indicates that the command will do its own - // flag parsing. - CustomFlags bool - - // output out writer if set in SetOutput(w) - output *io.Writer -} - -// Name returns the command's name: the first word in the usage line. -func (c *Command) Name() string { - name := c.UsageLine - i := strings.Index(name, " ") - if i >= 0 { - name = name[:i] - } - return name -} - -// SetOutput sets the destination for usage and error messages. -// If output is nil, os.Stderr is used. -func (c *Command) SetOutput(output io.Writer) { - c.output = &output -} - -// Out returns the out writer of the current command. -// If cmd.output is nil, os.Stderr is used. -func (c *Command) Out() io.Writer { - if c.output != nil { - return *c.output - } - return NewColorWriter(os.Stderr) -} - -// Usage puts out the usage for the command. -func (c *Command) Usage() { - tmpl(cmdUsage, c) - os.Exit(2) -} - -// Runnable reports whether the command can be run; otherwise -// it is a documentation pseudo-command such as import path. -func (c *Command) Runnable() bool { - return c.Run != nil -} - -func (c *Command) Options() map[string]string { - options := make(map[string]string) - c.Flag.VisitAll(func(f *flag.Flag) { - defaultVal := f.DefValue - if len(defaultVal) > 0 { - options[f.Name+"="+defaultVal] = f.Usage - } else { - options[f.Name] = f.Usage - } - }) - return options -} - -var availableCommands = []*Command{ - cmdNew, - cmdRun, - cmdPack, - cmdApiapp, - cmdHproseapp, - //cmdRouter, - //cmdTest, - cmdBale, - cmdVersion, - cmdGenerate, - //cmdRundocs, - cmdMigrate, - cmdFix, - cmdDockerize, -} - -var logger = GetBeeLogger(os.Stdout) - -func main() { - currentpath, _ := os.Getwd() - - flag.Usage = usage - flag.Parse() - log.SetFlags(0) - - args := flag.Args() - - if len(args) < 1 { - usage() - } - - if args[0] == "help" { - help(args[1:]) - return - } - - for _, cmd := range availableCommands { - if cmd.Name() == args[0] && cmd.Run != nil { - cmd.Flag.Usage = func() { cmd.Usage() } - if cmd.CustomFlags { - args = args[1:] - } else { - cmd.Flag.Parse(args[1:]) - args = cmd.Flag.Args() - } - - if cmd.PreRun != nil { - cmd.PreRun(cmd, args) - } - - // Check if current directory is inside the GOPATH, - // if so parse the packages inside it. - if strings.Contains(currentpath, GetGOPATHs()[0]+"/src") && isGenerateDocs(cmd.Name(), args) { - parsePackagesFromDir(currentpath) - } - - os.Exit(cmd.Run(cmd, args)) - return - } - } - - printErrorAndExit("Unknown subcommand") -} - -func isGenerateDocs(name string, args []string) bool { - if name != "generate" { - return false - } - for _, a := range args { - if a == "docs" { - return true - } - } - return false -} - -var usageTemplate = `Bee is a Fast and Flexible tool for managing your Beego Web Application. - -{{"USAGE" | headline}} - {{"bee command [arguments]" | bold}} - -{{"AVAILABLE COMMANDS" | headline}} -{{range .}}{{if .Runnable}} - {{.Name | printf "%-11s" | bold}} {{.Short}}{{end}}{{end}} - -Use {{"bee help [command]" | bold}} for more information about a command. - -{{"ADDITIONAL HELP TOPICS" | headline}} -{{range .}}{{if not .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} - -Use {{"bee help [topic]" | bold}} for more information about that topic. -` - -var helpTemplate = `{{"USAGE" | headline}} - {{.UsageLine | printf "bee %s" | bold}} -{{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} - {{$k | printf "-%s" | bold}} - {{$v}} - {{end}}{{end}} -{{"DESCRIPTION" | headline}} - {{tmpltostr .Long . | trim}} -` - -var errorTemplate = `bee: %s. -Use {{"bee help" | bold}} for more information. -` - -var cmdUsage = `Use {{printf "bee help %s" .Name | bold}} for more information.{{endline}}` - -func usage() { - tmpl(usageTemplate, availableCommands) - os.Exit(2) -} - -func tmpl(text string, data interface{}) { - output := NewColorWriter(os.Stderr) - - t := template.New("usage").Funcs(BeeFuncMap()) - template.Must(t.Parse(text)) - - err := t.Execute(output, data) - MustCheck(err) -} - -func help(args []string) { - if len(args) == 0 { - usage() - } - if len(args) != 1 { - printErrorAndExit("Too many arguments") - } - - arg := args[0] - - for _, cmd := range availableCommands { - if cmd.Name() == arg { - tmpl(helpTemplate, cmd) - return - } - } - printErrorAndExit("Unknown help topic") -} - -func printErrorAndExit(message string) { - tmpl(fmt.Sprintf(errorTemplate, message), nil) - os.Exit(2) -} diff --git a/cmd/bee.go b/cmd/bee.go new file mode 100644 index 0000000..93a75cf --- /dev/null +++ b/cmd/bee.go @@ -0,0 +1,102 @@ +// Copyright 2013 bee authors +// +// 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. + +// Bee is a tool for developing applications based on beego framework. +package cmd + +import ( + "os" + + "github.com/beego/bee/cmd/commands" + _ "github.com/beego/bee/cmd/commands/api" + _ "github.com/beego/bee/cmd/commands/bale" + _ "github.com/beego/bee/cmd/commands/beefix" + _ "github.com/beego/bee/cmd/commands/dockerize" + _ "github.com/beego/bee/cmd/commands/generate" + _ "github.com/beego/bee/cmd/commands/hprose" + _ "github.com/beego/bee/cmd/commands/migrate" + _ "github.com/beego/bee/cmd/commands/new" + _ "github.com/beego/bee/cmd/commands/pack" + _ "github.com/beego/bee/cmd/commands/run" + _ "github.com/beego/bee/cmd/commands/version" + "github.com/beego/bee/utils" +) + +func IfGenerateDocs(name string, args []string) bool { + if name != "generate" { + return false + } + for _, a := range args { + if a == "docs" { + return true + } + } + return false +} + +var usageTemplate = `Bee is a Fast and Flexible tool for managing your Beego Web Application. + +{{"USAGE" | headline}} + {{"bee command [arguments]" | bold}} + +{{"AVAILABLE COMMANDS" | headline}} +{{range .}}{{if .Runnable}} + {{.Name | printf "%-11s" | bold}} {{.Short}}{{end}}{{end}} + +Use {{"bee help [command]" | bold}} for more information about a command. + +{{"ADDITIONAL HELP TOPICS" | headline}} +{{range .}}{{if not .Runnable}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use {{"bee help [topic]" | bold}} for more information about that topic. +` + +var helpTemplate = `{{"USAGE" | headline}} + {{.UsageLine | printf "bee %s" | bold}} +{{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} + {{$k | printf "-%s" | bold}} + {{$v}} + {{end}}{{end}} +{{"DESCRIPTION" | headline}} + {{tmpltostr .Long . | trim}} +` + +var ErrorTemplate = `bee: %s. +Use {{"bee help" | bold}} for more information. +` + +func Usage() { + utils.Tmpl(usageTemplate, commands.AvailableCommands) + os.Exit(2) +} + +func Help(args []string) { + if len(args) == 0 { + Usage() + } + if len(args) != 1 { + utils.PrintErrorAndExit("Too many arguments", ErrorTemplate) + } + + arg := args[0] + + for _, cmd := range commands.AvailableCommands { + if cmd.Name() == arg { + utils.Tmpl(helpTemplate, cmd) + return + } + } + utils.PrintErrorAndExit("Unknown help topic", ErrorTemplate) +} diff --git a/apiapp.go b/cmd/commands/api/apiapp.go similarity index 84% rename from apiapp.go rename to cmd/commands/api/apiapp.go index 3e2a9be..26e33e5 100644 --- a/apiapp.go +++ b/cmd/commands/api/apiapp.go @@ -12,16 +12,22 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package apiapp import ( "fmt" "os" path "path/filepath" "strings" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + "github.com/beego/bee/generate" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" ) -var cmdApiapp = &Command{ +var CmdApiapp = &commands.Command{ // CustomFlags: true, UsageLine: "api [appname]", Short: "Creates a Beego API application", @@ -50,10 +56,9 @@ var cmdApiapp = &Command{ └── object.go └── user.go `, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, Run: createapi, } - var apiconf = `appname = {{.Appname}} httpport = 8080 runmode = dev @@ -134,7 +139,7 @@ func init() { } ` -var apiModels = `package models +var ApiModels = `package models import ( "errors" @@ -189,7 +194,7 @@ func Delete(ObjectId string) { ` -var apiModels2 = `package models +var ApiModels2 = `package models import ( "errors" @@ -532,33 +537,32 @@ func TestGet(t *testing.T) { ` func init() { - cmdApiapp.Flag.Var(&tables, "tables", "List of table names separated by a comma.") - cmdApiapp.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.") - cmdApiapp.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.") + CmdApiapp.Flag.Var(&generate.Tables, "tables", "List of table names separated by a comma.") + CmdApiapp.Flag.Var(&generate.SQLDriver, "driver", "Database driver. Either mysql, postgres or sqlite.") + CmdApiapp.Flag.Var(&generate.SQLConn, "conn", "Connection string used by the driver to connect to a database instance.") + commands.AvailableCommands = append(commands.AvailableCommands, CmdApiapp) } -func createapi(cmd *Command, args []string) int { +func createapi(cmd *commands.Command, args []string) int { output := cmd.Out() if len(args) < 1 { - logger.Fatal("Argument [appname] is missing") + beeLogger.Log.Fatal("Argument [appname] is missing") } if len(args) > 1 { cmd.Flag.Parse(args[1:]) } - apppath, packpath, err := checkEnv(args[0]) + apppath, packpath, err := utils.CheckEnv(args[0]) if err != nil { - logger.Fatalf("%s", err) + beeLogger.Log.Fatalf("%s", err) } - if driver == "" { - driver = "mysql" - } - if conn == "" { + if generate.SQLDriver == "" { + generate.SQLDriver = "mysql" } - logger.Info("Creating API...") + beeLogger.Log.Info("Creating API...") os.MkdirAll(apppath, 0755) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") @@ -569,30 +573,30 @@ func createapi(cmd *Command, args []string) int { os.Mkdir(path.Join(apppath, "tests"), 0755) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests"), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") - WriteToFile(path.Join(apppath, "conf", "app.conf"), + utils.WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(apiconf, "{{.Appname}}", path.Base(args[0]), -1)) - if conn != "" { + if generate.SQLConn != "" { fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") maingoContent := strings.Replace(apiMainconngo, "{{.Appname}}", packpath, -1) - maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1) - if driver == "mysql" { + maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(generate.SQLDriver), -1) + if generate.SQLDriver == "mysql" { maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1) - } else if driver == "postgres" { + } else if generate.SQLDriver == "postgres" { maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1) } - WriteToFile(path.Join(apppath, "main.go"), + utils.WriteToFile(path.Join(apppath, "main.go"), strings.Replace( maingoContent, "{{.conn}}", - conn.String(), + generate.SQLConn.String(), -1, ), ) - logger.Infof("Using '%s' as 'driver'", driver) - logger.Infof("Using '%s' as 'conn'", conn) - logger.Infof("Using '%s' as 'tables'", tables) - generateAppcode(string(driver), string(conn), "3", string(tables), apppath) + beeLogger.Log.Infof("Using '%s' as 'driver'", generate.SQLDriver) + beeLogger.Log.Infof("Using '%s' as 'conn'", generate.SQLConn) + beeLogger.Log.Infof("Using '%s' as 'tables'", generate.Tables) + generate.GenerateAppcode(string(generate.SQLDriver), string(generate.SQLConn), "3", string(generate.Tables), apppath) } else { os.Mkdir(path.Join(apppath, "models"), 0755) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") @@ -600,65 +604,31 @@ func createapi(cmd *Command, args []string) int { fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "object.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "controllers", "object.go"), + utils.WriteToFile(path.Join(apppath, "controllers", "object.go"), strings.Replace(apiControllers, "{{.Appname}}", packpath, -1)) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "user.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "controllers", "user.go"), + utils.WriteToFile(path.Join(apppath, "controllers", "user.go"), strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1)) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "tests", "default_test.go"), + utils.WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(apiTests, "{{.Appname}}", packpath, -1)) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "routers", "router.go"), + utils.WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(apirouter, "{{.Appname}}", packpath, -1)) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "models", "object.go"), apiModels) + utils.WriteToFile(path.Join(apppath, "models", "object.go"), ApiModels) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2) + utils.WriteToFile(path.Join(apppath, "models", "user.go"), ApiModels2) fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "main.go"), + utils.WriteToFile(path.Join(apppath, "main.go"), strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1)) } - logger.Success("New API successfully created!") + beeLogger.Log.Success("New API successfully created!") return 0 } - -func checkEnv(appname string) (apppath, packpath string, err error) { - gps := GetGOPATHs() - if len(gps) == 0 { - logger.Fatal("GOPATH environment variable is not set or empty") - } - currpath, _ := os.Getwd() - currpath = path.Join(currpath, appname) - for _, gpath := range gps { - gsrcpath := path.Join(gpath, "src") - if strings.HasPrefix(currpath, gsrcpath) { - packpath = strings.Replace(currpath[len(gsrcpath)+1:], string(path.Separator), "/", -1) - return currpath, packpath, nil - } - } - - // In case of multiple paths in the GOPATH, by default - // we use the first path - gopath := gps[0] - - logger.Warn("You current workdir is not inside $GOPATH/src.") - logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) - - gosrcpath := path.Join(gopath, "src") - apppath = path.Join(gosrcpath, appname) - - if _, e := os.Stat(apppath); os.IsNotExist(e) == false { - err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath) - logger.Errorf("Path '%s' already exists", apppath) - return - } - packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/") - return -} diff --git a/bale.go b/cmd/commands/bale/bale.go similarity index 80% rename from bale.go rename to cmd/commands/bale/bale.go index 2cd6667..d086dd3 100644 --- a/bale.go +++ b/cmd/commands/bale/bale.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package bale import ( "bytes" @@ -24,9 +24,15 @@ import ( "path/filepath" "runtime" "strings" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + "github.com/beego/bee/config" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" ) -var cmdBale = &Command{ +var CmdBale = &commands.Command{ UsageLine: "bale", Short: "Transforms non-Go files to Go source files", Long: `Bale command compress all the static files in to a single binary file. @@ -37,47 +43,46 @@ var cmdBale = &Command{ It will auto-generate an unpack function to the main package then run it during the runtime. This is mainly used for zealots who are requiring 100% Go code. `, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, Run: runBale, } -func runBale(cmd *Command, args []string) int { - err := loadConfig() - if err != nil { - logger.Fatalf("Failed to load configuration: %s", err) - } +func init() { + commands.AvailableCommands = append(commands.AvailableCommands, CmdBale) +} +func runBale(cmd *commands.Command, args []string) int { os.RemoveAll("bale") os.Mkdir("bale", os.ModePerm) // Pack and compress data - for _, p := range conf.Bale.Dirs { - if !isExist(p) { - logger.Warnf("Skipped directory: %s", p) + for _, p := range config.Conf.Bale.Dirs { + if !utils.IsExist(p) { + beeLogger.Log.Warnf("Skipped directory: %s", p) continue } - logger.Infof("Packaging directory: %s", p) + beeLogger.Log.Infof("Packaging directory: %s", p) filepath.Walk(p, walkFn) } // Generate auto-uncompress function. buf := new(bytes.Buffer) - buf.WriteString(fmt.Sprintf(BaleHeader, conf.Bale.Import, + buf.WriteString(fmt.Sprintf(BaleHeader, config.Conf.Bale.Import, strings.Join(resFiles, "\",\n\t\t\""), strings.Join(resFiles, ",\n\t\tbale.R"))) fw, err := os.Create("bale.go") if err != nil { - logger.Fatalf("Failed to create file: %s", err) + beeLogger.Log.Fatalf("Failed to create file: %s", err) } defer fw.Close() _, err = fw.Write(buf.Bytes()) if err != nil { - logger.Fatalf("Failed to write data: %s", err) + beeLogger.Log.Fatalf("Failed to write data: %s", err) } - logger.Success("Baled resources successfully!") + beeLogger.Log.Success("Baled resources successfully!") return 0 } @@ -146,7 +151,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error { // Open resource files fr, err := os.Open(resPath) if err != nil { - logger.Fatalf("Failed to read file: %s", err) + beeLogger.Log.Fatalf("Failed to read file: %s", err) } // Convert path @@ -164,7 +169,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error { os.MkdirAll(path.Dir(resPath), os.ModePerm) fw, err := os.Create("bale/" + resPath + ".go") if err != nil { - logger.Fatalf("Failed to create file: %s", err) + beeLogger.Log.Fatalf("Failed to create file: %s", err) } defer fw.Close() @@ -184,7 +189,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error { } func filterSuffix(name string) bool { - for _, s := range conf.Bale.IngExt { + for _, s := range config.Conf.Bale.IngExt { if strings.HasSuffix(name, s) { return true } diff --git a/fix.go b/cmd/commands/beefix/fix.go similarity index 91% rename from fix.go rename to cmd/commands/beefix/fix.go index 13453e6..1345bf0 100644 --- a/fix.go +++ b/cmd/commands/beefix/fix.go @@ -1,4 +1,4 @@ -package main +package beefix import ( "fmt" @@ -9,9 +9,14 @@ import ( "path/filepath" "regexp" "strings" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" ) -var cmdFix = &Command{ +var CmdFix = &commands.Command{ UsageLine: "fix", Short: "Fixes your application by making it compatible with newer versions of Beego", Long: `As of {{"Beego 1.6"|bold}}, there are some backward compatibility issues. @@ -22,18 +27,19 @@ var cmdFix = &Command{ } func init() { - cmdFix.Run = runFix - cmdFix.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } + CmdFix.Run = runFix + CmdFix.PreRun = func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() } + commands.AvailableCommands = append(commands.AvailableCommands, CmdFix) } -func runFix(cmd *Command, args []string) int { +func runFix(cmd *commands.Command, args []string) int { output := cmd.Out() - logger.Info("Upgrading the application...") + beeLogger.Log.Info("Upgrading the application...") dir, err := os.Getwd() if err != nil { - logger.Fatalf("Error while getting the current working directory: %s", err) + beeLogger.Log.Fatalf("Error while getting the current working directory: %s", err) } filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { @@ -50,13 +56,13 @@ func runFix(cmd *Command, args []string) int { return nil } err = fixFile(path) - fmt.Fprintf(output, GreenBold("\tfix\t")+"%s\n", path) + fmt.Fprintf(output, colors.GreenBold("\tfix\t")+"%s\n", path) if err != nil { - logger.Errorf("Could not fix file: %s", err) + beeLogger.Log.Errorf("Could not fix file: %s", err) } return err }) - logger.Success("Upgrade Done!") + beeLogger.Log.Success("Upgrade Done!") return 0 } diff --git a/cmd/commands/command.go b/cmd/commands/command.go new file mode 100644 index 0000000..cab663f --- /dev/null +++ b/cmd/commands/command.go @@ -0,0 +1,94 @@ +package commands + +import ( + "flag" + "io" + "os" + "strings" + + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" +) + +// Command is the unit of execution +type Command struct { + // Run runs the command. + // The args are the arguments after the command name. + Run func(cmd *Command, args []string) int + + // PreRun performs an operation before running the command + PreRun func(cmd *Command, args []string) + + // UsageLine is the one-line Usage message. + // The first word in the line is taken to be the command name. + UsageLine string + + // Short is the short description shown in the 'go help' output. + Short string + + // Long is the long message shown in the 'go help ' output. + Long string + + // Flag is a set of flags specific to this command. + Flag flag.FlagSet + + // CustomFlags indicates that the command will do its own + // flag parsing. + CustomFlags bool + + // output out writer if set in SetOutput(w) + output *io.Writer +} + +var AvailableCommands = []*Command{} +var cmdUsage = `Use {{printf "bee help %s" .Name | bold}} for more information.{{endline}}` + +// Name returns the command's name: the first word in the Usage line. +func (c *Command) Name() string { + name := c.UsageLine + i := strings.Index(name, " ") + if i >= 0 { + name = name[:i] + } + return name +} + +// SetOutput sets the destination for Usage and error messages. +// If output is nil, os.Stderr is used. +func (c *Command) SetOutput(output io.Writer) { + c.output = &output +} + +// Out returns the out writer of the current command. +// If cmd.output is nil, os.Stderr is used. +func (c *Command) Out() io.Writer { + if c.output != nil { + return *c.output + } + return colors.NewColorWriter(os.Stderr) +} + +// Usage puts out the Usage for the command. +func (c *Command) Usage() { + utils.Tmpl(cmdUsage, c) + os.Exit(2) +} + +// Runnable reports whether the command can be run; otherwise +// it is a documentation pseudo-command such as import path. +func (c *Command) Runnable() bool { + return c.Run != nil +} + +func (c *Command) Options() map[string]string { + options := make(map[string]string) + c.Flag.VisitAll(func(f *flag.Flag) { + defaultVal := f.DefValue + if len(defaultVal) > 0 { + options[f.Name+"="+defaultVal] = f.Usage + } else { + options[f.Name] = f.Usage + } + }) + return options +} diff --git a/dockerize.go b/cmd/commands/dockerize/dockerize.go similarity index 77% rename from dockerize.go rename to cmd/commands/dockerize/dockerize.go index 827fc8e..4e07a17 100644 --- a/dockerize.go +++ b/cmd/commands/dockerize/dockerize.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package dockerize import ( "flag" @@ -21,22 +21,13 @@ import ( "path/filepath" "strings" "text/template" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" ) -var cmdDockerize = &Command{ - CustomFlags: true, - UsageLine: "dockerize", - Short: "Generates a Dockerfile for your Beego application", - Long: `Dockerize generates a Dockerfile for your Beego Web Application. - The Dockerfile will compile, get the dependencies with {{"godep"|bold}}, and set the entrypoint. - - {{"Example:"|bold}} - $ bee dockerize -expose="3000,80,25" - `, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, - Run: dockerizeApp, -} - const dockerBuildTemplate = `FROM {{.BaseImage}} # Godep for vendoring @@ -67,6 +58,20 @@ type Dockerfile struct { Expose string } +var CmdDockerize = &commands.Command{ + CustomFlags: true, + UsageLine: "dockerize", + Short: "Generates a Dockerfile for your Beego application", + Long: `Dockerize generates a Dockerfile for your Beego Web Application. + The Dockerfile will compile, get the dependencies with {{"godep"|bold}}, and set the entrypoint. + + {{"Example:"|bold}} + $ bee dockerize -expose="3000,80,25" + `, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, + Run: dockerizeApp, +} + var ( expose string baseImage string @@ -76,19 +81,22 @@ func init() { fs := flag.NewFlagSet("dockerize", flag.ContinueOnError) fs.StringVar(&baseImage, "image", "library/golang", "Set the base image of the Docker container.") fs.StringVar(&expose, "expose", "8080", "Port(s) to expose in the Docker container.") - cmdDockerize.Flag = *fs + CmdDockerize.Flag = *fs + commands.AvailableCommands = append(commands.AvailableCommands, CmdDockerize) } -func dockerizeApp(cmd *Command, args []string) int { +func dockerizeApp(cmd *commands.Command, args []string) int { if err := cmd.Flag.Parse(args); err != nil { - logger.Fatalf("Error parsing flags: %v", err.Error()) + beeLogger.Log.Fatalf("Error parsing flags: %v", err.Error()) } - logger.Info("Generating Dockerfile...") + beeLogger.Log.Info("Generating Dockerfile...") gopath := os.Getenv("GOPATH") dir, err := filepath.Abs(".") - MustCheck(err) + if err != nil { + beeLogger.Log.Error(err.Error()) + } appdir := strings.Replace(dir, gopath, "", 1) @@ -112,15 +120,15 @@ func dockerizeApp(cmd *Command, args []string) int { } func generateDockerfile(df Dockerfile) { - t := template.Must(template.New("dockerBuildTemplate").Parse(dockerBuildTemplate)).Funcs(BeeFuncMap()) + t := template.Must(template.New("dockerBuildTemplate").Parse(dockerBuildTemplate)).Funcs(utils.BeeFuncMap()) f, err := os.Create("Dockerfile") if err != nil { - logger.Fatalf("Error writing Dockerfile: %v", err.Error()) + beeLogger.Log.Fatalf("Error writing Dockerfile: %v", err.Error()) } - defer CloseFile(f) + defer utils.CloseFile(f) t.Execute(f, df) - logger.Success("Dockerfile generated.") + beeLogger.Log.Success("Dockerfile generated.") } diff --git a/cmd/commands/generate/generate.go b/cmd/commands/generate/generate.go new file mode 100644 index 0000000..f679514 --- /dev/null +++ b/cmd/commands/generate/generate.go @@ -0,0 +1,203 @@ +package generate + +import ( + "os" + "strings" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + "github.com/beego/bee/config" + "github.com/beego/bee/generate" + "github.com/beego/bee/generate/swaggergen" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" +) + +var CmdGenerate = &commands.Command{ + UsageLine: "generate [command]", + Short: "Source code generator", + Long: `▶ {{"To scaffold out your entire application:"|bold}} + + $ bee generate scaffold [scaffoldname] [-fields="title:string,body:text"] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] + + ▶ {{"To generate a Model based on fields:"|bold}} + + $ bee generate model [modelname] [-fields="name:type"] + + ▶ {{"To generate a controller:"|bold}} + + $ bee generate controller [controllerfile] + + ▶ {{"To generate a CRUD view:"|bold}} + + $ bee generate view [viewpath] + + ▶ {{"To generate a migration file for making database schema updates:"|bold}} + + $ bee generate migration [migrationfile] [-fields="name:type"] + + ▶ {{"To generate swagger doc file:"|bold}} + + $ bee generate docs + + ▶ {{"To generate a test case:"|bold}} + + $ bee generate test [routerfile] + + ▶ {{"To generate appcode based on an existing database:"|bold}} + + $ bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3] +`, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, + Run: GenerateCode, +} + +func init() { + CmdGenerate.Flag.Var(&generate.Tables, "Tables", "List of table names separated by a comma.") + CmdGenerate.Flag.Var(&generate.SQLDriver, "SQLDriver", "Database SQLDriver. Either mysql, postgres or sqlite.") + CmdGenerate.Flag.Var(&generate.SQLConn, "SQLConn", "Connection string used by the SQLDriver to connect to a database instance.") + CmdGenerate.Flag.Var(&generate.Level, "Level", "Either 1, 2 or 3. i.e. 1=models; 2=models and controllers; 3=models, controllers and routers.") + CmdGenerate.Flag.Var(&generate.Fields, "Fields", "List of table Fields.") + commands.AvailableCommands = append(commands.AvailableCommands, CmdGenerate) +} + +func GenerateCode(cmd *commands.Command, args []string) int { + currpath, _ := os.Getwd() + if len(args) < 1 { + beeLogger.Log.Fatal("Command is missing") + } + + gps := utils.GetGOPATHs() + if len(gps) == 0 { + beeLogger.Log.Fatal("GOPATH environment variable is not set or empty") + } + + gopath := gps[0] + + beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath) + + gcmd := args[0] + switch gcmd { + case "scaffold": + scaffold(cmd, args, currpath) + case "docs": + swaggergen.GenerateDocs(currpath) + case "appcode": + appCode(cmd, args, currpath) + case "migration": + migration(cmd, args, currpath) + case "controller": + controller(args, currpath) + case "model": + model(cmd, args, currpath) + case "view": + view(args, currpath) + default: + beeLogger.Log.Fatal("Command is missing") + } + beeLogger.Log.Successf("%s successfully generated!", strings.Title(gcmd)) + return 0 +} + +func scaffold(cmd *commands.Command, args []string, currpath string) { + if len(args) < 2 { + beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") + } + + cmd.Flag.Parse(args[2:]) + if generate.SQLDriver == "" { + generate.SQLDriver = utils.DocValue(config.Conf.Database.Driver) + if generate.SQLDriver == "" { + generate.SQLDriver = "mysql" + } + } + if generate.SQLConn == "" { + generate.SQLConn = utils.DocValue(config.Conf.Database.Conn) + if generate.SQLConn == "" { + generate.SQLConn = "root:@tcp(127.0.0.1:3306)/test" + } + } + if generate.Fields == "" { + beeLogger.Log.Hint("Fields option should not be empty, i.e. -Fields=\"title:string,body:text\"") + beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") + } + sname := args[1] + generate.GenerateScaffold(sname, generate.Fields.String(), currpath, generate.SQLDriver.String(), generate.SQLConn.String()) +} + +func appCode(cmd *commands.Command, args []string, currpath string) { + cmd.Flag.Parse(args[1:]) + if generate.SQLDriver == "" { + generate.SQLDriver = utils.DocValue(config.Conf.Database.Driver) + if generate.SQLDriver == "" { + generate.SQLDriver = "mysql" + } + } + if generate.SQLConn == "" { + generate.SQLConn = utils.DocValue(config.Conf.Database.Conn) + if generate.SQLConn == "" { + if generate.SQLDriver == "mysql" { + generate.SQLConn = "root:@tcp(127.0.0.1:3306)/test" + } else if generate.SQLDriver == "postgres" { + generate.SQLConn = "postgres://postgres:postgres@127.0.0.1:5432/postgres" + } + } + } + if generate.Level == "" { + generate.Level = "3" + } + beeLogger.Log.Infof("Using '%s' as 'SQLDriver'", generate.SQLDriver) + beeLogger.Log.Infof("Using '%s' as 'SQLConn'", generate.SQLConn) + beeLogger.Log.Infof("Using '%s' as 'Tables'", generate.Tables) + beeLogger.Log.Infof("Using '%s' as 'Level'", generate.Level) + generate.GenerateAppcode(generate.SQLDriver.String(), generate.SQLConn.String(), generate.Level.String(), generate.Tables.String(), currpath) +} +func migration(cmd *commands.Command, args []string, currpath string) { + if len(args) < 2 { + beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") + } + cmd.Flag.Parse(args[2:]) + mname := args[1] + + beeLogger.Log.Infof("Using '%s' as migration name", mname) + + upsql := "" + downsql := "" + if generate.Fields != "" { + dbMigrator := generate.NewDBDriver() + upsql = dbMigrator.GenerateCreateUp(mname) + downsql = dbMigrator.GenerateCreateDown(mname) + } + generate.GenerateMigration(mname, upsql, downsql, currpath) +} + +func controller(args []string, currpath string) { + if len(args) == 2 { + cname := args[1] + generate.GenerateController(cname, currpath) + } else { + beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") + } +} + +func model(cmd *commands.Command, args []string, currpath string) { + if len(args) < 2 { + beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") + } + cmd.Flag.Parse(args[2:]) + if generate.Fields == "" { + beeLogger.Log.Hint("Fields option should not be empty, i.e. -Fields=\"title:string,body:text\"") + beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") + } + sname := args[1] + generate.GenerateModel(sname, generate.Fields.String(), currpath) +} + +func view(args []string, currpath string) { + if len(args) == 2 { + cname := args[1] + generate.GenerateView(cname, currpath) + } else { + beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") + } +} diff --git a/cmd/commands/hprose/hprose.go b/cmd/commands/hprose/hprose.go new file mode 100644 index 0000000..c77b13e --- /dev/null +++ b/cmd/commands/hprose/hprose.go @@ -0,0 +1,119 @@ +package hprose + +import ( + "os" + + "fmt" + "path" + "strings" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/api" + "github.com/beego/bee/cmd/commands/version" + "github.com/beego/bee/generate" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" +) + +var CmdHproseapp = &commands.Command{ + // CustomFlags: true, + UsageLine: "hprose [appname]", + Short: "Creates an RPC application based on Hprose and Beego frameworks", + Long: ` + The command 'hprose' creates an RPC application based on both Beego and Hprose (http://hprose.com/). + + {{"To scaffold out your application, use:"|bold}} + + $ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test] + + If 'conn' is empty, the command will generate a sample application. Otherwise the command + will connect to your database and generate models based on the existing tables. + + The command 'hprose' creates a folder named [appname] with the following structure: + + ├── main.go + ├── {{"conf"|foldername}} + │ └── app.conf + └── {{"models"|foldername}} + └── object.go + └── user.go +`, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, + Run: createhprose, +} + +func init() { + CmdHproseapp.Flag.Var(&generate.Tables, "tables", "List of table names separated by a comma.") + CmdHproseapp.Flag.Var(&generate.SQLDriver, "driver", "Database driver. Either mysql, postgres or sqlite.") + CmdHproseapp.Flag.Var(&generate.SQLConn, "conn", "Connection string used by the driver to connect to a database instance.") + commands.AvailableCommands = append(commands.AvailableCommands, CmdHproseapp) +} + +func createhprose(cmd *commands.Command, args []string) int { + output := cmd.Out() + + if len(args) != 1 { + beeLogger.Log.Fatal("Argument [appname] is missing") + } + + curpath, _ := os.Getwd() + if len(args) > 1 { + cmd.Flag.Parse(args[1:]) + } + apppath, packpath, err := utils.CheckEnv(args[0]) + if err != nil { + beeLogger.Log.Fatalf("%s", err) + } + if generate.SQLDriver == "" { + generate.SQLDriver = "mysql" + } + beeLogger.Log.Info("Creating Hprose application...") + + os.MkdirAll(apppath, 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") + os.Mkdir(path.Join(apppath, "conf"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "conf", "app.conf"), + strings.Replace(generate.Hproseconf, "{{.Appname}}", args[0], -1)) + + if generate.SQLConn != "" { + beeLogger.Log.Infof("Using '%s' as 'driver'", generate.SQLDriver) + beeLogger.Log.Infof("Using '%s' as 'conn'", generate.SQLConn) + beeLogger.Log.Infof("Using '%s' as 'tables'", generate.Tables) + generate.GenerateHproseAppcode(string(generate.SQLDriver), string(generate.SQLConn), "1", string(generate.Tables), path.Join(curpath, args[0])) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + maingoContent := strings.Replace(generate.HproseMainconngo, "{{.Appname}}", packpath, -1) + maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(generate.SQLDriver), -1) + maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(generate.HproseAddFunctions, ""), -1) + if generate.SQLDriver == "mysql" { + maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1) + } else if generate.SQLDriver == "postgres" { + maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1) + } + utils.WriteToFile(path.Join(apppath, "main.go"), + strings.Replace( + maingoContent, + "{{.conn}}", + generate.SQLConn.String(), + -1, + ), + ) + } else { + os.Mkdir(path.Join(apppath, "models"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "models", "object.go"), apiapp.ApiModels) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "models", "user.go"), apiapp.ApiModels2) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "main.go"), + strings.Replace(generate.HproseMaingo, "{{.Appname}}", packpath, -1)) + } + beeLogger.Log.Success("New Hprose application successfully created!") + return 0 +} diff --git a/migrate.go b/cmd/commands/migrate/migrate.go similarity index 71% rename from migrate.go rename to cmd/commands/migrate/migrate.go index 5ef4681..17ae222 100644 --- a/migrate.go +++ b/cmd/commands/migrate/migrate.go @@ -1,18 +1,4 @@ -// Copyright 2013 bee authors -// -// 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 +package migrate import ( "database/sql" @@ -23,9 +9,16 @@ import ( "strconv" "strings" "time" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + "github.com/beego/bee/config" + "github.com/beego/bee/utils" + + beeLogger "github.com/beego/bee/logger" ) -var cmdMigrate = &Command{ +var CmdMigrate = &commands.Command{ UsageLine: "migrate [Command]", Short: "Runs database migrations", Long: `The command 'migrate' allows you to run database migrations to keep it up-to-date. @@ -46,100 +39,75 @@ var cmdMigrate = &Command{ $ bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] `, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, - Run: runMigration, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, + Run: RunMigration, } -var mDriver docValue -var mConn docValue +var mDriver utils.DocValue +var mConn utils.DocValue func init() { - cmdMigrate.Flag.Var(&mDriver, "driver", "Database driver. Either mysql, postgres or sqlite.") - cmdMigrate.Flag.Var(&mConn, "conn", "Connection string used by the driver to connect to a database instance.") + CmdMigrate.Flag.Var(&mDriver, "driver", "Database driver. Either mysql, postgres or sqlite.") + CmdMigrate.Flag.Var(&mConn, "conn", "Connection string used by the driver to connect to a database instance.") + commands.AvailableCommands = append(commands.AvailableCommands, CmdMigrate) } // runMigration is the entry point for starting a migration -func runMigration(cmd *Command, args []string) int { +func RunMigration(cmd *commands.Command, args []string) int { currpath, _ := os.Getwd() - gps := GetGOPATHs() + gps := utils.GetGOPATHs() if len(gps) == 0 { - logger.Fatal("GOPATH environment variable is not set or empty") + beeLogger.Log.Fatal("GOPATH environment variable is not set or empty") } gopath := gps[0] - logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) - - // Load the configuration - err := loadConfig() - if err != nil { - logger.Errorf("Failed to load configuration: %s", err) - } + beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath) // Getting command line arguments if len(args) != 0 { cmd.Flag.Parse(args[1:]) } if mDriver == "" { - mDriver = docValue(conf.Database.Driver) + mDriver = utils.DocValue(config.Conf.Database.Driver) if mDriver == "" { mDriver = "mysql" } } if mConn == "" { - mConn = docValue(conf.Database.Conn) + mConn = utils.DocValue(config.Conf.Database.Conn) if mConn == "" { mConn = "root:@tcp(127.0.0.1:3306)/test" } } - logger.Infof("Using '%s' as 'driver'", mDriver) - logger.Infof("Using '%s' as 'conn'", mConn) + beeLogger.Log.Infof("Using '%s' as 'driver'", mDriver) + beeLogger.Log.Infof("Using '%s' as 'conn'", mConn) driverStr, connStr := string(mDriver), string(mConn) if len(args) == 0 { // run all outstanding migrations - logger.Info("Running all outstanding migrations") - migrateUpdate(currpath, driverStr, connStr) + beeLogger.Log.Info("Running all outstanding migrations") + MigrateUpdate(currpath, driverStr, connStr) } else { mcmd := args[0] switch mcmd { case "rollback": - logger.Info("Rolling back the last migration operation") - migrateRollback(currpath, driverStr, connStr) + beeLogger.Log.Info("Rolling back the last migration operation") + MigrateRollback(currpath, driverStr, connStr) case "reset": - logger.Info("Reseting all migrations") - migrateReset(currpath, driverStr, connStr) + beeLogger.Log.Info("Reseting all migrations") + MigrateReset(currpath, driverStr, connStr) case "refresh": - logger.Info("Refreshing all migrations") - migrateRefresh(currpath, driverStr, connStr) + beeLogger.Log.Info("Refreshing all migrations") + MigrateRefresh(currpath, driverStr, connStr) default: - logger.Fatal("Command is missing") + beeLogger.Log.Fatal("Command is missing") } } - logger.Success("Migration successful!") + beeLogger.Log.Success("Migration successful!") return 0 } -// migrateUpdate does the schema update -func migrateUpdate(currpath, driver, connStr string) { - migrate("upgrade", currpath, driver, connStr) -} - -// migrateRollback rolls back the latest migration -func migrateRollback(currpath, driver, connStr string) { - migrate("rollback", currpath, driver, connStr) -} - -// migrateReset rolls back all migrations -func migrateReset(currpath, driver, connStr string) { - migrate("reset", currpath, driver, connStr) -} - -// migrationRefresh rolls back all migrations and start over again -func migrateRefresh(currpath, driver, connStr string) { - migrate("refresh", currpath, driver, connStr) -} - // migrate generates source code, build it, and invoke the binary who does the actual migration func migrate(goal, currpath, driver, connStr string) { dir := path.Join(currpath, "database", "migrations") @@ -153,7 +121,7 @@ func migrate(goal, currpath, driver, connStr string) { // Connect to database db, err := sql.Open(driver, connStr) if err != nil { - logger.Fatalf("Could not connect to database using '%s': %s", connStr, err) + beeLogger.Log.Fatalf("Could not connect to database using '%s': %s", connStr, err) } defer db.Close() @@ -171,44 +139,44 @@ func migrate(goal, currpath, driver, connStr string) { func checkForSchemaUpdateTable(db *sql.DB, driver string) { showTableSQL := showMigrationsTableSQL(driver) if rows, err := db.Query(showTableSQL); err != nil { - logger.Fatalf("Could not show migrations table: %s", err) + beeLogger.Log.Fatalf("Could not show migrations table: %s", err) } else if !rows.Next() { // No migrations table, create new ones createTableSQL := createMigrationsTableSQL(driver) - logger.Infof("Creating 'migrations' table...") + beeLogger.Log.Infof("Creating 'migrations' table...") if _, err := db.Query(createTableSQL); err != nil { - logger.Fatalf("Could not create migrations table: %s", err) + beeLogger.Log.Fatalf("Could not create migrations table: %s", err) } } // Checking that migrations table schema are expected selectTableSQL := selectMigrationsTableSQL(driver) if rows, err := db.Query(selectTableSQL); err != nil { - logger.Fatalf("Could not show columns of migrations table: %s", err) + beeLogger.Log.Fatalf("Could not show columns of migrations table: %s", err) } else { for rows.Next() { var fieldBytes, typeBytes, nullBytes, keyBytes, defaultBytes, extraBytes []byte if err := rows.Scan(&fieldBytes, &typeBytes, &nullBytes, &keyBytes, &defaultBytes, &extraBytes); err != nil { - logger.Fatalf("Could not read column information: %s", err) + beeLogger.Log.Fatalf("Could not read column information: %s", err) } fieldStr, typeStr, nullStr, keyStr, defaultStr, extraStr := string(fieldBytes), string(typeBytes), string(nullBytes), string(keyBytes), string(defaultBytes), string(extraBytes) if fieldStr == "id_migration" { if keyStr != "PRI" || extraStr != "auto_increment" { - logger.Hint("Expecting KEY: PRI, EXTRA: auto_increment") - logger.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr) + beeLogger.Log.Hint("Expecting KEY: PRI, EXTRA: auto_increment") + beeLogger.Log.Fatalf("Column migration.id_migration type mismatch: KEY: %s, EXTRA: %s", keyStr, extraStr) } } else if fieldStr == "name" { if !strings.HasPrefix(typeStr, "varchar") || nullStr != "YES" { - logger.Hint("Expecting TYPE: varchar, NULL: YES") - logger.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr) + beeLogger.Log.Hint("Expecting TYPE: varchar, NULL: YES") + beeLogger.Log.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr) } } else if fieldStr == "created_at" { if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" { - logger.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP") - logger.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr) + beeLogger.Log.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP") + beeLogger.Log.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr) } } } @@ -252,22 +220,22 @@ func selectMigrationsTableSQL(driver string) string { func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) { sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1" if rows, err := db.Query(sql); err != nil { - logger.Fatalf("Could not retrieve migrations: %s", err) + beeLogger.Log.Fatalf("Could not retrieve migrations: %s", err) } else { if rows.Next() { if err := rows.Scan(&file); err != nil { - logger.Fatalf("Could not read migrations in database: %s", err) + beeLogger.Log.Fatalf("Could not read migrations in database: %s", err) } createdAtStr := file[len(file)-15:] if t, err := time.Parse("20060102_150405", createdAtStr); err != nil { - logger.Fatalf("Could not parse time: %s", err) + beeLogger.Log.Fatalf("Could not parse time: %s", err) } else { createdAt = t.Unix() } } else { // migration table has no 'update' record, no point rolling back if goal == "rollback" { - logger.Fatal("There is nothing to rollback") + beeLogger.Log.Fatal("There is nothing to rollback") } file, createdAt = "", 0 } @@ -279,7 +247,7 @@ func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime int64, latestName string, task string) { changeDir(dir) if f, err := os.OpenFile(source, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err != nil { - logger.Fatalf("Could not create file: %s", err) + beeLogger.Log.Fatalf("Could not create file: %s", err) } else { content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1) content = strings.Replace(content, "{{ConnStr}}", connStr, -1) @@ -287,9 +255,9 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in content = strings.Replace(content, "{{LatestName}}", latestName, -1) content = strings.Replace(content, "{{Task}}", task, -1) if _, err := f.WriteString(content); err != nil { - logger.Fatalf("Could not write to file: %s", err) + beeLogger.Log.Fatalf("Could not write to file: %s", err) } - CloseFile(f) + utils.CloseFile(f) } } @@ -298,7 +266,7 @@ func buildMigrationBinary(dir, binary string) { changeDir(dir) cmd := exec.Command("go", "build", "-o", binary) if out, err := cmd.CombinedOutput(); err != nil { - logger.Errorf("Could not build migration binary: %s", err) + beeLogger.Log.Errorf("Could not build migration binary: %s", err) formatShellErrOutput(string(out)) removeTempFile(dir, binary) removeTempFile(dir, binary+".go") @@ -312,7 +280,7 @@ func runMigrationBinary(dir, binary string) { cmd := exec.Command("./" + binary) if out, err := cmd.CombinedOutput(); err != nil { formatShellOutput(string(out)) - logger.Errorf("Could not run migration binary: %s", err) + beeLogger.Log.Errorf("Could not run migration binary: %s", err) removeTempFile(dir, binary) removeTempFile(dir, binary+".go") os.Exit(2) @@ -325,7 +293,7 @@ func runMigrationBinary(dir, binary string) { // It exits the system when encouter an error func changeDir(dir string) { if err := os.Chdir(dir); err != nil { - logger.Fatalf("Could not find migration directory: %s", err) + beeLogger.Log.Fatalf("Could not find migration directory: %s", err) } } @@ -333,7 +301,7 @@ func changeDir(dir string) { func removeTempFile(dir, file string) { changeDir(dir) if err := os.Remove(file); err != nil { - logger.Warnf("Could not remove temporary file: %s", err) + beeLogger.Log.Warnf("Could not remove temporary file: %s", err) } } @@ -341,7 +309,7 @@ func removeTempFile(dir, file string) { func formatShellErrOutput(o string) { for _, line := range strings.Split(o, "\n") { if line != "" { - logger.Errorf("|> %s", line) + beeLogger.Log.Errorf("|> %s", line) } } } @@ -350,7 +318,7 @@ func formatShellErrOutput(o string) { func formatShellOutput(o string) { for _, line := range strings.Split(o, "\n") { if line != "" { - logger.Infof("|> %s", line) + beeLogger.Log.Infof("|> %s", line) } } } @@ -421,3 +389,23 @@ CREATE TABLE migrations ( status migrations_status )` ) + +// MigrateUpdate does the schema update +func MigrateUpdate(currpath, driver, connStr string) { + migrate("upgrade", currpath, driver, connStr) +} + +// MigrateRollback rolls back the latest migration +func MigrateRollback(currpath, driver, connStr string) { + migrate("rollback", currpath, driver, connStr) +} + +// MigrateReset rolls back all migrations +func MigrateReset(currpath, driver, connStr string) { + migrate("reset", currpath, driver, connStr) +} + +// migrationRefresh rolls back all migrations and start over again +func MigrateRefresh(currpath, driver, connStr string) { + migrate("refresh", currpath, driver, connStr) +} diff --git a/new.go b/cmd/commands/new/new.go similarity index 97% rename from new.go rename to cmd/commands/new/new.go index 6eb9e73..299f094 100644 --- a/new.go +++ b/cmd/commands/new/new.go @@ -12,16 +12,22 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package new import ( "fmt" "os" path "path/filepath" "strings" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" ) -var cmdNew = &Command{ +var CmdNew = &commands.Command{ UsageLine: "new [appname]", Short: "Creates a Beego application", Long: ` @@ -47,74 +53,8 @@ Creates a Beego application for the given app name in the current directory. └── index.tpl `, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, - Run: createApp, -} - -func createApp(cmd *Command, args []string) int { - output := cmd.Out() - if len(args) != 1 { - logger.Fatal("Argument [appname] is missing") - } - - apppath, packpath, err := checkEnv(args[0]) - if err != nil { - logger.Fatalf("%s", err) - } - - if isExist(apppath) { - logger.Errorf(bold("Application '%s' already exists"), apppath) - logger.Warn(bold("Do you want to overwrite it? [Yes|No] ")) - if !askForConfirmation() { - os.Exit(2) - } - } - - logger.Info("Creating application...") - - os.MkdirAll(apppath, 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "conf"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "controllers"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "models"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "routers"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "tests"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "static"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "static", "js"), 0755) - WriteToFile(path.Join(apppath, "static", "js", "reload.min.js"), reloadJsClient) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "static", "css"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "static", "img"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "img")+string(path.Separator), "\x1b[0m") - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views")+string(path.Separator), "\x1b[0m") - os.Mkdir(path.Join(apppath, "views"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") - WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", path.Base(args[0]), -1)) - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "default.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "controllers", "default.go"), controllers) - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views", "index.tpl"), "\x1b[0m") - WriteToFile(path.Join(apppath, "views", "index.tpl"), indextpl) - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(router, "{{.Appname}}", packpath, -1)) - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(test, "{{.Appname}}", packpath, -1)) - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packpath, -1)) - - logger.Success("New application successfully created!") - return 0 + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, + Run: CreateApp, } var appconf = `appname = {{.Appname}} @@ -303,3 +243,73 @@ var indextpl = ` var reloadJsClient = `function b(a){var c=new WebSocket(a);c.onclose=function(){setTimeout(function(){b(a)},2E3)};c.onmessage=function(){location.reload()}}try{if(window.WebSocket)try{b("ws://localhost:12450/reload")}catch(a){console.error(a)}else console.log("Your browser does not support WebSockets.")}catch(a){console.error("Exception during connecting to Reload:",a)}; ` + +func init() { + commands.AvailableCommands = append(commands.AvailableCommands, CmdNew) +} + +func CreateApp(cmd *commands.Command, args []string) int { + output := cmd.Out() + if len(args) != 1 { + beeLogger.Log.Fatal("Argument [appname] is missing") + } + + apppath, packpath, err := utils.CheckEnv(args[0]) + if err != nil { + beeLogger.Log.Fatalf("%s", err) + } + + if utils.IsExist(apppath) { + beeLogger.Log.Errorf(colors.Bold("Application '%s' already exists"), apppath) + beeLogger.Log.Warn(colors.Bold("Do you want to overwrite it? [Yes|No] ")) + if !utils.AskForConfirmation() { + os.Exit(2) + } + } + + beeLogger.Log.Info("Creating application...") + + os.MkdirAll(apppath, 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "conf"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "controllers"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "models"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "routers"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "tests"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "static"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "static", "js"), 0755) + utils.WriteToFile(path.Join(apppath, "static", "js", "reload.min.js"), reloadJsClient) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "static", "css"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "static", "img"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "img")+string(path.Separator), "\x1b[0m") + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views")+string(path.Separator), "\x1b[0m") + os.Mkdir(path.Join(apppath, "views"), 0755) + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", path.Base(args[0]), -1)) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "default.go"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "controllers", "default.go"), controllers) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "views", "index.tpl"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "views", "index.tpl"), indextpl) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(router, "{{.Appname}}", packpath, -1)) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(test, "{{.Appname}}", packpath, -1)) + + fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") + utils.WriteToFile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", packpath, -1)) + + beeLogger.Log.Success("New application successfully created!") + return 0 +} diff --git a/pack.go b/cmd/commands/pack/pack.go similarity index 83% rename from pack.go rename to cmd/commands/pack/pack.go index c957585..ee7eced 100644 --- a/pack.go +++ b/cmd/commands/pack/pack.go @@ -1,18 +1,4 @@ -// Copyright 2013 bee authors -// -// 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 +package pack import ( "archive/tar" @@ -31,9 +17,14 @@ import ( "strings" "syscall" "time" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" ) -var cmdPack = &Command{ +var CmdPack = &commands.Command{ CustomFlags: true, UsageLine: "pack", Short: "Compresses a Beego application into a single file", @@ -43,7 +34,7 @@ var cmdPack = &Command{ {{"Example:"|bold}} $ bee pack -v -ba="-ldflags '-s -w'" `, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, Run: packApp, } @@ -52,27 +43,16 @@ var ( excludeP string excludeS string outputP string - excludeR ListOpts + excludeR utils.ListOpts fsym bool ssym bool build bool buildArgs string - buildEnvs ListOpts + buildEnvs utils.ListOpts verbose bool format string ) -type ListOpts []string - -func (opts *ListOpts) String() string { - return fmt.Sprint(*opts) -} - -func (opts *ListOpts) Set(value string) error { - *opts = append(*opts, value) - return nil -} - func init() { fs := flag.NewFlagSet("pack", flag.ContinueOnError) fs.StringVar(&appPath, "p", "", "Set the application path. Defaults to the current path.") @@ -87,7 +67,8 @@ func init() { fs.BoolVar(&fsym, "fs", false, "Tell the command to follow symlinks. Defaults to false.") fs.BoolVar(&ssym, "ss", false, "Tell the command to skip symlinks. Defaults to false.") fs.BoolVar(&verbose, "v", false, "Be more verbose during the operation. Defaults to false.") - cmdPack.Flag = *fs + CmdPack.Flag = *fs + commands.AvailableCommands = append(commands.AvailableCommands, CmdPack) } type walker interface { @@ -115,10 +96,6 @@ type walkFileTree struct { output *io.Writer } -func (wft *walkFileTree) setPrefix(prefix string) { - wft.prefix = prefix -} - func (wft *walkFileTree) isExclude(fPath string) bool { if fPath == "" { return true @@ -316,12 +293,12 @@ func (wft *tarWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) { return false, err } - if isSym == false { + if !isSym { fr, err := os.Open(fpath) if err != nil { return false, err } - defer CloseFile(fr) + defer utils.CloseFile(fr) _, err = io.Copy(tw, fr) if err != nil { return false, err @@ -352,12 +329,12 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) { return false, err } - if isSym == false { + if !isSym { fr, err := os.Open(fpath) if err != nil { return false, err } - defer CloseFile(fr) + defer utils.CloseFile(fr) _, err = io.Copy(w, fr) if err != nil { return false, err @@ -379,10 +356,10 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) { func packDirectory(output io.Writer, excludePrefix []string, excludeSuffix []string, excludeRegexp []*regexp.Regexp, includePath ...string) (err error) { - logger.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":")) - logger.Infof("Excluding relpath suffix: %s", strings.Join(excludeSuffix, ":")) + beeLogger.Log.Infof("Excluding relpath prefix: %s", strings.Join(excludePrefix, ":")) + beeLogger.Log.Infof("Excluding relpath suffix: %s", strings.Join(excludeSuffix, ":")) if len(excludeRegexp) > 0 { - logger.Infof("Excluding filename regex: `%s`", strings.Join(excludeR, "`, `")) + beeLogger.Log.Infof("Excluding filename regex: `%s`", strings.Join(excludeR, "`, `")) } w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) @@ -437,7 +414,7 @@ func packDirectory(output io.Writer, excludePrefix []string, excludeSuffix []str return } -func packApp(cmd *Command, args []string) int { +func packApp(cmd *commands.Command, args []string) int { output := cmd.Out() curPath, _ := os.Getwd() thePath := "" @@ -454,19 +431,19 @@ func packApp(cmd *Command, args []string) int { } cmd.Flag.Parse(nArgs) - if path.IsAbs(appPath) == false { + if !path.IsAbs(appPath) { appPath = path.Join(curPath, appPath) } thePath, err := path.Abs(appPath) if err != nil { - logger.Fatalf("Wrong application path: %s", thePath) + beeLogger.Log.Fatalf("Wrong application path: %s", thePath) } - if stat, err := os.Stat(thePath); os.IsNotExist(err) || stat.IsDir() == false { - logger.Fatalf("Application path does not exist: %s", thePath) + if stat, err := os.Stat(thePath); os.IsNotExist(err) || !stat.IsDir() { + beeLogger.Log.Fatalf("Application path does not exist: %s", thePath) } - logger.Infof("Packaging application on '%s'...", thePath) + beeLogger.Log.Infof("Packaging application on '%s'...", thePath) appName := path.Base(thePath) @@ -488,12 +465,12 @@ func packApp(cmd *Command, args []string) int { // Remove the tmpdir once bee pack is done err := os.RemoveAll(tmpdir) if err != nil { - logger.Error("Failed to remove the generated temp dir") + beeLogger.Log.Error("Failed to remove the generated temp dir") } }() if build { - logger.Info("Building application...") + beeLogger.Log.Info("Building application...") var envs []string for _, env := range buildEnvs { parts := strings.SplitN(env, "=", 2) @@ -515,7 +492,7 @@ func packApp(cmd *Command, args []string) int { os.Setenv("GOOS", goos) os.Setenv("GOARCH", goarch) - logger.Infof("Using: GOOS=%s GOARCH=%s", goos, goarch) + beeLogger.Log.Infof("Using: GOOS=%s GOARCH=%s", goos, goarch) binPath := path.Join(tmpdir, appName) if goos == "windows" { @@ -538,10 +515,10 @@ func packApp(cmd *Command, args []string) int { execmd.Dir = thePath err = execmd.Run() if err != nil { - logger.Fatal(err.Error()) + beeLogger.Log.Fatal(err.Error()) } - logger.Success("Build Successful!") + beeLogger.Log.Success("Build Successful!") } switch format { @@ -552,14 +529,14 @@ func packApp(cmd *Command, args []string) int { outputN := appName + "." + format - if outputP == "" || path.IsAbs(outputP) == false { + if outputP == "" || !path.IsAbs(outputP) { outputP = path.Join(curPath, outputP) } if _, err := os.Stat(outputP); err != nil { err = os.MkdirAll(outputP, 0755) if err != nil { - logger.Fatal(err.Error()) + beeLogger.Log.Fatal(err.Error()) } } @@ -581,20 +558,20 @@ func packApp(cmd *Command, args []string) int { for _, r := range excludeR { if len(r) > 0 { if re, err := regexp.Compile(r); err != nil { - logger.Fatal(err.Error()) + beeLogger.Log.Fatal(err.Error()) } else { exr = append(exr, re) } } } - logger.Infof("Writing to output: %s", outputP) + beeLogger.Log.Infof("Writing to output: %s", outputP) err = packDirectory(output, exp, exs, exr, tmpdir, thePath) if err != nil { - logger.Fatal(err.Error()) + beeLogger.Log.Fatal(err.Error()) } - logger.Success("Application packed!") + beeLogger.Log.Success("Application packed!") return 0 } diff --git a/cmd/commands/run/docs.go b/cmd/commands/run/docs.go new file mode 100644 index 0000000..28979a0 --- /dev/null +++ b/cmd/commands/run/docs.go @@ -0,0 +1,88 @@ +package run + +import ( + "archive/zip" + "io" + "net/http" + "os" + "strings" + + beeLogger "github.com/beego/bee/logger" +) + +var ( + swaggerVersion = "2" + swaggerlink = "https://github.com/beego/swagger/archive/v" + swaggerVersion + ".zip" +) + +func downloadFromURL(url, fileName string) { + var down bool + if fd, err := os.Stat(fileName); err != nil && os.IsNotExist(err) { + down = true + } else if fd.Size() == int64(0) { + down = true + } else { + beeLogger.Log.Infof("'%s' already exists", fileName) + return + } + if down { + beeLogger.Log.Infof("Downloading '%s' to '%s'...", url, fileName) + output, err := os.Create(fileName) + if err != nil { + beeLogger.Log.Errorf("Error while creating '%s': %s", fileName, err) + return + } + defer output.Close() + + response, err := http.Get(url) + if err != nil { + beeLogger.Log.Errorf("Error while downloading '%s': %s", url, err) + return + } + defer response.Body.Close() + + n, err := io.Copy(output, response.Body) + if err != nil { + beeLogger.Log.Errorf("Error while downloading '%s': %s", url, err) + return + } + beeLogger.Log.Successf("%d bytes downloaded!", n) + } +} + +func unzipAndDelete(src string) error { + beeLogger.Log.Infof("Unzipping '%s'...", src) + r, err := zip.OpenReader(src) + if err != nil { + return err + } + defer r.Close() + + rp := strings.NewReplacer("swagger-"+swaggerVersion, "swagger") + for _, f := range r.File { + rc, err := f.Open() + if err != nil { + return err + } + defer rc.Close() + + fname := rp.Replace(f.Name) + if f.FileInfo().IsDir() { + os.MkdirAll(fname, f.Mode()) + } else { + f, err := os.OpenFile( + fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + defer f.Close() + + _, err = io.Copy(f, rc) + if err != nil { + return err + } + } + } + beeLogger.Log.Successf("Done! Deleting '%s'...", src) + return os.RemoveAll(src) +} diff --git a/reload.go b/cmd/commands/run/reload.go similarity index 92% rename from reload.go rename to cmd/commands/run/reload.go index c7d3c0d..4cd731a 100644 --- a/reload.go +++ b/cmd/commands/run/reload.go @@ -11,13 +11,14 @@ // 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 +package run import ( "bytes" "net/http" "time" + beeLogger "github.com/beego/bee/logger" "github.com/gorilla/websocket" ) @@ -70,7 +71,7 @@ func (c *wsClient) readPump() { _, _, err := c.conn.ReadMessage() if err != nil { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { - logger.Errorf("An error happened when reading from the Websocket client: %v", err) + beeLogger.Log.Errorf("An error happened when reading from the Websocket client: %v", err) } break } @@ -109,7 +110,7 @@ func (c *wsClient) writePump() { n := len(c.send) for i := 0; i < n; i++ { - w.Write(newline) + w.Write([]byte("/n")) w.Write(<-c.send) } @@ -155,13 +156,13 @@ func startReloadServer() { }) go startServer() - logger.Infof("Reload server listening at %s", reloadAddress) + beeLogger.Log.Infof("Reload server listening at %s", reloadAddress) } func startServer() { err := http.ListenAndServe(reloadAddress, nil) if err != nil { - logger.Errorf("Failed to start up the Reload server: %v", err) + beeLogger.Log.Errorf("Failed to start up the Reload server: %v", err) return } } @@ -175,7 +176,7 @@ func sendReload(payload string) { func handleWsRequest(broker *wsBroker, w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - logger.Errorf("error while upgrading server connection: %v", err) + beeLogger.Log.Errorf("error while upgrading server connection: %v", err) return } diff --git a/run.go b/cmd/commands/run/run.go similarity index 62% rename from run.go rename to cmd/commands/run/run.go index 267b3f4..72fbbbc 100644 --- a/run.go +++ b/cmd/commands/run/run.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package run import ( "io/ioutil" @@ -20,25 +20,31 @@ import ( path "path/filepath" "runtime" "strings" + + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/cmd/commands/version" + "github.com/beego/bee/config" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" ) -var cmdRun = &Command{ +var CmdRun = &commands.Command{ UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-ex=extraPackageToWatch] [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]", Short: "Run the application by starting a local development server", Long: ` Run command will supervise the filesystem of the application for any changes, and recompile/restart it. `, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, - Run: runApp, + PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }, + Run: RunApp, } var ( - mainFiles ListOpts - downdoc docValue - gendoc docValue + mainFiles utils.ListOpts + downdoc utils.DocValue + gendoc utils.DocValue // The flags list of the paths excluded from watching - excludedPaths strFlags + excludedPaths utils.StrFlags // Pass through to -tags arg of "go build" buildTags string // Application path @@ -54,66 +60,63 @@ var ( // Current runmode runmode string // Extra directories - extraPackages strFlags + extraPackages utils.StrFlags ) +var started = make(chan bool) func init() { - cmdRun.Flag.Var(&mainFiles, "main", "Specify main go files.") - cmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.") - cmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.") - cmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.") - cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.") - cmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/") - cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.") - cmdRun.Flag.Var(&extraPackages, "ex", "List of extra package to watch.") + CmdRun.Flag.Var(&mainFiles, "main", "Specify main go files.") + CmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.") + CmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.") + CmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.") + CmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.") + CmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/") + CmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.") + CmdRun.Flag.Var(&extraPackages, "ex", "List of extra package to watch.") exit = make(chan bool) + commands.AvailableCommands = append(commands.AvailableCommands, CmdRun) } -func runApp(cmd *Command, args []string) int { +func RunApp(cmd *commands.Command, args []string) int { if len(args) == 0 || args[0] == "watchall" { currpath, _ = os.Getwd() - if found, _gopath, _ := SearchGOPATHs(currpath); found { + if found, _gopath, _ := utils.SearchGOPATHs(currpath); found { appname = path.Base(currpath) currentGoPath = _gopath } else { - logger.Fatalf("No application '%s' found in your GOPATH", currpath) + beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", currpath) } } else { // Check if passed Bee application path/name exists in the GOPATH(s) - if found, _gopath, _path := SearchGOPATHs(args[0]); found { + if found, _gopath, _path := utils.SearchGOPATHs(args[0]); found { currpath = _path currentGoPath = _gopath appname = path.Base(currpath) } else { - logger.Fatalf("No application '%s' found in your GOPATH", args[0]) + beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", args[0]) } - if strings.HasSuffix(appname, ".go") && isExist(currpath) { - logger.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname) - logger.Info("Do you want to overwrite it? [yes|no] ") - if !askForConfirmation() { + if strings.HasSuffix(appname, ".go") && utils.IsExist(currpath) { + beeLogger.Log.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname) + beeLogger.Log.Info("Do you want to overwrite it? [yes|no] ") + if !utils.AskForConfirmation() { return 0 } } } - logger.Infof("Using '%s' as 'appname'", appname) + beeLogger.Log.Infof("Using '%s' as 'appname'", appname) - logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath) + beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), currpath) if runmode == "prod" || runmode == "dev" { os.Setenv("BEEGO_RUNMODE", runmode) - logger.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) + beeLogger.Log.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) } else if runmode != "" { os.Setenv("BEEGO_RUNMODE", runmode) - logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) + beeLogger.Log.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) } else if os.Getenv("BEEGO_RUNMODE") != "" { - logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) - } - - err := loadConfig() - if err != nil { - logger.Fatalf("Failed to load configuration: %s", err) + beeLogger.Log.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE")) } var paths []string @@ -121,17 +124,17 @@ func runApp(cmd *Command, args []string) int { // Because monitor files has some issues, we watch current directory // and ignore non-go files. - for _, p := range conf.DirStruct.Others { + for _, p := range config.Conf.DirStruct.Others { paths = append(paths, strings.Replace(p, "$GOPATH", currentGoPath, -1)) } if len(extraPackages) > 0 { // get the full path for _, packagePath := range extraPackages { - if found, _, _fullPath := SearchGOPATHs(packagePath); found { + if found, _, _fullPath := utils.SearchGOPATHs(packagePath); found { readAppDirectories(_fullPath, &paths) } else { - logger.Warnf("No extra package '%s' found in your GOPATH", packagePath) + beeLogger.Log.Warnf("No extra package '%s' found in your GOPATH", packagePath) } } // let paths unique @@ -163,7 +166,7 @@ func runApp(cmd *Command, args []string) int { } // Start the Reload server (if enabled) - if conf.EnableReload { + if config.Conf.EnableReload { startReloadServer() } if gendoc == "true" { @@ -205,16 +208,16 @@ func readAppDirectories(directory string, paths *[]string) { continue } - if fileInfo.IsDir() == true && fileInfo.Name()[0] != '.' { + if fileInfo.IsDir() && fileInfo.Name()[0] != '.' { readAppDirectories(directory+"/"+fileInfo.Name(), paths) continue } - if useDirectory == true { + if useDirectory { continue } - if path.Ext(fileInfo.Name()) == ".go" || (ifStaticFile(fileInfo.Name()) && conf.EnableReload) { + if path.Ext(fileInfo.Name()) == ".go" || (ifStaticFile(fileInfo.Name()) && config.Conf.EnableReload) { *paths = append(*paths, directory) useDirectory = true } @@ -227,16 +230,16 @@ func isExcluded(filePath string) bool { for _, p := range excludedPaths { absP, err := path.Abs(p) if err != nil { - logger.Errorf("Cannot get absolute path of '%s'", p) + beeLogger.Log.Errorf("Cannot get absolute path of '%s'", p) continue } absFilePath, err := path.Abs(filePath) if err != nil { - logger.Errorf("Cannot get absolute path of '%s'", filePath) + beeLogger.Log.Errorf("Cannot get absolute path of '%s'", filePath) break } if strings.HasPrefix(absFilePath, absP) { - logger.Infof("'%s' is not being watched", filePath) + beeLogger.Log.Infof("'%s' is not being watched", filePath) return true } } diff --git a/watch.go b/cmd/commands/run/watch.go similarity index 76% rename from watch.go rename to cmd/commands/run/watch.go index 9b1daa4..4452b61 100644 --- a/watch.go +++ b/cmd/commands/run/watch.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package run import ( "bytes" @@ -24,6 +24,10 @@ import ( "sync" "time" + "github.com/beego/bee/config" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" "github.com/fsnotify/fsnotify" ) @@ -46,7 +50,7 @@ var ( func NewWatcher(paths []string, files []string, isgenerate bool) { watcher, err := fsnotify.NewWatcher() if err != nil { - logger.Fatalf("Failed to create watcher: %s", err) + beeLogger.Log.Fatalf("Failed to create watcher: %s", err) } go func() { @@ -55,7 +59,7 @@ func NewWatcher(paths []string, files []string, isgenerate bool) { case e := <-watcher.Events: isBuild := true - if ifStaticFile(e.Name) && conf.EnableReload { + if ifStaticFile(e.Name) && config.Conf.EnableReload { sendReload(e.String()) continue } @@ -69,14 +73,14 @@ func NewWatcher(paths []string, files []string, isgenerate bool) { mt := getFileModTime(e.Name) if t := eventTime[e.Name]; mt == t { - logger.Infof(bold("Skipping: ")+"%s", e.String()) + beeLogger.Log.Infof(colors.Bold("Skipping: ")+"%s", e.String()) isBuild = false } eventTime[e.Name] = mt if isBuild { - logger.Infof("Event fired: %s", e) + beeLogger.Log.Infof("Event fired: %s", e) go func() { // Wait 1s before autobuild until there is no file change. scheduleTime = time.Now().Add(1 * time.Second) @@ -85,17 +89,17 @@ func NewWatcher(paths []string, files []string, isgenerate bool) { }() } case err := <-watcher.Errors: - logger.Warnf("Watcher error: %s", err.Error()) // No need to exit here + beeLogger.Log.Warnf("Watcher error: %s", err.Error()) // No need to exit here } } }() - logger.Info("Initializing watcher...") + beeLogger.Log.Info("Initializing watcher...") for _, path := range paths { - logger.Infof(bold("Watching: ")+"%s", path) + beeLogger.Log.Infof(colors.Bold("Watching: ")+"%s", path) err = watcher.Add(path) if err != nil { - logger.Fatalf("Failed to watch directory: %s", err) + beeLogger.Log.Fatalf("Failed to watch directory: %s", err) } } } @@ -105,14 +109,14 @@ func getFileModTime(path string) int64 { path = strings.Replace(path, "\\", "/", -1) f, err := os.Open(path) if err != nil { - logger.Errorf("Failed to open file on '%s': %s", path, err) + beeLogger.Log.Errorf("Failed to open file on '%s': %s", path, err) return time.Now().Unix() } defer f.Close() fi, err := f.Stat() if err != nil { - logger.Errorf("Failed to get file stats: %s", err) + beeLogger.Log.Errorf("Failed to get file stats: %s", err) return time.Now().Unix() } @@ -127,7 +131,7 @@ func AutoBuild(files []string, isgenerate bool) { os.Chdir(currpath) cmdName := "go" - if conf.Gopm.Enable { + if config.Conf.Gopm.Enable { cmdName = "gopm" } @@ -138,14 +142,14 @@ func AutoBuild(files []string, isgenerate bool) { ) // For applications use full import path like "github.com/.../.." // are able to use "go install" to reduce build time. - if conf.GoInstall { + if config.Conf.GoInstall { icmd := exec.Command(cmdName, "install", "-v") icmd.Stdout = os.Stdout icmd.Stderr = os.Stderr icmd.Env = append(os.Environ(), "GOGC=off") icmd.Run() } - if conf.Gopm.Install { + if config.Conf.Gopm.Install { icmd := exec.Command("go", "list", "./...") icmd.Stdout = &stdout icmd.Env = append(os.Environ(), "GOGC=off") @@ -169,15 +173,15 @@ func AutoBuild(files []string, isgenerate bool) { } if isgenerate { - logger.Info("Generating the docs...") + beeLogger.Log.Info("Generating the docs...") icmd := exec.Command("bee", "generate", "docs") icmd.Env = append(os.Environ(), "GOGC=off") err = icmd.Run() if err != nil { - logger.Errorf("Failed to generate the docs.") + beeLogger.Log.Errorf("Failed to generate the docs.") return } - logger.Success("Docs generated!") + beeLogger.Log.Success("Docs generated!") } if err == nil { @@ -198,12 +202,12 @@ func AutoBuild(files []string, isgenerate bool) { bcmd.Stderr = &stderr err = bcmd.Run() if err != nil { - logger.Errorf("Failed to build the application: %s", stderr.String()) + beeLogger.Log.Errorf("Failed to build the application: %s", stderr.String()) return } } - logger.Success("Built Successfully!") + beeLogger.Log.Success("Built Successfully!") Restart(appname) } @@ -211,39 +215,39 @@ func AutoBuild(files []string, isgenerate bool) { func Kill() { defer func() { if e := recover(); e != nil { - logger.Infof("Kill recover: %s", e) + beeLogger.Log.Infof("Kill recover: %s", e) } }() if cmd != nil && cmd.Process != nil { err := cmd.Process.Kill() if err != nil { - logger.Errorf("Error while killing cmd process: %s", err) + beeLogger.Log.Errorf("Error while killing cmd process: %s", err) } } } // Restart kills the running command process and starts it again func Restart(appname string) { - logger.Debugf("Kill running process", __FILE__(), __LINE__()) + beeLogger.Log.Debugf("Kill running process", utils.FILE(), utils.LINE()) Kill() go Start(appname) } // Start starts the command process func Start(appname string) { - logger.Infof("Restarting '%s'...", appname) - if strings.Index(appname, "./") == -1 { + beeLogger.Log.Infof("Restarting '%s'...", appname) + if !strings.Contains(appname, "./") { appname = "./" + appname } cmd = exec.Command(appname) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - cmd.Args = append([]string{appname}, conf.CmdArgs...) - cmd.Env = append(os.Environ(), conf.Envs...) + cmd.Args = append([]string{appname}, config.Conf.CmdArgs...) + cmd.Env = append(os.Environ(), config.Conf.Envs...) go cmd.Run() - logger.Successf("'%s' is running...", appname) + beeLogger.Log.Successf("'%s' is running...", appname) started <- true } @@ -262,7 +266,7 @@ func shouldIgnoreFile(filename string) bool { for _, regex := range ignoredFilesRegExps { r, err := regexp.Compile(regex) if err != nil { - logger.Fatalf("Could not compile regular expression: %s", err) + beeLogger.Log.Fatalf("Could not compile regular expression: %s", err) } if r.MatchString(filename) { return true diff --git a/banner.go b/cmd/commands/version/banner.go similarity index 66% rename from banner.go rename to cmd/commands/version/banner.go index fb06316..0ab367d 100644 --- a/banner.go +++ b/cmd/commands/version/banner.go @@ -1,4 +1,4 @@ -package main +package version import ( "io" @@ -6,6 +6,10 @@ import ( "os" "runtime" "text/template" + + "time" + + beeLogger "github.com/beego/bee/logger" ) // RuntimeInfo holds information about the current runtime. @@ -26,12 +30,12 @@ type RuntimeInfo struct { // print the banner in case of error. func InitBanner(out io.Writer, in io.Reader) { if in == nil { - logger.Fatal("The input is nil") + beeLogger.Log.Fatal("The input is nil") } banner, err := ioutil.ReadAll(in) if err != nil { - logger.Fatalf("Error while trying to read the banner: %s", err) + beeLogger.Log.Fatalf("Error while trying to read the banner: %s", err) } show(out, string(banner)) @@ -43,11 +47,11 @@ func show(out io.Writer, content string) { Parse(content) if err != nil { - logger.Fatalf("Cannot parse the banner template: %s", err) + beeLogger.Log.Fatalf("Cannot parse the banner template: %s", err) } err = t.Execute(out, RuntimeInfo{ - getGoVersion(), + GetGoVersion(), runtime.GOOS, runtime.GOARCH, runtime.NumCPU(), @@ -55,7 +59,14 @@ func show(out io.Writer, content string) { runtime.GOROOT(), runtime.Compiler, version, - getBeegoVersion(), + GetBeegoVersion(), }) - MustCheck(err) + if err != nil { + beeLogger.Log.Error(err.Error()) + } +} + +// Now returns the current local time in the specified layout +func Now(layout string) string { + return time.Now().Format(layout) } diff --git a/version.go b/cmd/commands/version/version.go similarity index 77% rename from version.go rename to cmd/commands/version/version.go index 6da9d1d..b70e5b8 100644 --- a/version.go +++ b/cmd/commands/version/version.go @@ -1,4 +1,4 @@ -package main +package version import ( "bufio" @@ -14,18 +14,12 @@ import ( "runtime" "strings" + "github.com/beego/bee/cmd/commands" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" "gopkg.in/yaml.v2" ) -var cmdVersion = &Command{ - UsageLine: "version", - Short: "Prints the current Bee version", - Long: ` -Prints the current Bee, Beego and Go version alongside the platform information. -`, - Run: versionCmd, -} - const verboseVersionBanner string = `%s%s______ | ___ \ | |_/ / ___ ___ @@ -52,21 +46,33 @@ const shortVersionBanner = `______ \____/ \___| \___| v{{ .BeeVersion }} ` +var CmdVersion = &commands.Command{ + UsageLine: "version", + Short: "Prints the current Bee version", + Long: ` +Prints the current Bee, Beego and Go version alongside the platform information. +`, + Run: versionCmd, +} var outputFormat string +const version = "1.8.0" + func init() { fs := flag.NewFlagSet("version", flag.ContinueOnError) fs.StringVar(&outputFormat, "o", "", "Set the output format. Either json or yaml.") - cmdVersion.Flag = *fs + CmdVersion.Flag = *fs + commands.AvailableCommands = append(commands.AvailableCommands, CmdVersion) } -func versionCmd(cmd *Command, args []string) int { +func versionCmd(cmd *commands.Command, args []string) int { + cmd.Flag.Parse(args) stdout := cmd.Out() if outputFormat != "" { runtimeInfo := RuntimeInfo{ - getGoVersion(), + GetGoVersion(), runtime.GOOS, runtime.GOARCH, runtime.NumCPU(), @@ -74,20 +80,24 @@ func versionCmd(cmd *Command, args []string) int { runtime.GOROOT(), runtime.Compiler, version, - getBeegoVersion(), + GetBeegoVersion(), } switch outputFormat { case "json": { b, err := json.MarshalIndent(runtimeInfo, "", " ") - MustCheck(err) + if err != nil { + beeLogger.Log.Error(err.Error()) + } fmt.Println(string(b)) return 0 } case "yaml": { b, err := yaml.Marshal(&runtimeInfo) - MustCheck(err) + if err != nil { + beeLogger.Log.Error(err.Error()) + } fmt.Println(string(b)) return 0 } @@ -102,11 +112,11 @@ func versionCmd(cmd *Command, args []string) int { // ShowShortVersionBanner prints the short version banner. func ShowShortVersionBanner() { - output := NewColorWriter(os.Stdout) - InitBanner(output, bytes.NewBufferString(MagentaBold(shortVersionBanner))) + output := colors.NewColorWriter(os.Stdout) + InitBanner(output, bytes.NewBufferString(colors.MagentaBold(shortVersionBanner))) } -func getBeegoVersion() string { +func GetBeegoVersion() string { gopath := os.Getenv("GOPATH") re, err := regexp.Compile(`VERSION = "([0-9.]+)"`) if err != nil { @@ -125,11 +135,11 @@ func getBeegoVersion() string { if os.IsNotExist(err) { continue } - logger.Error("Error while getting stats of 'beego.go'") + beeLogger.Log.Error("Error while getting stats of 'beego.go'") } fd, err := os.Open(filename) if err != nil { - logger.Error("Error while reading 'beego.go'") + beeLogger.Log.Error("Error while reading 'beego.go'") continue } reader := bufio.NewReader(fd) @@ -152,14 +162,14 @@ func getBeegoVersion() string { return "Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego" } -func getGoVersion() string { +func GetGoVersion() string { var ( cmdOut []byte err error ) if cmdOut, err = exec.Command("go", "version").Output(); err != nil { - logger.Fatalf("There was an error running 'go version' command: %s", err) + beeLogger.Log.Fatalf("There was an error running 'go version' command: %s", err) } return strings.Split(string(cmdOut), " ")[2] } diff --git a/code.go b/code.go deleted file mode 100644 index 335b021..0000000 --- a/code.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2011 Gary Burd -// Copyright 2013 Unknown -// -// 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 ( - "go/ast" - "go/printer" - "go/scanner" - "go/token" - "math" - "strconv" -) - -const ( - notPredeclared = iota - predeclaredType - predeclaredConstant - predeclaredFunction -) - -// predeclared represents the set of all predeclared identifiers. -var predeclared = map[string]int{ - "bool": predeclaredType, - "byte": predeclaredType, - "complex128": predeclaredType, - "complex64": predeclaredType, - "error": predeclaredType, - "float32": predeclaredType, - "float64": predeclaredType, - "int16": predeclaredType, - "int32": predeclaredType, - "int64": predeclaredType, - "int8": predeclaredType, - "int": predeclaredType, - "rune": predeclaredType, - "string": predeclaredType, - "uint16": predeclaredType, - "uint32": predeclaredType, - "uint64": predeclaredType, - "uint8": predeclaredType, - "uint": predeclaredType, - "uintptr": predeclaredType, - - "true": predeclaredConstant, - "false": predeclaredConstant, - "iota": predeclaredConstant, - "nil": predeclaredConstant, - - "append": predeclaredFunction, - "cap": predeclaredFunction, - "close": predeclaredFunction, - "complex": predeclaredFunction, - "copy": predeclaredFunction, - "delete": predeclaredFunction, - "imag": predeclaredFunction, - "len": predeclaredFunction, - "make": predeclaredFunction, - "new": predeclaredFunction, - "panic": predeclaredFunction, - "print": predeclaredFunction, - "println": predeclaredFunction, - "real": predeclaredFunction, - "recover": predeclaredFunction, -} - -const ( - ExportLinkAnnotation AnnotationKind = iota - AnchorAnnotation - CommentAnnotation - PackageLinkAnnotation - BuiltinAnnotation -) - -// annotationVisitor collects annotations. -type annotationVisitor struct { - annotations []Annotation -} - -func (v *annotationVisitor) add(kind AnnotationKind, importPath string) { - v.annotations = append(v.annotations, Annotation{Kind: kind, ImportPath: importPath}) -} - -func (v *annotationVisitor) ignoreName() { - v.add(-1, "") -} - -func (v *annotationVisitor) Visit(n ast.Node) ast.Visitor { - switch n := n.(type) { - case *ast.TypeSpec: - v.ignoreName() - ast.Walk(v, n.Type) - case *ast.FuncDecl: - if n.Recv != nil { - ast.Walk(v, n.Recv) - } - v.ignoreName() - ast.Walk(v, n.Type) - case *ast.Field: - for range n.Names { - v.ignoreName() - } - ast.Walk(v, n.Type) - case *ast.ValueSpec: - for range n.Names { - v.add(AnchorAnnotation, "") - } - if n.Type != nil { - ast.Walk(v, n.Type) - } - for _, x := range n.Values { - ast.Walk(v, x) - } - case *ast.Ident: - switch { - case n.Obj == nil && predeclared[n.Name] != notPredeclared: - v.add(BuiltinAnnotation, "") - case n.Obj != nil && ast.IsExported(n.Name): - v.add(ExportLinkAnnotation, "") - default: - v.ignoreName() - } - case *ast.SelectorExpr: - if x, _ := n.X.(*ast.Ident); x != nil { - if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg { - if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil { - if path, err := strconv.Unquote(spec.Path.Value); err == nil { - v.add(PackageLinkAnnotation, path) - if path == "C" { - v.ignoreName() - } else { - v.add(ExportLinkAnnotation, path) - } - return nil - } - } - } - } - ast.Walk(v, n.X) - v.ignoreName() - default: - return v - } - return nil -} - -func printDecl(decl ast.Node, fset *token.FileSet, buf []byte) (Code, []byte) { - v := &annotationVisitor{} - ast.Walk(v, decl) - - buf = buf[:0] - err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(sliceWriter{&buf}, fset, decl) - if err != nil { - return Code{Text: err.Error()}, buf - } - - var annotations []Annotation - var s scanner.Scanner - fset = token.NewFileSet() - file := fset.AddFile("", fset.Base(), len(buf)) - s.Init(file, buf, nil, scanner.ScanComments) -loop: - for { - pos, tok, lit := s.Scan() - switch tok { - case token.EOF: - break loop - case token.COMMENT: - p := file.Offset(pos) - e := p + len(lit) - if p > math.MaxInt16 || e > math.MaxInt16 { - break loop - } - annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)}) - case token.IDENT: - if len(v.annotations) == 0 { - // Oops! - break loop - } - annotation := v.annotations[0] - v.annotations = v.annotations[1:] - if annotation.Kind == -1 { - continue - } - p := file.Offset(pos) - e := p + len(lit) - if p > math.MaxInt16 || e > math.MaxInt16 { - break loop - } - annotation.Pos = int16(p) - annotation.End = int16(e) - if len(annotations) > 0 && annotation.Kind == ExportLinkAnnotation { - prev := annotations[len(annotations)-1] - if prev.Kind == PackageLinkAnnotation && - prev.ImportPath == annotation.ImportPath && - prev.End+1 == annotation.Pos { - // merge with previous - annotation.Pos = prev.Pos - annotations[len(annotations)-1] = annotation - continue loop - } - } - annotations = append(annotations, annotation) - } - } - return Code{Text: string(buf), Annotations: annotations}, buf -} - -type AnnotationKind int16 - -type Annotation struct { - Pos, End int16 - Kind AnnotationKind - ImportPath string -} - -type Code struct { - Text string - Annotations []Annotation -} - -func commentAnnotations(src string) []Annotation { - var annotations []Annotation - var s scanner.Scanner - fset := token.NewFileSet() - file := fset.AddFile("", fset.Base(), len(src)) - s.Init(file, []byte(src), nil, scanner.ScanComments) - for { - pos, tok, lit := s.Scan() - switch tok { - case token.EOF: - return annotations - case token.COMMENT: - p := file.Offset(pos) - e := p + len(lit) - if p > math.MaxInt16 || e > math.MaxInt16 { - return annotations - } - annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)}) - } - } -} - -type sliceWriter struct{ p *[]byte } - -func (w sliceWriter) Write(p []byte) (int, error) { - *w.p = append(*w.p, p...) - return len(p), nil -} diff --git a/conf.go b/config/conf.go similarity index 75% rename from conf.go rename to config/conf.go index e352f84..ed1a0d7 100644 --- a/conf.go +++ b/config/conf.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package config import ( "encoding/json" @@ -22,6 +22,7 @@ import ( "io" "path/filepath" + beeLogger "github.com/beego/bee/logger" "gopkg.in/yaml.v2" ) @@ -49,7 +50,7 @@ var defaultConf = `{ "enable_reload": false } ` -var conf struct { +var Conf struct { Version int // gopm support Gopm struct { @@ -79,6 +80,10 @@ var conf struct { EnableReload bool `json:"enable_reload" yaml:"enable_reload"` } +func init() { + loadConfig() +} + // loadConfig loads customized configuration. func loadConfig() (err error) { err = filepath.Walk(".", func(path string, fileInfo os.FileInfo, err error) error { @@ -91,20 +96,20 @@ func loadConfig() (err error) { } if fileInfo.Name() == "bee.json" { - logger.Info("Loading configuration from 'bee.json'...") - err = parseJSON(path, &conf) + beeLogger.Log.Info("Loading configuration from 'bee.json'...") + err = parseJSON(path, &Conf) if err != nil { - logger.Errorf("Failed to parse JSON file: %s", err) + beeLogger.Log.Errorf("Failed to parse JSON file: %s", err) return err } return io.EOF } if fileInfo.Name() == "Beefile" { - logger.Info("Loading configuration from 'Beefile'...") - err = parseYAML(path, &conf) + beeLogger.Log.Info("Loading configuration from 'Beefile'...") + err = parseYAML(path, &Conf) if err != nil { - logger.Errorf("Failed to parse YAML file: %s", err) + beeLogger.Log.Errorf("Failed to parse YAML file: %s", err) return err } return io.EOF @@ -115,8 +120,8 @@ func loadConfig() (err error) { // In case no configuration file found or an error different than io.EOF, // fallback to default configuration if err != io.EOF { - logger.Info("Loading default configuration...") - err = json.Unmarshal([]byte(defaultConf), &conf) + beeLogger.Log.Info("Loading default configuration...") + err = json.Unmarshal([]byte(defaultConf), &Conf) if err != nil { return } @@ -126,21 +131,21 @@ func loadConfig() (err error) { err = nil // Check format version - if conf.Version != confVer { - logger.Warn("Your configuration file is outdated. Please do consider updating it.") - logger.Hint("Check the latest version of bee's configuration file.") + if Conf.Version != confVer { + beeLogger.Log.Warn("Your configuration file is outdated. Please do consider updating it.") + beeLogger.Log.Hint("Check the latest version of bee's configuration file.") } // Set variables - if len(conf.DirStruct.Controllers) == 0 { - conf.DirStruct.Controllers = "controllers" + if len(Conf.DirStruct.Controllers) == 0 { + Conf.DirStruct.Controllers = "controllers" } - if len(conf.DirStruct.Models) == 0 { - conf.DirStruct.Models = "models" + if len(Conf.DirStruct.Models) == 0 { + Conf.DirStruct.Models = "models" } // Append watch exts - watchExts = append(watchExts, conf.WatchExt...) + //watchExts = append(watchExts, Conf.WatchExt...) return } @@ -154,10 +159,7 @@ func parseJSON(path string, v interface{}) error { return err } err = json.Unmarshal(data, v) - if err != nil { - return err - } - return nil + return err } func parseYAML(path string, v interface{}) error { @@ -170,8 +172,5 @@ func parseYAML(path string, v interface{}) error { return err } err = yaml.Unmarshal(data, v) - if err != nil { - return err - } - return nil + return err } diff --git a/g.go b/g.go deleted file mode 100644 index 8c133d9..0000000 --- a/g.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2013 bee authors -// -// 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 ( - "os" - "strings" -) - -var cmdGenerate = &Command{ - UsageLine: "generate [command]", - Short: "Source code generator", - Long: `▶ {{"To scaffold out your entire application:"|bold}} - - $ bee generate scaffold [scaffoldname] [-fields="title:string,body:text"] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] - - ▶ {{"To generate a Model based on fields:"|bold}} - - $ bee generate model [modelname] [-fields="name:type"] - - ▶ {{"To generate a controller:"|bold}} - - $ bee generate controller [controllerfile] - - ▶ {{"To generate a CRUD view:"|bold}} - - $ bee generate view [viewpath] - - ▶ {{"To generate a migration file for making database schema updates:"|bold}} - - $ bee generate migration [migrationfile] [-fields="name:type"] - - ▶ {{"To generate swagger doc file:"|bold}} - - $ bee generate docs - - ▶ {{"To generate a test case:"|bold}} - - $ bee generate test [routerfile] - - ▶ {{"To generate appcode based on an existing database:"|bold}} - - $ bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3] -`, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, - Run: generateCode, -} - -var driver docValue -var conn docValue -var level docValue -var tables docValue -var fields docValue - -func init() { - cmdGenerate.Flag.Var(&tables, "tables", "List of table names separated by a comma.") - cmdGenerate.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.") - cmdGenerate.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.") - cmdGenerate.Flag.Var(&level, "level", "Either 1, 2 or 3. i.e. 1=models; 2=models and controllers; 3=models, controllers and routers.") - cmdGenerate.Flag.Var(&fields, "fields", "List of table fields.") -} - -func generateCode(cmd *Command, args []string) int { - currpath, _ := os.Getwd() - if len(args) < 1 { - logger.Fatal("Command is missing") - } - - gps := GetGOPATHs() - if len(gps) == 0 { - logger.Fatal("GOPATH environment variable is not set or empty") - } - - gopath := gps[0] - - logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) - - gcmd := args[0] - switch gcmd { - case "scaffold": - if len(args) < 2 { - logger.Fatal("Wrong number of arguments. Run: bee help generate") - } - // Load the configuration - err := loadConfig() - if err != nil { - logger.Fatalf("Failed to load configuration: %s", err) - } - cmd.Flag.Parse(args[2:]) - if driver == "" { - driver = docValue(conf.Database.Driver) - if driver == "" { - driver = "mysql" - } - } - if conn == "" { - conn = docValue(conf.Database.Conn) - if conn == "" { - conn = "root:@tcp(127.0.0.1:3306)/test" - } - } - if fields == "" { - logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"") - logger.Fatal("Wrong number of arguments. Run: bee help generate") - } - sname := args[1] - generateScaffold(sname, fields.String(), currpath, driver.String(), conn.String()) - case "docs": - generateDocs(currpath) - case "appcode": - // Load the configuration - err := loadConfig() - if err != nil { - logger.Fatalf("Failed to load configuration: %s", err) - } - cmd.Flag.Parse(args[1:]) - if driver == "" { - driver = docValue(conf.Database.Driver) - if driver == "" { - driver = "mysql" - } - } - if conn == "" { - conn = docValue(conf.Database.Conn) - if conn == "" { - if driver == "mysql" { - conn = "root:@tcp(127.0.0.1:3306)/test" - } else if driver == "postgres" { - conn = "postgres://postgres:postgres@127.0.0.1:5432/postgres" - } - } - } - if level == "" { - level = "3" - } - logger.Infof("Using '%s' as 'driver'", driver) - logger.Infof("Using '%s' as 'conn'", conn) - logger.Infof("Using '%s' as 'tables'", tables) - logger.Infof("Using '%s' as 'level'", level) - generateAppcode(driver.String(), conn.String(), level.String(), tables.String(), currpath) - case "migration": - if len(args) < 2 { - logger.Fatal("Wrong number of arguments. Run: bee help generate") - } - cmd.Flag.Parse(args[2:]) - mname := args[1] - - logger.Infof("Using '%s' as migration name", mname) - - upsql := "" - downsql := "" - if fields != "" { - dbMigrator := newDBDriver() - upsql = dbMigrator.generateCreateUp(mname) - downsql = dbMigrator.generateCreateDown(mname) - } - generateMigration(mname, upsql, downsql, currpath) - case "controller": - if len(args) == 2 { - cname := args[1] - generateController(cname, currpath) - } else { - logger.Fatal("Wrong number of arguments. Run: bee help generate") - } - case "model": - if len(args) < 2 { - logger.Fatal("Wrong number of arguments. Run: bee help generate") - } - cmd.Flag.Parse(args[2:]) - if fields == "" { - logger.Hint("fields option should not be empty, i.e. -fields=\"title:string,body:text\"") - logger.Fatal("Wrong number of arguments. Run: bee help generate") - } - sname := args[1] - generateModel(sname, fields.String(), currpath) - case "view": - if len(args) == 2 { - cname := args[1] - generateView(cname, currpath) - } else { - logger.Fatal("Wrong number of arguments. Run: bee help generate") - } - default: - logger.Fatal("Command is missing") - } - logger.Successf("%s successfully generated!", strings.Title(gcmd)) - return 0 -} diff --git a/g_scaffold.go b/g_scaffold.go deleted file mode 100644 index dd43425..0000000 --- a/g_scaffold.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import "strings" - -func generateScaffold(sname, fields, currpath, driver, conn string) { - logger.Infof("Do you want to create a '%s' model? [Yes|No] ", sname) - - // Generate the model - if askForConfirmation() { - generateModel(sname, fields, currpath) - } - - // Generate the controller - logger.Infof("Do you want to create a '%s' controller? [Yes|No] ", sname) - if askForConfirmation() { - generateController(sname, currpath) - } - - // Generate the views - logger.Infof("Do you want to create views for this '%s' resource? [Yes|No] ", sname) - if askForConfirmation() { - generateView(sname, currpath) - } - - // Generate a migration - logger.Infof("Do you want to create a '%s' migration and schema for this resource? [Yes|No] ", sname) - if askForConfirmation() { - upsql := "" - downsql := "" - if fields != "" { - dbMigrator := newDBDriver() - upsql = dbMigrator.generateCreateUp(sname) - downsql = dbMigrator.generateCreateDown(sname) - //todo remove - //if driver == "" { - // downsql = strings.Replace(downsql, "`", "", -1) - //} - } - generateMigration(sname, upsql, downsql, currpath) - } - - // Run the migration - logger.Infof("Do you want to migrate the database? [Yes|No] ") - if askForConfirmation() { - migrateUpdate(currpath, driver, conn) - } - logger.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname)) -} diff --git a/generate/g.go b/generate/g.go new file mode 100644 index 0000000..e4260b2 --- /dev/null +++ b/generate/g.go @@ -0,0 +1,23 @@ +// Copyright 2013 bee authors +// +// 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 generate + +import "github.com/beego/bee/utils" + +var SQLDriver utils.DocValue +var SQLConn utils.DocValue +var Level utils.DocValue +var Tables utils.DocValue +var Fields utils.DocValue diff --git a/g_appcode.go b/generate/g_appcode.go similarity index 89% rename from g_appcode.go rename to generate/g_appcode.go index e20cc63..c774689 100644 --- a/g_appcode.go +++ b/generate/g_appcode.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package generate import ( "database/sql" @@ -23,6 +23,9 @@ import ( "regexp" "strings" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" ) @@ -183,7 +186,7 @@ type OrmTag struct { // String returns the source code string for the Table struct func (tb *Table) String() string { - rv := fmt.Sprintf("type %s struct {\n", camelCase(tb.Name)) + rv := fmt.Sprintf("type %s struct {\n", utils.CamelCase(tb.Name)) for _, v := range tb.Columns { rv += v.String() + "\n" } @@ -255,7 +258,7 @@ func (tag *OrmTag) String() string { return fmt.Sprintf("`orm:\"%s\"`", strings.Join(ormOptions, ";")) } -func generateAppcode(driver, connStr, level, tables, currpath string) { +func GenerateAppcode(driver, connStr, level, tables, currpath string) { var mode byte switch level { case "1": @@ -265,7 +268,7 @@ func generateAppcode(driver, connStr, level, tables, currpath string) { case "3": mode = OModel | OController | ORouter default: - logger.Fatal("Invalid level value. Must be either \"1\", \"2\", or \"3\"") + beeLogger.Log.Fatal("Invalid level value. Must be either \"1\", \"2\", or \"3\"") } var selectedTables map[string]bool if tables != "" { @@ -278,9 +281,9 @@ func generateAppcode(driver, connStr, level, tables, currpath string) { case "mysql": case "postgres": case "sqlite": - logger.Fatal("Generating app code from SQLite database is not supported yet.") + beeLogger.Log.Fatal("Generating app code from SQLite database is not supported yet.") default: - logger.Fatal("Unknown database driver. Must be either \"mysql\", \"postgres\" or \"sqlite\"") + beeLogger.Log.Fatal("Unknown database driver. Must be either \"mysql\", \"postgres\" or \"sqlite\"") } gen(driver, connStr, mode, selectedTables, currpath) } @@ -290,11 +293,11 @@ func generateAppcode(driver, connStr, level, tables, currpath string) { func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, apppath string) { db, err := sql.Open(dbms, connStr) if err != nil { - logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) + beeLogger.Log.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) } defer db.Close() if trans, ok := dbDriver[dbms]; ok { - logger.Info("Analyzing database tables...") + beeLogger.Log.Info("Analyzing database tables...") tableNames := trans.GetTableNames(db) tables := getTableObjects(tableNames, db, trans) mvcPath := new(MvcPath) @@ -305,7 +308,7 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap pkgPath := getPackagePath(apppath) writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) } else { - logger.Fatalf("Generating app code from '%s' database is not supported yet.", dbms) + beeLogger.Log.Fatalf("Generating app code from '%s' database is not supported yet.", dbms) } } @@ -313,13 +316,13 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap func (*MysqlDB) GetTableNames(db *sql.DB) (tables []string) { rows, err := db.Query("SHOW TABLES") if err != nil { - logger.Fatalf("Could not show tables: %s", err) + beeLogger.Log.Fatalf("Could not show tables: %s", err) } defer rows.Close() for rows.Next() { var name string if err := rows.Scan(&name); err != nil { - logger.Fatalf("Could not show tables: %s", err) + beeLogger.Log.Fatalf("Could not show tables: %s", err) } tables = append(tables, name) } @@ -362,12 +365,12 @@ func (*MysqlDB) GetConstraints(db *sql.DB, table *Table, blackList map[string]bo c.table_schema = database() AND c.table_name = ? AND u.table_schema = database() AND u.table_name = ?`, table.Name, table.Name) // u.position_in_unique_constraint, if err != nil { - logger.Fatal("Could not query INFORMATION_SCHEMA for PK/UK/FK information") + beeLogger.Log.Fatal("Could not query INFORMATION_SCHEMA for PK/UK/FK information") } for rows.Next() { var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { - logger.Fatal("Could not read INFORMATION_SCHEMA for PK/UK/FK information") + beeLogger.Log.Fatal("Could not read INFORMATION_SCHEMA for PK/UK/FK information") } constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), @@ -407,7 +410,7 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin table_schema = database() AND table_name = ?`, table.Name) if err != nil { - logger.Fatalf("Could not query the database: %s", err) + beeLogger.Log.Fatalf("Could not query the database: %s", err) } defer colDefRows.Close() @@ -415,17 +418,17 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin // datatype as bytes so that SQL values can be retrieved var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { - logger.Fatal("Could not query INFORMATION_SCHEMA for column information") + beeLogger.Log.Fatal("Could not query INFORMATION_SCHEMA for column information") } colName, dataType, columnType, isNullable, columnDefault, extra := string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) // create a column col := new(Column) - col.Name = camelCase(colName) + col.Name = utils.CamelCase(colName) col.Type, err = mysqlDB.GetGoDataType(dataType) if err != nil { - logger.Fatalf("%s", err) + beeLogger.Log.Fatalf("%s", err) } // Tag info @@ -449,8 +452,8 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin if isFk && !isBl { tag.RelFk = true refStructName := fkCol.RefTable - col.Name = camelCase(colName) - col.Type = "*" + camelCase(refStructName) + col.Name = utils.CamelCase(colName) + col.Type = "*" + utils.CamelCase(refStructName) } else { // if the name of column is Id, and it's not primary key if colName == "id" { @@ -464,7 +467,7 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin if sign == "unsigned" && extra != "auto_increment" { col.Type, err = mysqlDB.GetGoDataType(dataType + " " + sign) if err != nil { - logger.Fatalf("%s", err) + beeLogger.Log.Fatalf("%s", err) } } } @@ -516,14 +519,14 @@ func (*PostgresDB) GetTableNames(db *sql.DB) (tables []string) { table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')`) if err != nil { - logger.Fatalf("Could not show tables: %s", err) + beeLogger.Log.Fatalf("Could not show tables: %s", err) } defer rows.Close() for rows.Next() { var name string if err := rows.Scan(&name); err != nil { - logger.Fatalf("Could not show tables: %s", err) + beeLogger.Log.Fatalf("Could not show tables: %s", err) } tables = append(tables, name) } @@ -553,13 +556,13 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string AND u.table_name = $2`, table.Name, table.Name) // u.position_in_unique_constraint, if err != nil { - logger.Fatalf("Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s", err) + beeLogger.Log.Fatalf("Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s", err) } for rows.Next() { var constraintTypeBytes, columnNameBytes, refTableSchemaBytes, refTableNameBytes, refColumnNameBytes, refOrdinalPosBytes []byte if err := rows.Scan(&constraintTypeBytes, &columnNameBytes, &refTableSchemaBytes, &refTableNameBytes, &refColumnNameBytes, &refOrdinalPosBytes); err != nil { - logger.Fatalf("Could not read INFORMATION_SCHEMA for PK/UK/FK information: %s", err) + beeLogger.Log.Fatalf("Could not read INFORMATION_SCHEMA for PK/UK/FK information: %s", err) } constraintType, columnName, refTableSchema, refTableName, refColumnName, refOrdinalPos := string(constraintTypeBytes), string(columnNameBytes), string(refTableSchemaBytes), @@ -609,7 +612,7 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map AND table_name = $1`, table.Name) if err != nil { - logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) + beeLogger.Log.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) } defer colDefRows.Close() @@ -617,16 +620,16 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map // datatype as bytes so that SQL values can be retrieved var colNameBytes, dataTypeBytes, columnTypeBytes, isNullableBytes, columnDefaultBytes, extraBytes []byte if err := colDefRows.Scan(&colNameBytes, &dataTypeBytes, &columnTypeBytes, &isNullableBytes, &columnDefaultBytes, &extraBytes); err != nil { - logger.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) + beeLogger.Log.Fatalf("Could not query INFORMATION_SCHEMA for column information: %s", err) } colName, dataType, columnType, isNullable, columnDefault, extra := string(colNameBytes), string(dataTypeBytes), string(columnTypeBytes), string(isNullableBytes), string(columnDefaultBytes), string(extraBytes) // Create a column col := new(Column) - col.Name = camelCase(colName) + col.Name = utils.CamelCase(colName) col.Type, err = postgresDB.GetGoDataType(dataType) if err != nil { - logger.Fatalf("%s", err) + beeLogger.Log.Fatalf("%s", err) } // Tag info @@ -650,8 +653,8 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map if isFk && !isBl { tag.RelFk = true refStructName := fkCol.RefTable - col.Name = camelCase(colName) - col.Type = "*" + camelCase(refStructName) + col.Name = utils.CamelCase(colName) + col.Type = "*" + utils.CamelCase(refStructName) } else { // if the name of column is Id, and it's not primary key if colName == "id" { @@ -716,22 +719,22 @@ func createPaths(mode byte, paths *MvcPath) { // Newly geneated files will be inside these folders. func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { if (OModel & mode) == OModel { - logger.Info("Creating model files...") + beeLogger.Log.Info("Creating model files...") writeModelFiles(tables, paths.ModelPath, selectedTables) } if (OController & mode) == OController { - logger.Info("Creating controller files...") + beeLogger.Log.Info("Creating controller files...") writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath) } if (ORouter & mode) == ORouter { - logger.Info("Creating router files...") + beeLogger.Log.Info("Creating router files...") writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath) } } // writeModelFiles generates model files func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) { - w := NewColorWriter(os.Stdout) + w := colors.NewColorWriter(os.Stdout) for _, tb := range tables { // if selectedTables map is not nil and this table is not selected, ignore it @@ -744,22 +747,22 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo fpath := path.Join(mPath, filename+".go") var f *os.File var err error - if isExist(fpath) { - logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) - if askForConfirmation() { + if utils.IsExist(fpath) { + beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + if utils.AskForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) continue } } else { - logger.Warnf("Skipped create file '%s'", fpath) + beeLogger.Log.Warnf("Skipped create file '%s'", fpath) continue } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) continue } } @@ -770,7 +773,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo template = ModelTPL } fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1) - fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) + fileStr = strings.Replace(fileStr, "{{modelName}}", utils.CamelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1) // If table contains time field, import time.Time package @@ -783,17 +786,17 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) if _, err := f.WriteString(fileStr); err != nil { - logger.Fatalf("Could not write model file to '%s': %s", fpath, err) + beeLogger.Log.Fatalf("Could not write model file to '%s': %s", fpath, err) } - CloseFile(f) + utils.CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") - formatSourceCode(fpath) + utils.FormatSourceCode(fpath) } } // writeControllerFiles generates controller files func writeControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath string) { - w := NewColorWriter(os.Stdout) + w := colors.NewColorWriter(os.Stdout) for _, tb := range tables { // If selectedTables map is not nil and this table is not selected, ignore it @@ -809,39 +812,39 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri fpath := path.Join(cPath, filename+".go") var f *os.File var err error - if isExist(fpath) { - logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) - if askForConfirmation() { + if utils.IsExist(fpath) { + beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + if utils.AskForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) continue } } else { - logger.Warnf("Skipped create file '%s'", fpath) + beeLogger.Log.Warnf("Skipped create file '%s'", fpath) continue } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) continue } } - fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", camelCase(tb.Name), -1) + fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", utils.CamelCase(tb.Name), -1) fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1) if _, err := f.WriteString(fileStr); err != nil { - logger.Fatalf("Could not write controller file to '%s': %s", fpath, err) + beeLogger.Log.Fatalf("Could not write controller file to '%s': %s", fpath, err) } - CloseFile(f) + utils.CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") - formatSourceCode(fpath) + utils.FormatSourceCode(fpath) } } // writeRouterFile generates router file func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bool, pkgPath string) { - w := NewColorWriter(os.Stdout) + w := colors.NewColorWriter(os.Stdout) var nameSpaces []string for _, tb := range tables { @@ -856,7 +859,7 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo } // Add namespaces nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1) - nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1) + nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", utils.CamelCase(tb.Name), -1) nameSpaces = append(nameSpaces, nameSpace) } // Add export controller @@ -865,31 +868,31 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1) var f *os.File var err error - if isExist(fpath) { - logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) - if askForConfirmation() { + if utils.IsExist(fpath) { + beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + if utils.AskForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) return } } else { - logger.Warnf("Skipped create file '%s'", fpath) + beeLogger.Log.Warnf("Skipped create file '%s'", fpath) return } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) return } } if _, err := f.WriteString(routerStr); err != nil { - logger.Fatalf("Could not write router file to '%s': %s", fpath, err) + beeLogger.Log.Fatalf("Could not write router file to '%s': %s", fpath, err) } - CloseFile(f) + utils.CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") - formatSourceCode(fpath) + utils.FormatSourceCode(fpath) } func isSQLTemporalType(t string) bool { @@ -952,10 +955,10 @@ func getFileName(tbName string) (filename string) { func getPackagePath(curpath string) (packpath string) { gopath := os.Getenv("GOPATH") if gopath == "" { - logger.Fatal("GOPATH environment variable is not set or empty") + beeLogger.Log.Fatal("GOPATH environment variable is not set or empty") } - logger.Debugf("GOPATH: %s", __FILE__(), __LINE__(), gopath) + beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath) appsrcpath := "" haspath := false @@ -964,7 +967,7 @@ func getPackagePath(curpath string) (packpath string) { for _, wg := range wgopath { wg, _ = filepath.EvalSymlinks(path.Join(wg, "src")) - if filepath.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) { + if strings.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) { haspath = true appsrcpath = wg break @@ -972,11 +975,11 @@ func getPackagePath(curpath string) (packpath string) { } if !haspath { - logger.Fatalf("Cannot generate application code outside of GOPATH '%s'", gopath) + beeLogger.Log.Fatalf("Cannot generate application code outside of GOPATH '%s'", gopath) } if curpath == appsrcpath { - logger.Fatal("Cannot generate application code outside of application path") + beeLogger.Log.Fatal("Cannot generate application code outside of application path") } packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/") diff --git a/g_controllers.go b/generate/g_controllers.go similarity index 93% rename from g_controllers.go rename to generate/g_controllers.go index a85f3df..d05d04a 100644 --- a/g_controllers.go +++ b/generate/g_controllers.go @@ -12,17 +12,21 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package generate import ( "fmt" "os" "path" "strings" + + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" ) -func generateController(cname, currpath string) { - w := NewColorWriter(os.Stdout) +func GenerateController(cname, currpath string) { + w := colors.NewColorWriter(os.Stdout) p, f := path.Split(cname) controllerName := strings.Title(f) @@ -33,26 +37,26 @@ func generateController(cname, currpath string) { packageName = p[i+1 : len(p)-1] } - logger.Infof("Using '%s' as controller name", controllerName) - logger.Infof("Using '%s' as package name", packageName) + beeLogger.Log.Infof("Using '%s' as controller name", controllerName) + beeLogger.Log.Infof("Using '%s' as package name", packageName) fp := path.Join(currpath, "controllers", p) if _, err := os.Stat(fp); os.IsNotExist(err) { // Create the controller's directory if err := os.MkdirAll(fp, 0777); err != nil { - logger.Fatalf("Could not create controllers directory: %s", err) + beeLogger.Log.Fatalf("Could not create controllers directory: %s", err) } } fpath := path.Join(fp, strings.ToLower(controllerName)+".go") if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { - defer CloseFile(f) + defer utils.CloseFile(f) modelPath := path.Join(currpath, "models", strings.ToLower(controllerName)+".go") var content string if _, err := os.Stat(modelPath); err == nil { - logger.Infof("Using matching model '%s'", controllerName) + beeLogger.Log.Infof("Using matching model '%s'", controllerName) content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1) pkgPath := getPackagePath(currpath) content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1) @@ -64,10 +68,10 @@ func generateController(cname, currpath string) { f.WriteString(content) // Run 'gofmt' on the generated source code - formatSourceCode(fpath) + utils.FormatSourceCode(fpath) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") } else { - logger.Fatalf("Could not create controller file: %s", err) + beeLogger.Log.Fatalf("Could not create controller file: %s", err) } } diff --git a/g_hproseappcode.go b/generate/g_hproseappcode.go similarity index 58% rename from g_hproseappcode.go rename to generate/g_hproseappcode.go index 3ce52ae..f73d6ff 100644 --- a/g_hproseappcode.go +++ b/generate/g_hproseappcode.go @@ -15,7 +15,7 @@ * * \**********************************************************/ -package main +package generate import ( "database/sql" @@ -24,11 +24,256 @@ import ( "path" "strings" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" ) -func generateHproseAppcode(driver, connStr, level, tables, currpath string) { +var Hproseconf = `appname = {{.Appname}} +httpport = 8080 +runmode = dev +autorender = false +copyrequestbody = true +EnableDocs = true +` +var HproseMaingo = `package main + +import ( + "fmt" + "reflect" + + "{{.Appname}}/models" + "github.com/hprose/hprose-golang/rpc" + + "github.com/astaxie/beego" +) + +func logInvokeHandler( + name string, + args []reflect.Value, + context rpc.Context, + next rpc.NextInvokeHandler) (results []reflect.Value, err error) { + fmt.Printf("%s(%v) = ", name, args) + results, err = next(name, args, context) + fmt.Printf("%v %v\r\n", results, err) + return +} + +func main() { + // Create WebSocketServer + // service := rpc.NewWebSocketService() + + // Create Http Server + service := rpc.NewHTTPService() + + // Use Logger Middleware + service.AddInvokeHandler(logInvokeHandler) + + // Publish Functions + service.AddFunction("AddOne", models.AddOne) + service.AddFunction("GetOne", models.GetOne) + + // Start Service + beego.Handler("/", service) + beego.Run() +} +` + +var HproseMainconngo = `package main + +import ( + "fmt" + "reflect" + + "{{.Appname}}/models" + "github.com/hprose/hprose-golang/rpc" + + "github.com/astaxie/beego" + "github.com/astaxie/beego/orm" + {{.DriverPkg}} +) + +func init() { + orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}") +} + +func logInvokeHandler( + name string, + args []reflect.Value, + context rpc.Context, + next rpc.NextInvokeHandler) (results []reflect.Value, err error) { + fmt.Printf("%s(%v) = ", name, args) + results, err = next(name, args, context) + fmt.Printf("%v %v\r\n", results, err) + return +} + +func main() { + // Create WebSocketServer + // service := rpc.NewWebSocketService() + + // Create Http Server + service := rpc.NewHTTPService() + + // Use Logger Middleware + service.AddInvokeHandler(logInvokeHandler) + + {{HproseFunctionList}} + + // Start Service + beego.Handler("/", service) + beego.Run() +} + +` + +var HproseModels = `package models + +import ( + "errors" + "strconv" + "time" +) + +var ( + Objects map[string]*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"} +} + +func AddOne(object 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) { + if v, ok := Objects[ObjectId]; ok { + return v, nil + } + return nil, errors.New("ObjectId Not Exist") +} + +func GetAll() map[string]*Object { + return Objects +} + +func Update(ObjectId string, Score int64) (err error) { + if v, ok := Objects[ObjectId]; ok { + v.Score = Score + return nil + } + return errors.New("ObjectId Not Exist") +} + +func Delete(ObjectId string) { + delete(Objects, ObjectId) +} + +` + +var HproseModels2 = `package models + +import ( + "errors" + "strconv" + "time" +) + +var ( + UserList map[string]*User +) + +func init() { + UserList = make(map[string]*User) + u := User{"user_11111", "astaxie", "11111", 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 { + u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10) + UserList[u.Id] = &u + return u.Id +} + +func GetUser(uid string) (u *User, err error) { + if u, ok := UserList[uid]; ok { + return u, nil + } + return nil, errors.New("User not exists") +} + +func GetAllUsers() map[string]*User { + return UserList +} + +func UpdateUser(uid string, uu *User) (a *User, err error) { + if u, ok := UserList[uid]; ok { + if uu.Username != "" { + u.Username = uu.Username + } + if uu.Password != "" { + u.Password = uu.Password + } + if uu.Profile.Age != 0 { + u.Profile.Age = uu.Profile.Age + } + if uu.Profile.Address != "" { + u.Profile.Address = uu.Profile.Address + } + if uu.Profile.Gender != "" { + u.Profile.Gender = uu.Profile.Gender + } + if uu.Profile.Email != "" { + u.Profile.Email = uu.Profile.Email + } + return u, nil + } + return nil, errors.New("User Not Exist") +} + +func Login(username, password string) bool { + for _, u := range UserList { + if u.Username == username && u.Password == password { + return true + } + } + return false +} + +func DeleteUser(uid string) { + delete(UserList, uid) +} +` +var HproseAddFunctions = []string{} + +func GenerateHproseAppcode(driver, connStr, level, tables, currpath string) { var mode byte switch level { case "1": @@ -38,7 +283,7 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) { case "3": mode = OModel | OController | ORouter default: - logger.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"") + beeLogger.Log.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"") } var selectedTables map[string]bool if tables != "" { @@ -51,9 +296,9 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) { case "mysql": case "postgres": case "sqlite": - logger.Fatal("Generating app code from SQLite database is not supported yet") + beeLogger.Log.Fatal("Generating app code from SQLite database is not supported yet") default: - logger.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver) + beeLogger.Log.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver) } genHprose(driver, connStr, mode, selectedTables, currpath) } @@ -63,11 +308,11 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) { func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) { db, err := sql.Open(dbms, connStr) if err != nil { - logger.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) + beeLogger.Log.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err) } defer db.Close() if trans, ok := dbDriver[dbms]; ok { - logger.Info("Analyzing database tables...") + beeLogger.Log.Info("Analyzing database tables...") tableNames := trans.GetTableNames(db) tables := getTableObjects(tableNames, db, trans) mvcPath := new(MvcPath) @@ -76,7 +321,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo pkgPath := getPackagePath(currpath) writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) } else { - logger.Fatalf("Generating app code from '%s' database is not supported yet", dbms) + beeLogger.Log.Fatalf("Generating app code from '%s' database is not supported yet", dbms) } } @@ -85,14 +330,14 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo // Newly geneated files will be inside these folders. func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { if (OModel & mode) == OModel { - logger.Info("Creating model files...") + beeLogger.Log.Info("Creating model files...") writeHproseModelFiles(tables, paths.ModelPath, selectedTables) } } // writeHproseModelFiles generates model files func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) { - w := NewColorWriter(os.Stdout) + w := colors.NewColorWriter(os.Stdout) for _, tb := range tables { // if selectedTables map is not nil and this table is not selected, ignore it @@ -105,22 +350,22 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str fpath := path.Join(mPath, filename+".go") var f *os.File var err error - if isExist(fpath) { - logger.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) - if askForConfirmation() { + if utils.IsExist(fpath) { + beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath) + if utils.AskForConfirmation() { f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) continue } } else { - logger.Warnf("Skipped create file '%s'", fpath) + beeLogger.Log.Warnf("Skipped create file '%s'", fpath) continue } } else { f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666) if err != nil { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) continue } } @@ -129,10 +374,10 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str template = HproseStructModelTPL } else { template = HproseModelTPL - hproseAddFunctions = append(hproseAddFunctions, strings.Replace(HproseAddFunction, "{{modelName}}", camelCase(tb.Name), -1)) + HproseAddFunctions = append(HproseAddFunctions, strings.Replace(HproseAddFunction, "{{modelName}}", utils.CamelCase(tb.Name), -1)) } fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1) - fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1) + fileStr = strings.Replace(fileStr, "{{modelName}}", utils.CamelCase(tb.Name), -1) // if table contains time field, import time.Time package timePkg := "" importTimePkg := "" @@ -143,11 +388,11 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1) fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1) if _, err := f.WriteString(fileStr); err != nil { - logger.Fatalf("Could not write model file to '%s'", fpath) + beeLogger.Log.Fatalf("Could not write model file to '%s'", fpath) } - CloseFile(f) + utils.CloseFile(f) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") - formatSourceCode(fpath) + utils.FormatSourceCode(fpath) } } diff --git a/g_migration.go b/generate/g_migration.go similarity index 76% rename from g_migration.go rename to generate/g_migration.go index 8ab3f1f..56ec063 100644 --- a/g_migration.go +++ b/generate/g_migration.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package generate import ( "fmt" @@ -20,6 +20,10 @@ import ( "path" "strings" "time" + + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" ) const ( @@ -29,18 +33,18 @@ const ( ) type DBDriver interface { - generateCreateUp(tableName string) string - generateCreateDown(tableName string) string + GenerateCreateUp(tableName string) string + GenerateCreateDown(tableName string) string } type mysqlDriver struct{} -func (m mysqlDriver) generateCreateUp(tableName string) string { - upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(fields.String()) + `)");` +func (m mysqlDriver) GenerateCreateUp(tableName string) string { + upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(Fields.String()) + `)");` return upsql } -func (m mysqlDriver) generateCreateDown(tableName string) string { +func (m mysqlDriver) GenerateCreateDown(tableName string) string { downsql := `m.SQL("DROP TABLE ` + "`" + tableName + "`" + `")` return downsql } @@ -51,21 +55,21 @@ func (m mysqlDriver) generateSQLFromFields(fields string) string { for i, v := range fds { kv := strings.SplitN(v, ":", 2) if len(kv) != 2 { - logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) + beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } typ, tag := m.getSQLType(kv[1]) if typ == "" { - logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) + beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } if i == 0 && strings.ToLower(kv[0]) != "id" { sql += "`id` int(11) NOT NULL AUTO_INCREMENT," tags = tags + "PRIMARY KEY (`id`)," } - sql += "`" + snakeString(kv[0]) + "` " + typ + "," + sql += "`" + utils.SnakeString(kv[0]) + "` " + typ + "," if tag != "" { - tags = tags + fmt.Sprintf(tag, "`"+snakeString(kv[0])+"`") + "," + tags = tags + fmt.Sprintf(tag, "`"+utils.SnakeString(kv[0])+"`") + "," } } sql = strings.TrimRight(sql+tags, ",") @@ -104,12 +108,12 @@ func (m mysqlDriver) getSQLType(ktype string) (tp, tag string) { type postgresqlDriver struct{} -func (m postgresqlDriver) generateCreateUp(tableName string) string { - upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(fields.String()) + `)");` +func (m postgresqlDriver) GenerateCreateUp(tableName string) string { + upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(Fields.String()) + `)");` return upsql } -func (m postgresqlDriver) generateCreateDown(tableName string) string { +func (m postgresqlDriver) GenerateCreateDown(tableName string) string { downsql := `m.SQL("DROP TABLE ` + tableName + `")` return downsql } @@ -120,20 +124,20 @@ func (m postgresqlDriver) generateSQLFromFields(fields string) string { for i, v := range fds { kv := strings.SplitN(v, ":", 2) if len(kv) != 2 { - logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) + beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } typ, tag := m.getSQLType(kv[1]) if typ == "" { - logger.Error("Fields format is wrong. Should be: key:type,key:type " + v) + beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v) return "" } if i == 0 && strings.ToLower(kv[0]) != "id" { sql += "id serial primary key," } - sql += snakeString(kv[0]) + " " + typ + "," + sql += utils.SnakeString(kv[0]) + " " + typ + "," if tag != "" { - tags = tags + fmt.Sprintf(tag, snakeString(kv[0])) + "," + tags = tags + fmt.Sprintf(tag, utils.SnakeString(kv[0])) + "," } } if tags != "" { @@ -170,14 +174,14 @@ func (m postgresqlDriver) getSQLType(ktype string) (tp, tag string) { return "", "" } -func newDBDriver() DBDriver { - switch driver { +func NewDBDriver() DBDriver { + switch SQLDriver { case "mysql": return mysqlDriver{} case "postgres": return postgresqlDriver{} default: - logger.Fatal("Driver not supported") + beeLogger.Log.Fatal("Driver not supported") return nil } } @@ -185,30 +189,30 @@ func newDBDriver() DBDriver { // generateMigration generates migration file template for database schema update. // The generated file template consists of an up() method for updating schema and // a down() method for reverting the update. -func generateMigration(mname, upsql, downsql, curpath string) { - w := NewColorWriter(os.Stdout) +func GenerateMigration(mname, upsql, downsql, curpath string) { + w := colors.NewColorWriter(os.Stdout) migrationFilePath := path.Join(curpath, DBPath, MPath) if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) { // create migrations directory if err := os.MkdirAll(migrationFilePath, 0777); err != nil { - logger.Fatalf("Could not create migration directory: %s", err) + beeLogger.Log.Fatalf("Could not create migration directory: %s", err) } } // create file today := time.Now().Format(MDateFormat) fpath := path.Join(migrationFilePath, fmt.Sprintf("%s_%s.go", today, mname)) if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { - defer CloseFile(f) - content := strings.Replace(MigrationTPL, "{{StructName}}", camelCase(mname)+"_"+today, -1) + defer utils.CloseFile(f) + content := strings.Replace(MigrationTPL, "{{StructName}}", utils.CamelCase(mname)+"_"+today, -1) content = strings.Replace(content, "{{CurrTime}}", today, -1) content = strings.Replace(content, "{{UpSQL}}", upsql, -1) content = strings.Replace(content, "{{DownSQL}}", downsql, -1) f.WriteString(content) // Run 'gofmt' on the generated source code - formatSourceCode(fpath) + utils.FormatSourceCode(fpath) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") } else { - logger.Fatalf("Could not create migration file: %s", err) + beeLogger.Log.Fatalf("Could not create migration file: %s", err) } } diff --git a/g_model.go b/generate/g_model.go similarity index 91% rename from g_model.go rename to generate/g_model.go index fc94309..e9b917c 100644 --- a/g_model.go +++ b/generate/g_model.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package generate import ( "errors" @@ -20,10 +20,14 @@ import ( "os" "path" "strings" + + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" ) -func generateModel(mname, fields, currpath string) { - w := NewColorWriter(os.Stdout) +func GenerateModel(mname, fields, currpath string) { + w := colors.NewColorWriter(os.Stdout) p, f := path.Split(mname) modelName := strings.Title(f) @@ -35,23 +39,23 @@ func generateModel(mname, fields, currpath string) { modelStruct, hastime, err := getStruct(modelName, fields) if err != nil { - logger.Fatalf("Could not generate the model struct: %s", err) + beeLogger.Log.Fatalf("Could not generate the model struct: %s", err) } - logger.Infof("Using '%s' as model name", modelName) - logger.Infof("Using '%s' as package name", packageName) + beeLogger.Log.Infof("Using '%s' as model name", modelName) + beeLogger.Log.Infof("Using '%s' as package name", packageName) fp := path.Join(currpath, "models", p) if _, err := os.Stat(fp); os.IsNotExist(err) { // Create the model's directory if err := os.MkdirAll(fp, 0777); err != nil { - logger.Fatalf("Could not create the model directory: %s", err) + beeLogger.Log.Fatalf("Could not create the model directory: %s", err) } } 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 CloseFile(f) + defer utils.CloseFile(f) content := strings.Replace(modelTpl, "{{packageName}}", packageName, -1) content = strings.Replace(content, "{{modelName}}", modelName, -1) content = strings.Replace(content, "{{modelStruct}}", modelStruct, -1) @@ -62,10 +66,10 @@ func generateModel(mname, fields, currpath string) { } f.WriteString(content) // Run 'gofmt' on the generated source code - formatSourceCode(fpath) + utils.FormatSourceCode(fpath) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m") } else { - logger.Fatalf("Could not create model file: %s", err) + beeLogger.Log.Fatalf("Could not create model file: %s", err) } } @@ -95,7 +99,7 @@ func getStruct(structname, fields string) (string, bool, error) { if hastimeinner { hastime = true } - structStr = structStr + camelString(kv[0]) + " " + typ + " " + tag + "\n" + structStr = structStr + utils.CamelString(kv[0]) + " " + typ + " " + tag + "\n" } structStr += "}\n" return structStr, hastime, nil diff --git a/generate/g_scaffold.go b/generate/g_scaffold.go new file mode 100644 index 0000000..6991d26 --- /dev/null +++ b/generate/g_scaffold.go @@ -0,0 +1,50 @@ +package generate + +import ( + "strings" + + "github.com/beego/bee/cmd/commands/migrate" + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/utils" +) + +func GenerateScaffold(sname, fields, currpath, driver, conn string) { + beeLogger.Log.Infof("Do you want to create a '%s' model? [Yes|No] ", sname) + + // Generate the model + if utils.AskForConfirmation() { + GenerateModel(sname, fields, currpath) + } + + // Generate the controller + beeLogger.Log.Infof("Do you want to create a '%s' controller? [Yes|No] ", sname) + if utils.AskForConfirmation() { + GenerateController(sname, currpath) + } + + // Generate the views + beeLogger.Log.Infof("Do you want to create views for this '%s' resource? [Yes|No] ", sname) + if utils.AskForConfirmation() { + GenerateView(sname, currpath) + } + + // Generate a migration + beeLogger.Log.Infof("Do you want to create a '%s' migration and schema for this resource? [Yes|No] ", sname) + if utils.AskForConfirmation() { + upsql := "" + downsql := "" + if fields != "" { + dbMigrator := NewDBDriver() + upsql = dbMigrator.GenerateCreateUp(sname) + downsql = dbMigrator.GenerateCreateDown(sname) + } + GenerateMigration(sname, upsql, downsql, currpath) + } + + // Run the migration + beeLogger.Log.Infof("Do you want to migrate the database? [Yes|No] ") + if utils.AskForConfirmation() { + migrate.MigrateUpdate(currpath, driver, conn) + } + beeLogger.Log.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname)) +} diff --git a/g_views.go b/generate/g_views.go similarity index 72% rename from g_views.go rename to generate/g_views.go index aa7815e..95d99bd 100644 --- a/g_views.go +++ b/generate/g_views.go @@ -12,60 +12,64 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package generate import ( "fmt" "os" "path" + + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" + "github.com/beego/bee/utils" ) // recipe // admin/recipe -func generateView(viewpath, currpath string) { - w := NewColorWriter(os.Stdout) +func GenerateView(viewpath, currpath string) { + w := colors.NewColorWriter(os.Stdout) - logger.Info("Generating view...") + beeLogger.Log.Info("Generating view...") absViewPath := path.Join(currpath, "views", viewpath) err := os.MkdirAll(absViewPath, os.ModePerm) if err != nil { - logger.Fatalf("Could not create '%s' view: %s", viewpath, err) + beeLogger.Log.Fatalf("Could not create '%s' view: %s", viewpath, err) } cfile := path.Join(absViewPath, "index.tpl") if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { - defer CloseFile(f) + defer utils.CloseFile(f) f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - logger.Fatalf("Could not create view file: %s", err) + beeLogger.Log.Fatalf("Could not create view file: %s", err) } cfile = path.Join(absViewPath, "show.tpl") if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { - defer CloseFile(f) + defer utils.CloseFile(f) f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - logger.Fatalf("Could not create view file: %s", err) + beeLogger.Log.Fatalf("Could not create view file: %s", err) } cfile = path.Join(absViewPath, "create.tpl") if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { - defer CloseFile(f) + defer utils.CloseFile(f) f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - logger.Fatalf("Could not create view file: %s", err) + beeLogger.Log.Fatalf("Could not create view file: %s", err) } cfile = path.Join(absViewPath, "edit.tpl") if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil { - defer CloseFile(f) + defer utils.CloseFile(f) f.WriteString(cfile) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m") } else { - logger.Fatalf("Could not create view file: %s", err) + beeLogger.Log.Fatalf("Could not create view file: %s", err) } } diff --git a/g_docs.go b/generate/swaggergen/g_docs.go similarity index 94% rename from g_docs.go rename to generate/swaggergen/g_docs.go index 23e3ed5..553d346 100644 --- a/g_docs.go +++ b/generate/swaggergen/g_docs.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package swaggergen import ( "encoding/json" @@ -35,6 +35,7 @@ import ( "github.com/astaxie/beego/swagger" "github.com/astaxie/beego/utils" + beeLogger "github.com/beego/bee/logger" ) const ( @@ -74,7 +75,7 @@ var basicTypes = map[string]string{ "byte": "string:byte", "rune": "string:byte", // builtin golang objects - "time.Time": "string:string", + "time.Time": "string:string", } var stdlibObject = map[string]string{ @@ -90,7 +91,7 @@ func init() { astPkgs = map[string]*ast.Package{} } -func parsePackagesFromDir(dirpath string) { +func ParsePackagesFromDir(dirpath string) { c := make(chan error) go func() { @@ -116,7 +117,7 @@ func parsePackagesFromDir(dirpath string) { }() for err := range c { - logger.Warnf("%s", err) + beeLogger.Log.Warnf("%s", err) } } @@ -137,12 +138,12 @@ func parsePackageFromDir(path string) error { return nil } -func generateDocs(curpath string) { +func GenerateDocs(curpath string) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments) if err != nil { - logger.Fatalf("Error while parsing router.go: %s", err) + // beeLogger.Log.Fatalf("Error while parsing router.go: %s", err) } rootapi.Infos = swagger.Information{} @@ -251,6 +252,9 @@ func generateDocs(curpath string) { } os.Mkdir(path.Join(curpath, "swagger"), 0755) fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json")) + if err != nil { + panic(err) + } fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml")) if err != nil { panic(err) @@ -348,7 +352,7 @@ func analyseControllerPkg(localName, pkgpath string) { } gopath := os.Getenv("GOPATH") if gopath == "" { - logger.Fatal("GOPATH environment variable is not set or empty") + // beeLogger.Log.Fatal("GOPATH environment variable is not set or empty") } pkgRealpath := "" @@ -366,7 +370,7 @@ func analyseControllerPkg(localName, pkgpath string) { } pkgCache[pkgpath] = struct{}{} } else { - logger.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath) + // beeLogger.Log.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath) } fileSet := token.NewFileSet() @@ -375,7 +379,7 @@ func analyseControllerPkg(localName, pkgpath string) { return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") }, parser.ParseComments) if err != nil { - logger.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err) + // beeLogger.Log.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err) } for _, pkg := range astPkgs { for _, fl := range pkg.Files { @@ -413,7 +417,7 @@ func isSystemPackage(pkgpath string) bool { goroot = runtime.GOROOT() } if goroot == "" { - logger.Fatalf("GOROOT environment variable is not set or empty") + // beeLogger.Log.Fatalf("GOROOT environment variable is not set or empty") } wg, _ := filepath.EvalSymlinks(filepath.Join(goroot, "src", "pkg", pkgpath)) @@ -423,11 +427,7 @@ func isSystemPackage(pkgpath string) bool { //TODO(zh):support go1.4 wg, _ = filepath.EvalSymlinks(filepath.Join(goroot, "src", pkgpath)) - if utils.FileExists(wg) { - return true - } - - return false + return utils.FileExists(wg) } func peekNextSplitString(ss string) (s string, spacePos int) { @@ -481,7 +481,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat ss = strings.TrimSpace(ss[pos:]) schemaName, pos := peekNextSplitString(ss) if schemaName == "" { - logger.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName) + // beeLogger.Log.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName) } if strings.HasPrefix(schemaName, "[]") { schemaName = schemaName[2:] @@ -496,7 +496,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat m, mod, realTypes := getModel(schemaName) schema.Ref = "#/definitions/" + m if _, ok := modelsList[pkgpath+controllerName]; !ok { - modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0) + modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema) } modelsList[pkgpath+controllerName][schemaName] = mod appendModels(pkgpath, controllerName, realTypes) @@ -518,7 +518,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat para := swagger.Parameter{} p := getparams(strings.TrimSpace(t[len("@Param "):])) if len(p) < 4 { - logger.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params") + // beeLogger.Log.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params") } para.Name = p[0] switch p[1] { @@ -533,7 +533,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat case "body": break default: - logger.Warnf("[%s.%s] Unknown param location: %s. Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1]) + // beeLogger.Log.Warnf("[%s.%s] Unknown param location: %s. Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1]) } para.In = p[1] pp := strings.Split(p[2], ".") @@ -544,7 +544,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat Ref: "#/definitions/" + m, } if _, ok := modelsList[pkgpath+controllerName]; !ok { - modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0) + modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema) } modelsList[pkgpath+controllerName][typ] = mod appendModels(pkgpath, controllerName, realTypes) @@ -564,7 +564,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat paraType = typeFormat[0] paraFormat = typeFormat[1] } else { - logger.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ) + // beeLogger.Log.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ) } if isArray { para.Type = "array" @@ -717,7 +717,7 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri } } if m.Title == "" { - logger.Warnf("Cannot find the object: %s", str) + // beeLogger.Log.Warnf("Cannot find the object: %s", str) // TODO remove when all type have been supported //os.Exit(1) } @@ -732,7 +732,7 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package, packageName string) { ts, ok := d.Decl.(*ast.TypeSpec) if !ok { - logger.Fatalf("Unknown type without TypeSec: %v\n", d) + // beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d) } // TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc... st, ok := ts.Type.(*ast.StructType) @@ -808,7 +808,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string mp.Default = str2RealType(res[1], realType) } else { - logger.Warnf("Invalid default value: %s", defaultValue) + // beeLogger.Log.Warnf("Invalid default value: %s", defaultValue) } } @@ -899,16 +899,6 @@ func isBasicType(Type string) bool { return false } -// regexp get json tag -func grepJSONTag(tag string) string { - r, _ := regexp.Compile(`json:"([^"]*)"`) - matches := r.FindAllStringSubmatch(tag, -1) - if len(matches) > 0 { - return matches[0][1] - } - return "" -} - // append models func appendModels(pkgpath, controllerName string, realTypes []string) { for _, realType := range realTypes { @@ -956,7 +946,7 @@ func str2RealType(s string, typ string) interface{} { } if err != nil { - logger.Warnf("Invalid default value type '%s': %s", typ, s) + // beeLogger.Log.Warnf("Invalid default value type '%s': %s", typ, s) return s } diff --git a/hproseapp.go b/hproseapp.go deleted file mode 100644 index 50ba2bd..0000000 --- a/hproseapp.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2013 bee authors -// -// 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 ( - "fmt" - "os" - path "path/filepath" - "strings" -) - -var cmdHproseapp = &Command{ - // CustomFlags: true, - UsageLine: "hprose [appname]", - Short: "Creates an RPC application based on Hprose and Beego frameworks", - Long: ` - The command 'hprose' creates an RPC application based on both Beego and Hprose (http://hprose.com/). - - {{"To scaffold out your application, use:"|bold}} - - $ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test] - - If 'conn' is empty, the command will generate a sample application. Otherwise the command - will connect to your database and generate models based on the existing tables. - - The command 'hprose' creates a folder named [appname] with the following structure: - - ├── main.go - ├── {{"conf"|foldername}} - │ └── app.conf - └── {{"models"|foldername}} - └── object.go - └── user.go -`, - PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, - Run: createhprose, -} - -var hproseconf = `appname = {{.Appname}} -httpport = 8080 -runmode = dev -autorender = false -copyrequestbody = true -EnableDocs = true -` -var hproseMaingo = `package main - -import ( - "fmt" - "reflect" - - "{{.Appname}}/models" - "github.com/hprose/hprose-golang/rpc" - - "github.com/astaxie/beego" -) - -func logInvokeHandler( - name string, - args []reflect.Value, - context rpc.Context, - next rpc.NextInvokeHandler) (results []reflect.Value, err error) { - fmt.Printf("%s(%v) = ", name, args) - results, err = next(name, args, context) - fmt.Printf("%v %v\r\n", results, err) - return -} - -func main() { - // Create WebSocketServer - // service := rpc.NewWebSocketService() - - // Create Http Server - service := rpc.NewHTTPService() - - // Use Logger Middleware - service.AddInvokeHandler(logInvokeHandler) - - // Publish Functions - service.AddFunction("AddOne", models.AddOne) - service.AddFunction("GetOne", models.GetOne) - - // Start Service - beego.Handler("/", service) - beego.Run() -} -` - -var hproseMainconngo = `package main - -import ( - "fmt" - "reflect" - - "{{.Appname}}/models" - "github.com/hprose/hprose-golang/rpc" - - "github.com/astaxie/beego" - "github.com/astaxie/beego/orm" - {{.DriverPkg}} -) - -func init() { - orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}") -} - -func logInvokeHandler( - name string, - args []reflect.Value, - context rpc.Context, - next rpc.NextInvokeHandler) (results []reflect.Value, err error) { - fmt.Printf("%s(%v) = ", name, args) - results, err = next(name, args, context) - fmt.Printf("%v %v\r\n", results, err) - return -} - -func main() { - // Create WebSocketServer - // service := rpc.NewWebSocketService() - - // Create Http Server - service := rpc.NewHTTPService() - - // Use Logger Middleware - service.AddInvokeHandler(logInvokeHandler) - - {{HproseFunctionList}} - - // Start Service - beego.Handler("/", service) - beego.Run() -} - -` - -var hproseModels = `package models - -import ( - "errors" - "strconv" - "time" -) - -var ( - Objects map[string]*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"} -} - -func AddOne(object 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) { - if v, ok := Objects[ObjectId]; ok { - return v, nil - } - return nil, errors.New("ObjectId Not Exist") -} - -func GetAll() map[string]*Object { - return Objects -} - -func Update(ObjectId string, Score int64) (err error) { - if v, ok := Objects[ObjectId]; ok { - v.Score = Score - return nil - } - return errors.New("ObjectId Not Exist") -} - -func Delete(ObjectId string) { - delete(Objects, ObjectId) -} - -` - -var hproseModels2 = `package models - -import ( - "errors" - "strconv" - "time" -) - -var ( - UserList map[string]*User -) - -func init() { - UserList = make(map[string]*User) - u := User{"user_11111", "astaxie", "11111", 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 { - u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10) - UserList[u.Id] = &u - return u.Id -} - -func GetUser(uid string) (u *User, err error) { - if u, ok := UserList[uid]; ok { - return u, nil - } - return nil, errors.New("User not exists") -} - -func GetAllUsers() map[string]*User { - return UserList -} - -func UpdateUser(uid string, uu *User) (a *User, err error) { - if u, ok := UserList[uid]; ok { - if uu.Username != "" { - u.Username = uu.Username - } - if uu.Password != "" { - u.Password = uu.Password - } - if uu.Profile.Age != 0 { - u.Profile.Age = uu.Profile.Age - } - if uu.Profile.Address != "" { - u.Profile.Address = uu.Profile.Address - } - if uu.Profile.Gender != "" { - u.Profile.Gender = uu.Profile.Gender - } - if uu.Profile.Email != "" { - u.Profile.Email = uu.Profile.Email - } - return u, nil - } - return nil, errors.New("User Not Exist") -} - -func Login(username, password string) bool { - for _, u := range UserList { - if u.Username == username && u.Password == password { - return true - } - } - return false -} - -func DeleteUser(uid string) { - delete(UserList, uid) -} -` - -var hproseAddFunctions = []string{} - -func init() { - cmdHproseapp.Flag.Var(&tables, "tables", "List of table names separated by a comma.") - cmdHproseapp.Flag.Var(&driver, "driver", "Database driver. Either mysql, postgres or sqlite.") - cmdHproseapp.Flag.Var(&conn, "conn", "Connection string used by the driver to connect to a database instance.") -} - -func createhprose(cmd *Command, args []string) int { - output := cmd.Out() - - if len(args) != 1 { - logger.Fatal("Argument [appname] is missing") - } - - curpath, _ := os.Getwd() - if len(args) > 1 { - cmd.Flag.Parse(args[1:]) - } - apppath, packpath, err := checkEnv(args[0]) - if err != nil { - logger.Fatalf("%s", err) - } - if driver == "" { - driver = "mysql" - } - if conn == "" { - } - - logger.Info("Creating Hprose application...") - - os.MkdirAll(apppath, 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") - os.Mkdir(path.Join(apppath, "conf"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m") - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") - WriteToFile(path.Join(apppath, "conf", "app.conf"), - strings.Replace(hproseconf, "{{.Appname}}", args[0], -1)) - - if conn != "" { - logger.Infof("Using '%s' as 'driver'", driver) - logger.Infof("Using '%s' as 'conn'", conn) - logger.Infof("Using '%s' as 'tables'", tables) - generateHproseAppcode(string(driver), string(conn), "1", string(tables), path.Join(curpath, args[0])) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") - maingoContent := strings.Replace(hproseMainconngo, "{{.Appname}}", packpath, -1) - maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1) - maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(hproseAddFunctions, ""), -1) - if driver == "mysql" { - maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1) - } else if driver == "postgres" { - maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1) - } - WriteToFile(path.Join(apppath, "main.go"), - strings.Replace( - maingoContent, - "{{.conn}}", - conn.String(), - -1, - ), - ) - } else { - os.Mkdir(path.Join(apppath, "models"), 0755) - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "models", "object.go"), apiModels) - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2) - - fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") - WriteToFile(path.Join(apppath, "main.go"), - strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1)) - } - logger.Success("New Hprose application successfully created!") - return 0 -} diff --git a/color.go b/logger/colors/color.go similarity index 77% rename from color.go rename to logger/colors/color.go index 4057961..576280e 100644 --- a/color.go +++ b/logger/colors/color.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package colors import ( "fmt" @@ -53,7 +53,7 @@ func NewModeColorWriter(w io.Writer, mode outputMode) io.Writer { return w } -func bold(message string) string { +func Bold(message string) string { return fmt.Sprintf("\x1b[1m%s\x1b[21m", message) } @@ -102,47 +102,47 @@ func Magenta(message string) string { return fmt.Sprintf("\x1b[35m%s\x1b[0m", message) } -// BlackBold returns a black bold string +// BlackBold returns a black Bold string func BlackBold(message string) string { - return fmt.Sprintf("\x1b[30m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[30m%s\x1b[0m", Bold(message)) } -// WhiteBold returns a white bold string +// WhiteBold returns a white Bold string func WhiteBold(message string) string { - return fmt.Sprintf("\x1b[37m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[37m%s\x1b[0m", Bold(message)) } -// CyanBold returns a cyan bold string +// CyanBold returns a cyan Bold string func CyanBold(message string) string { - return fmt.Sprintf("\x1b[36m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[36m%s\x1b[0m", Bold(message)) } -// BlueBold returns a blue bold string +// BlueBold returns a blue Bold string func BlueBold(message string) string { - return fmt.Sprintf("\x1b[34m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[34m%s\x1b[0m", Bold(message)) } -// RedBold returns a red bold string +// RedBold returns a red Bold string func RedBold(message string) string { - return fmt.Sprintf("\x1b[31m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[31m%s\x1b[0m", Bold(message)) } -// GreenBold returns a green bold string +// GreenBold returns a green Bold string func GreenBold(message string) string { - return fmt.Sprintf("\x1b[32m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[32m%s\x1b[0m", Bold(message)) } -// YellowBold returns a yellow bold string +// YellowBold returns a yellow Bold string func YellowBold(message string) string { - return fmt.Sprintf("\x1b[33m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[33m%s\x1b[0m", Bold(message)) } -// GrayBold returns a gray bold string +// GrayBold returns a gray Bold string func GrayBold(message string) string { - return fmt.Sprintf("\x1b[37m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[37m%s\x1b[0m", Bold(message)) } -// MagentaBold returns a magenta bold string +// MagentaBold returns a magenta Bold string func MagentaBold(message string) string { - return fmt.Sprintf("\x1b[35m%s\x1b[0m", bold(message)) + return fmt.Sprintf("\x1b[35m%s\x1b[0m", Bold(message)) } diff --git a/colorwriter.go b/logger/colors/colorwriter.go similarity index 98% rename from colorwriter.go rename to logger/colors/colorwriter.go index 86201e8..07e69a1 100644 --- a/colorwriter.go +++ b/logger/colors/colorwriter.go @@ -14,7 +14,7 @@ // +build !windows -package main +package colors import "io" diff --git a/colorwriter_windows.go b/logger/colors/colorwriter_windows.go similarity index 99% rename from colorwriter_windows.go rename to logger/colors/colorwriter_windows.go index 7c9e709..d4c567a 100644 --- a/colorwriter_windows.go +++ b/logger/colors/colorwriter_windows.go @@ -14,7 +14,7 @@ // +build windows -package main +package colors import ( "bytes" diff --git a/logger.go b/logger/logger.go similarity index 86% rename from logger.go rename to logger/logger.go index e9e081f..5b15d81 100644 --- a/logger.go +++ b/logger/logger.go @@ -11,7 +11,7 @@ // 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 +package beeLogger import ( "errors" @@ -22,6 +22,9 @@ import ( "sync" "sync/atomic" "text/template" + "time" + + "github.com/beego/bee/logger/colors" ) var errInvalidLogLevel = errors.New("logger: invalid log level") @@ -59,6 +62,8 @@ type LogRecord struct { LineNo int } +var Log = GetBeeLogger(os.Stdout) + var ( logRecordTemplate *template.Template debugLogRecordTemplate *template.Template @@ -80,11 +85,15 @@ func GetBeeLogger(w io.Writer) *BeeLogger { "EndLine": EndLine, } logRecordTemplate, err = template.New("simpleLogFormat").Funcs(funcs).Parse(simpleLogFormat) - MustCheck(err) + if err != nil { + panic(err) + } debugLogRecordTemplate, err = template.New("debugLogFormat").Funcs(funcs).Parse(debugLogFormat) - MustCheck(err) + if err != nil { + panic(err) + } - instance = &BeeLogger{output: NewColorWriter(w)} + instance = &BeeLogger{output: colors.NewColorWriter(w)} }) return instance } @@ -93,7 +102,17 @@ func GetBeeLogger(w io.Writer) *BeeLogger { func (l *BeeLogger) SetOutput(w io.Writer) { l.mu.Lock() defer l.mu.Unlock() - l.output = NewColorWriter(w) + l.output = colors.NewColorWriter(w) +} + +// Now returns the current local time in the specified layout +func Now(layout string) string { + return time.Now().Format(layout) +} + +// EndLine returns the a newline escape character +func EndLine() string { + return "\n" } func (l *BeeLogger) getLevelTag(level int) string { @@ -122,21 +141,21 @@ func (l *BeeLogger) getLevelTag(level int) string { func (l *BeeLogger) getColorLevel(level int) string { switch level { case levelCritical: - return RedBold(l.getLevelTag(level)) + return colors.RedBold(l.getLevelTag(level)) case levelFatal: - return RedBold(l.getLevelTag(level)) + return colors.RedBold(l.getLevelTag(level)) case levelInfo: - return BlueBold(l.getLevelTag(level)) + return colors.BlueBold(l.getLevelTag(level)) case levelHint: - return CyanBold(l.getLevelTag(level)) + return colors.CyanBold(l.getLevelTag(level)) case levelDebug: - return YellowBold(l.getLevelTag(level)) + return colors.YellowBold(l.getLevelTag(level)) case levelError: - return RedBold(l.getLevelTag(level)) + return colors.RedBold(l.getLevelTag(level)) case levelWarn: - return YellowBold(l.getLevelTag(level)) + return colors.YellowBold(l.getLevelTag(level)) case levelSuccess: - return GreenBold(l.getLevelTag(level)) + return colors.GreenBold(l.getLevelTag(level)) default: panic(errInvalidLogLevel) } @@ -157,15 +176,17 @@ func (l *BeeLogger) mustLog(level int, message string, args ...interface{}) { } err := logRecordTemplate.Execute(l.output, record) - MustCheck(err) + if err != nil { + panic(err) + } } // mustLogDebug logs a debug message only if debug mode // is enabled. i.e. DEBUG_ENABLED="1" func (l *BeeLogger) mustLogDebug(message string, file string, line int, args ...interface{}) { - if !IsDebugEnabled() { - return - } + //if !IsDebugEnabled() { + // return + //} // Change the output to Stderr l.SetOutput(os.Stderr) @@ -179,7 +200,9 @@ func (l *BeeLogger) mustLogDebug(message string, file string, line int, args ... Filename: filepath.Base(file), } err := debugLogRecordTemplate.Execute(l.output, record) - MustCheck(err) + if err != nil { + panic(err) + } } // Debug outputs a debug log message diff --git a/main.go b/main.go new file mode 100644 index 0000000..f7b941f --- /dev/null +++ b/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "flag" + "log" + "os" + "strings" + + "github.com/beego/bee/cmd" + "github.com/beego/bee/cmd/commands" + "github.com/beego/bee/generate/swaggergen" + "github.com/beego/bee/utils" +) + +func main() { + currentpath, _ := os.Getwd() + + flag.Usage = cmd.Usage + flag.Parse() + log.SetFlags(0) + + args := flag.Args() + + if len(args) < 1 { + cmd.Usage() + } + + if args[0] == "help" { + cmd.Help(args[1:]) + return + } + + for _, c := range commands.AvailableCommands { + if c.Name() == args[0] && c.Run != nil { + c.Flag.Usage = func() { c.Usage() } + if c.CustomFlags { + args = args[1:] + } else { + c.Flag.Parse(args[1:]) + args = c.Flag.Args() + } + + if c.PreRun != nil { + c.PreRun(c, args) + } + + // Check if current directory is inside the GOPATH, + // if so parse the packages inside it. + if strings.Contains(currentpath, utils.GetGOPATHs()[0]+"/src") && cmd.IfGenerateDocs(c.Name(), args) { + swaggergen.ParsePackagesFromDir(currentpath) + } + + os.Exit(c.Run(c, args)) + return + } + } + + utils.PrintErrorAndExit("Unknown subcommand", cmd.ErrorTemplate) +} diff --git a/rundocs.go b/rundocs.go deleted file mode 100644 index 645d391..0000000 --- a/rundocs.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2013 bee authors -// -// 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 ( - "archive/zip" - "fmt" - "io" - "net/http" - "os" - "strings" -) - -var cmdRundocs = &Command{ - UsageLine: "rundocs [-isDownload=true] [-docport=8888]", - Short: "rundocs will run the docs server,default is 8089", - Long: ` --d meaning will download the docs file from github --p meaning server the Server on which port, default is 8089 - -`, -} - -var ( - swaggerVersion = "2" - swaggerlink = "https://github.com/beego/swagger/archive/v" + swaggerVersion + ".zip" -) - -type docValue string - -func (d *docValue) String() string { - return fmt.Sprint(*d) -} - -func (d *docValue) Set(value string) error { - *d = docValue(value) - return nil -} - -var isDownload docValue -var docport docValue - -func init() { - cmdRundocs.Run = runDocs - cmdRundocs.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } - cmdRundocs.Flag.Var(&isDownload, "isDownload", "weather download the Swagger Docs") - cmdRundocs.Flag.Var(&docport, "docport", "doc server port") -} - -func runDocs(cmd *Command, args []string) int { - if isDownload == "true" { - downloadFromURL(swaggerlink, "swagger.zip") - err := unzipAndDelete("swagger.zip") - if err != nil { - logger.Errorf("Error while unzipping 'swagger.zip' file: %s", err) - } - } - if docport == "" { - docport = "8089" - } - if _, err := os.Stat("swagger"); err != nil && os.IsNotExist(err) { - logger.Fatal("No Swagger dist found. Run: bee rundocs -isDownload=true") - } - - logger.Infof("Starting the docs server on: http://127.0.0.1:%s", docport) - - err := http.ListenAndServe(":"+string(docport), http.FileServer(http.Dir("swagger"))) - if err != nil { - logger.Fatalf("%s", err) - } - return 0 -} - -func downloadFromURL(url, fileName string) { - var down bool - if fd, err := os.Stat(fileName); err != nil && os.IsNotExist(err) { - down = true - } else if fd.Size() == int64(0) { - down = true - } else { - logger.Infof("'%s' already exists", fileName) - return - } - if down { - logger.Infof("Downloading '%s' to '%s'...", url, fileName) - output, err := os.Create(fileName) - if err != nil { - logger.Errorf("Error while creating '%s': %s", fileName, err) - return - } - defer output.Close() - - response, err := http.Get(url) - if err != nil { - logger.Errorf("Error while downloading '%s': %s", url, err) - return - } - defer response.Body.Close() - - n, err := io.Copy(output, response.Body) - if err != nil { - logger.Errorf("Error while downloading '%s': %s", url, err) - return - } - logger.Successf("%d bytes downloaded!", n) - } -} - -func unzipAndDelete(src string) error { - logger.Infof("Unzipping '%s'...", src) - r, err := zip.OpenReader(src) - if err != nil { - return err - } - defer r.Close() - - rp := strings.NewReplacer("swagger-"+swaggerVersion, "swagger") - for _, f := range r.File { - rc, err := f.Open() - if err != nil { - return err - } - defer rc.Close() - - fname := rp.Replace(f.Name) - if f.FileInfo().IsDir() { - os.MkdirAll(fname, f.Mode()) - } else { - f, err := os.OpenFile( - fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - return err - } - defer f.Close() - - _, err = io.Copy(f, rc) - if err != nil { - return err - } - } - } - logger.Successf("Done! Deleting '%s'...", src) - return os.RemoveAll(src) -} diff --git a/test.go b/test.go deleted file mode 100644 index f8be62b..0000000 --- a/test.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2013 bee authors -// -// 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 ( - "os" - "os/exec" - path "path/filepath" - "time" - - _ "github.com/smartystreets/goconvey/convey" -) - -var cmdTest = &Command{ - UsageLine: "test [appname]", - Short: "test the app", - Long: ``, -} - -func init() { - cmdTest.Run = testApp - cmdTest.PreRun = func(cmd *Command, args []string) { ShowShortVersionBanner() } -} - -func safePathAppend(arr []string, paths ...string) []string { - for _, path := range paths { - if pathExists(path) { - arr = append(arr, path) - } - } - return arr -} - -func pathExists(path string) bool { - _, err := os.Stat(path) - return err == nil || os.IsExist(err) -} - -var started = make(chan bool) - -func testApp(cmd *Command, args []string) int { - if len(args) != 1 { - logger.Fatalf("Failed to start: %s", "argument 'appname' is missing") - } - - currpath, _ := os.Getwd() - - logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath) - - err := loadConfig() - if err != nil { - logger.Fatalf("Failed to load configuration: %s", err) - } - var paths []string - readAppDirectories(currpath, &paths) - - NewWatcher(paths, nil, false) - appname = args[0] - for { - select { - case <-started: - runTest() - } - } -} - -func runTest() { - logger.Info("Start testing...") - time.Sleep(time.Second * 1) - crupwd, _ := os.Getwd() - testDir := path.Join(crupwd, "tests") - if pathExists(testDir) { - os.Chdir(testDir) - } - - var err error - icmd := exec.Command("go", "test") - icmd.Stdout = os.Stdout - icmd.Stderr = os.Stderr - logger.Info("============== Test Begin ===================") - err = icmd.Run() - logger.Info("============== Test End =====================") - - if err != nil { - logger.Error("============== Test failed ===================") - logger.Errorf("Cause: %s", err) - return - } - logger.Success("Test Completed") -} diff --git a/testdata/router/router.go b/testdata/router/router.go deleted file mode 100644 index 8a54102..0000000 --- a/testdata/router/router.go +++ /dev/null @@ -1,28 +0,0 @@ -package router - -import ( - "github.com/astaxie/beego" -) - -type Router struct { - beego.Controller -} - -func (r *Router) Get() { - -} - -func (r *Router) Post() { - -} - -type Controller struct { -} - -func (c *Controller) Put() { - -} - -func (c *Controller) Delete() { - -} diff --git a/utils/doc_value.go b/utils/doc_value.go new file mode 100644 index 0000000..daf407d --- /dev/null +++ b/utils/doc_value.go @@ -0,0 +1,14 @@ +package utils + +import "fmt" + +type DocValue string + +func (d *DocValue) String() string { + return fmt.Sprint(*d) +} + +func (d *DocValue) Set(value string) error { + *d = DocValue(value) + return nil +} diff --git a/utils/list_opts.go b/utils/list_opts.go new file mode 100644 index 0000000..28c2a10 --- /dev/null +++ b/utils/list_opts.go @@ -0,0 +1,14 @@ +package utils + +import "fmt" + +type ListOpts []string + +func (opts *ListOpts) String() string { + return fmt.Sprint(*opts) +} + +func (opts *ListOpts) Set(value string) error { + *opts = append(*opts, value) + return nil +} diff --git a/utils/str_flag.go b/utils/str_flag.go new file mode 100644 index 0000000..e65fc4e --- /dev/null +++ b/utils/str_flag.go @@ -0,0 +1,15 @@ +package utils + +import "fmt" + +// The string flag list, implemented flag.Value interface +type StrFlags []string + +func (s *StrFlags) String() string { + return fmt.Sprintf("%s", *s) +} + +func (s *StrFlags) Set(value string) error { + *s = append(*s, value) + return nil +} diff --git a/util.go b/utils/utils.go similarity index 73% rename from util.go rename to utils/utils.go index 0893144..03a94c8 100644 --- a/util.go +++ b/utils/utils.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package main +package utils import ( "bytes" @@ -26,7 +26,9 @@ import ( "runtime" "strings" "text/template" - "time" + + beeLogger "github.com/beego/bee/logger" + "github.com/beego/bee/logger/colors" ) // Go is a basic promise implementation: it wraps calls a function in a goroutine @@ -39,18 +41,8 @@ func Go(f func() error) chan error { return ch } -// Now returns the current local time in the specified layout -func Now(layout string) string { - return time.Now().Format(layout) -} - -// EndLine returns the a newline escape character -func EndLine() string { - return "\n" -} - // IsExist returns whether a file or directory exists. -func isExist(path string) bool { +func IsExist(path string) bool { _, err := os.Stat(path) return err == nil || os.IsExist(err) } @@ -99,7 +91,7 @@ func IsBeegoProject(thePath string) bool { }() if err := <-c; err != nil { - logger.Fatalf("Unable to walk '%s' tree: %s", thePath, err) + beeLogger.Log.Fatalf("Unable to walk '%s' tree: %s", thePath, err) } if len(mainFiles) > 0 { @@ -113,7 +105,7 @@ func IsBeegoProject(thePath string) bool { func SearchGOPATHs(app string) (bool, string, string) { gps := GetGOPATHs() if len(gps) == 0 { - logger.Fatal("GOPATH environment variable is not set or empty") + beeLogger.Log.Fatal("GOPATH environment variable is not set or empty") } // Lookup the application inside the user workspace(s) @@ -127,7 +119,7 @@ func SearchGOPATHs(app string) (bool, string, string) { currentPath = app } - if isExist(currentPath) { + if IsExist(currentPath) { return true, gopath, currentPath } } @@ -139,11 +131,11 @@ func SearchGOPATHs(app string) (bool, string, string) { // confirmations. If the input is not recognized, it will ask again. The function does not return // until it gets a valid response from the user. Typically, you should use fmt to print out a question // before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)") -func askForConfirmation() bool { +func AskForConfirmation() bool { var response string _, err := fmt.Scanln(&response) if err != nil { - logger.Fatalf("%s", err) + beeLogger.Log.Fatalf("%s", err) } okayResponses := []string{"y", "Y", "yes", "Yes", "YES"} nokayResponses := []string{"n", "N", "no", "No", "NO"} @@ -153,7 +145,7 @@ func askForConfirmation() bool { return false } else { fmt.Println("Please type yes or no and then press enter:") - return askForConfirmation() + return AskForConfirmation() } } @@ -167,7 +159,7 @@ func containsString(slice []string, element string) bool { } // snake string, XxYy to xx_yy -func snakeString(s string) string { +func SnakeString(s string) string { data := make([]byte, 0, len(s)*2) j := false num := len(s) @@ -184,17 +176,17 @@ func snakeString(s string) string { return strings.ToLower(string(data[:])) } -func camelString(s string) string { +func CamelString(s string) string { data := make([]byte, 0, len(s)) j := false k := false num := len(s) - 1 for i := 0; i <= num; i++ { d := s[i] - if k == false && d >= 'A' && d <= 'Z' { + if !k && d >= 'A' && d <= 'Z' { k = true } - if d >= 'a' && d <= 'z' && (j || k == false) { + if d >= 'a' && d <= 'z' && (j || !k) { d = d - 32 j = false k = true @@ -210,7 +202,7 @@ func camelString(s string) string { // camelCase converts a _ delimited string to camel case // e.g. very_important_person => VeryImportantPerson -func camelCase(in string) string { +func CamelCase(in string) string { tokens := strings.Split(in, "_") for i := range tokens { tokens[i] = strings.Title(strings.Trim(tokens[i], " ")) @@ -219,25 +211,13 @@ func camelCase(in string) string { } // formatSourceCode formats source files -func formatSourceCode(filename string) { +func FormatSourceCode(filename string) { cmd := exec.Command("gofmt", "-w", filename) if err := cmd.Run(); err != nil { - logger.Warnf("Error while running gofmt: %s", err) + beeLogger.Log.Warnf("Error while running gofmt: %s", err) } } -// The string flag list, implemented flag.Value interface -type strFlags []string - -func (s *strFlags) String() string { - return fmt.Sprintf("%s", *s) -} - -func (s *strFlags) Set(value string) error { - *s = append(*s, value) - return nil -} - // CloseFile attempts to close the passed file // or panics with the actual error func CloseFile(f *os.File) { @@ -252,11 +232,6 @@ func MustCheck(err error) { } } -func exitPrint(con string) { - fmt.Fprintln(os.Stderr, con) - os.Exit(2) -} - // WriteToFile creates a file and writes content to it func WriteToFile(filename, content string) { f, err := os.Create(filename) @@ -273,13 +248,13 @@ func IsDebugEnabled() bool { } // __FILE__ returns the file name in which the function was invoked -func __FILE__() string { +func FILE() string { _, file, _, _ := runtime.Caller(1) return file } // __LINE__ returns the line number at which the function was invoked -func __LINE__() int { +func LINE() int { _, _, line, _ := runtime.Caller(1) return line } @@ -288,9 +263,9 @@ func __LINE__() int { func BeeFuncMap() template.FuncMap { return template.FuncMap{ "trim": strings.TrimSpace, - "bold": bold, - "headline": MagentaBold, - "foldername": RedBold, + "bold": colors.Bold, + "headline": colors.MagentaBold, + "foldername": colors.RedBold, "endline": EndLine, "tmpltostr": TmplToString, } @@ -307,3 +282,59 @@ func TmplToString(tmpl string, data interface{}) string { return doc.String() } + +// EndLine returns the a newline escape character +func EndLine() string { + return "\n" +} + +func Tmpl(text string, data interface{}) { + output := colors.NewColorWriter(os.Stderr) + + t := template.New("Usage").Funcs(BeeFuncMap()) + template.Must(t.Parse(text)) + + err := t.Execute(output, data) + if err != nil { + beeLogger.Log.Error(err.Error()) + } +} + +func CheckEnv(appname string) (apppath, packpath string, err error) { + gps := GetGOPATHs() + if len(gps) == 0 { + beeLogger.Log.Fatal("GOPATH environment variable is not set or empty") + } + currpath, _ := os.Getwd() + currpath = path.Join(currpath, appname) + for _, gpath := range gps { + gsrcpath := path.Join(gpath, "src") + if strings.HasPrefix(currpath, gsrcpath) { + packpath = strings.Replace(currpath[len(gsrcpath)+1:], string(filepath.Separator), "/", -1) + return currpath, packpath, nil + } + } + + // In case of multiple paths in the GOPATH, by default + // we use the first path + gopath := gps[0] + + beeLogger.Log.Warn("You current workdir is not inside $GOPATH/src.") + beeLogger.Log.Debugf("GOPATH: %s", FILE(), LINE(), gopath) + + gosrcpath := path.Join(gopath, "src") + apppath = path.Join(gosrcpath, appname) + + if _, e := os.Stat(apppath); !os.IsNotExist(e) { + err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath) + beeLogger.Log.Errorf("Path '%s' already exists", apppath) + return + } + packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(filepath.Separator)), "/") + return +} + +func PrintErrorAndExit(message, errorTemplate string) { + Tmpl(fmt.Sprintf(errorTemplate, message), nil) + os.Exit(2) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go index f79d0c0..8ea2807 100644 --- a/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go @@ -23,7 +23,7 @@ func transformDescription(m Matcher, newDesc string) Matcher { } type transformDescriptionMatcher struct { - desc string + desc string wrappedMatcher Matcher }