mirror of
https://github.com/beego/bee.git
synced 2024-11-22 15:10:54 +00:00
Merge pull request #1 from astaxie/master
merge with asterxie/bee master
This commit is contained in:
commit
d8481e756f
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.DS_Store
|
||||||
|
bee
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
@ -1,5 +1,10 @@
|
|||||||
bee
|
bee
|
||||||
===
|
===
|
||||||
|
|
||||||
[![Build Status](https://drone.io/github.com/astaxie/bee/status.png)](https://drone.io/github.com/astaxie/bee/latest)
|
[![Build Status](https://drone.io/github.com/astaxie/bee/status.png)](https://drone.io/github.com/astaxie/bee/latest)
|
||||||
|
|
||||||
Bee is a tool for managing beego framework.
|
Bee is a tool for managing beego framework.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
285
apiapp.go
Normal file
285
apiapp.go
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
path "path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdApiapp = &Command{
|
||||||
|
// CustomFlags: true,
|
||||||
|
UsageLine: "api [appname]",
|
||||||
|
Short: "create an api application base on beego framework",
|
||||||
|
Long: `
|
||||||
|
create an api application base on beego framework
|
||||||
|
|
||||||
|
In the current path, will create a folder named [appname]
|
||||||
|
|
||||||
|
In the appname folder has the follow struct:
|
||||||
|
|
||||||
|
├── conf
|
||||||
|
│ └── app.conf
|
||||||
|
├── controllers
|
||||||
|
│ └── default.go
|
||||||
|
├── main.go
|
||||||
|
└── models
|
||||||
|
└── object.go
|
||||||
|
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiconf = `
|
||||||
|
appname = {{.Appname}}
|
||||||
|
httpport = 8080
|
||||||
|
runmode = dev
|
||||||
|
autorender = false
|
||||||
|
copyrequestbody = true
|
||||||
|
`
|
||||||
|
var apiMaingo = `package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"{{.Appname}}/controllers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Objects
|
||||||
|
|
||||||
|
// URL HTTP Verb Functionality
|
||||||
|
// /object POST Creating Objects
|
||||||
|
// /object/<objectId> GET Retrieving Objects
|
||||||
|
// /object/<objectId> PUT Updating Objects
|
||||||
|
// /object GET Queries
|
||||||
|
// /object/<objectId> DELETE Deleting Objects
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
beego.RESTRouter("/object", &controllers.ObejctController{})
|
||||||
|
beego.Router("/ping", &controllers.ObejctController{},"get:Ping")
|
||||||
|
beego.Run()
|
||||||
|
}
|
||||||
|
`
|
||||||
|
var apiModels = `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 apiControllers = `package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"{{.Appname}}/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResponseInfo struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObejctController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ObejctController) Post() {
|
||||||
|
var ob models.Object
|
||||||
|
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
||||||
|
objectid := models.AddOne(ob)
|
||||||
|
this.Data["json"] = map[string]string{"ObjectId": objectid}
|
||||||
|
this.ServeJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ObejctController) Get() {
|
||||||
|
objectId := this.Ctx.Params[":objectId"]
|
||||||
|
if objectId != "" {
|
||||||
|
ob, err := models.GetOne(objectId)
|
||||||
|
if err != nil {
|
||||||
|
this.Data["json"] = err
|
||||||
|
} else {
|
||||||
|
this.Data["json"] = ob
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obs := models.GetAll()
|
||||||
|
this.Data["json"] = obs
|
||||||
|
}
|
||||||
|
this.ServeJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ObejctController) Put() {
|
||||||
|
objectId := this.Ctx.Params[":objectId"]
|
||||||
|
var ob models.Object
|
||||||
|
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
||||||
|
|
||||||
|
err := models.Update(objectId, ob.Score)
|
||||||
|
if err != nil {
|
||||||
|
this.Data["json"] = err
|
||||||
|
} else {
|
||||||
|
this.Data["json"] = "update success!"
|
||||||
|
}
|
||||||
|
this.ServeJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ObejctController) Delete() {
|
||||||
|
objectId := this.Ctx.Input.Params[":objectId"]
|
||||||
|
models.Delete(objectId)
|
||||||
|
this.Data["json"] = "delete success!"
|
||||||
|
this.ServeJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ObejctController) Ping() {
|
||||||
|
this.Ctx.WriteString("pong")
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
var apiTests = `package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
beetest "github.com/astaxie/beego/testing"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHelloWorld(t *testing.T) {
|
||||||
|
request:=beetest.Get("/ping")
|
||||||
|
response,_:=request.Response()
|
||||||
|
defer response.Body.Close()
|
||||||
|
contents, _ := ioutil.ReadAll(response.Body)
|
||||||
|
if string(contents)!="pong"{
|
||||||
|
t.Errorf("response sould be pong")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdApiapp.Run = createapi
|
||||||
|
}
|
||||||
|
|
||||||
|
func createapi(cmd *Command, args []string) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
fmt.Println("error args")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
apppath, packpath, err := checkEnv(args[0])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
os.MkdirAll(apppath, 0755)
|
||||||
|
fmt.Println("create app folder:", apppath)
|
||||||
|
os.Mkdir(path.Join(apppath, "conf"), 0755)
|
||||||
|
fmt.Println("create conf:", path.Join(apppath, "conf"))
|
||||||
|
os.Mkdir(path.Join(apppath, "controllers"), 0755)
|
||||||
|
fmt.Println("create controllers:", path.Join(apppath, "controllers"))
|
||||||
|
os.Mkdir(path.Join(apppath, "models"), 0755)
|
||||||
|
fmt.Println("create models:", path.Join(apppath, "models"))
|
||||||
|
os.Mkdir(path.Join(apppath, "tests"), 0755)
|
||||||
|
fmt.Println("create tests:", path.Join(apppath, "tests"))
|
||||||
|
|
||||||
|
fmt.Println("create conf app.conf:", path.Join(apppath, "conf", "app.conf"))
|
||||||
|
writetofile(path.Join(apppath, "conf", "app.conf"),
|
||||||
|
strings.Replace(apiconf, "{{.Appname}}", args[0], -1))
|
||||||
|
|
||||||
|
fmt.Println("create controllers default.go:", path.Join(apppath, "controllers", "default.go"))
|
||||||
|
writetofile(path.Join(apppath, "controllers", "default.go"),
|
||||||
|
strings.Replace(apiControllers, "{{.Appname}}", packpath, -1))
|
||||||
|
|
||||||
|
fmt.Println("create tests default.go:", path.Join(apppath, "tests", "default_test.go"))
|
||||||
|
writetofile(path.Join(apppath, "tests", "default_test.go"),
|
||||||
|
apiTests)
|
||||||
|
|
||||||
|
fmt.Println("create models object.go:", path.Join(apppath, "models", "object.go"))
|
||||||
|
writetofile(path.Join(apppath, "models", "object.go"), apiModels)
|
||||||
|
|
||||||
|
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
|
||||||
|
writetofile(path.Join(apppath, "main.go"),
|
||||||
|
strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkEnv(appname string) (apppath, packpath string, err error) {
|
||||||
|
curpath, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gopath := os.Getenv("GOPATH")
|
||||||
|
Debugf("gopath:%s", gopath)
|
||||||
|
if gopath == "" {
|
||||||
|
err = fmt.Errorf("you should set GOPATH in the env")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
appsrcpath := ""
|
||||||
|
haspath := false
|
||||||
|
wgopath := path.SplitList(gopath)
|
||||||
|
for _, wg := range wgopath {
|
||||||
|
wg = path.Join(wg, "src")
|
||||||
|
|
||||||
|
if path.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) {
|
||||||
|
haspath = true
|
||||||
|
appsrcpath = wg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haspath {
|
||||||
|
err = fmt.Errorf("can't create application outside of GOPATH `%s`\n"+
|
||||||
|
"you first should `cd $GOPATH%ssrc` then use create\n", gopath, string(path.Separator))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
apppath = path.Join(curpath, appname)
|
||||||
|
|
||||||
|
if _, e := os.Stat(apppath); os.IsNotExist(e) == false {
|
||||||
|
err = fmt.Errorf("path `%s` exists, can not create app without remove it\n", apppath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
packpath = strings.Join(strings.Split(apppath[len(appsrcpath)+1:], string(path.Separator)), "/")
|
||||||
|
return
|
||||||
|
}
|
282
autorouter.go
Normal file
282
autorouter.go
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
// 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) {
|
||||||
|
fmt.Println("[INFO] Starting auto-generating routers...")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// A source describles 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 }
|
||||||
|
|
||||||
|
// A 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
|
||||||
|
|
||||||
|
// Top-level declarations.
|
||||||
|
Types []*Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
9
autorouter_test.go
Normal file
9
autorouter_test.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetControllerInfo(t *testing.T) {
|
||||||
|
getControllerInfo("testdata/router/")
|
||||||
|
}
|
@ -1,3 +1,18 @@
|
|||||||
|
// 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 developling applications based on beego framework.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -56,9 +71,12 @@ func (c *Command) Runnable() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var commands = []*Command{
|
var commands = []*Command{
|
||||||
cmdCreate,
|
cmdNew,
|
||||||
cmdStart,
|
cmdRun,
|
||||||
cmdPack,
|
cmdPack,
|
||||||
|
cmdApiapp,
|
||||||
|
cmdRouter,
|
||||||
|
cmdTest,
|
||||||
//cmdReStart,
|
//cmdReStart,
|
||||||
}
|
}
|
||||||
|
|
262
code.go
Normal file
262
code.go
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
// 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)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceWriter struct{ p *[]byte }
|
||||||
|
|
||||||
|
func (w sliceWriter) Write(p []byte) (int, error) {
|
||||||
|
*w.p = append(*w.p, p...)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
165
createapp.go
165
createapp.go
@ -1,165 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
path "path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmdCreate = &Command{
|
|
||||||
UsageLine: "create [appname]",
|
|
||||||
Short: "create an application base on beego framework",
|
|
||||||
Long: `
|
|
||||||
create an application base on beego framework
|
|
||||||
|
|
||||||
In the current path, will create a folder named [appname]
|
|
||||||
|
|
||||||
In the appname folder has the follow struct:
|
|
||||||
|
|
||||||
|- main.go
|
|
||||||
|- conf
|
|
||||||
|- app.conf
|
|
||||||
|- controllers
|
|
||||||
|- default.go
|
|
||||||
|- models
|
|
||||||
|- static
|
|
||||||
|- js
|
|
||||||
|- css
|
|
||||||
|- img
|
|
||||||
|- views
|
|
||||||
index.tpl
|
|
||||||
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
cmdCreate.Run = createapp
|
|
||||||
}
|
|
||||||
|
|
||||||
func createapp(cmd *Command, args []string) {
|
|
||||||
curpath, _ := os.Getwd()
|
|
||||||
if len(args) != 1 {
|
|
||||||
fmt.Println("error args")
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
gopath := os.Getenv("GOPATH")
|
|
||||||
Debugf("gopath:%s", gopath)
|
|
||||||
if gopath == "" {
|
|
||||||
fmt.Println("you should set GOPATH in the env")
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
haspath := false
|
|
||||||
appsrcpath := ""
|
|
||||||
|
|
||||||
wgopath := path.SplitList(gopath)
|
|
||||||
for _, wg := range wgopath {
|
|
||||||
wg = path.Join(wg, "src")
|
|
||||||
|
|
||||||
if path.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) {
|
|
||||||
haspath = true
|
|
||||||
appsrcpath = wg
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !haspath {
|
|
||||||
fmt.Printf("can't create application outside of GOPATH `%s`\n", gopath)
|
|
||||||
fmt.Printf("you first should `cd $GOPATH%ssrc` then use create\n", string(path.Separator))
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
apppath := path.Join(curpath, args[0])
|
|
||||||
|
|
||||||
if _, err := os.Stat(apppath); os.IsNotExist(err) == false {
|
|
||||||
fmt.Printf("path `%s` exists, can not create app without remove it\n", apppath)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.MkdirAll(apppath, 0755)
|
|
||||||
fmt.Println("create app folder:", apppath)
|
|
||||||
os.Mkdir(path.Join(apppath, "conf"), 0755)
|
|
||||||
fmt.Println("create conf:", path.Join(apppath, "conf"))
|
|
||||||
os.Mkdir(path.Join(apppath, "controllers"), 0755)
|
|
||||||
fmt.Println("create controllers:", path.Join(apppath, "controllers"))
|
|
||||||
os.Mkdir(path.Join(apppath, "models"), 0755)
|
|
||||||
fmt.Println("create models:", path.Join(apppath, "models"))
|
|
||||||
os.Mkdir(path.Join(apppath, "static"), 0755)
|
|
||||||
fmt.Println("create static:", path.Join(apppath, "static"))
|
|
||||||
os.Mkdir(path.Join(apppath, "static", "js"), 0755)
|
|
||||||
fmt.Println("create static js:", path.Join(apppath, "static", "js"))
|
|
||||||
os.Mkdir(path.Join(apppath, "static", "css"), 0755)
|
|
||||||
fmt.Println("create static css:", path.Join(apppath, "static", "css"))
|
|
||||||
os.Mkdir(path.Join(apppath, "static", "img"), 0755)
|
|
||||||
fmt.Println("create static img:", path.Join(apppath, "static", "img"))
|
|
||||||
fmt.Println("create views:", path.Join(apppath, "views"))
|
|
||||||
os.Mkdir(path.Join(apppath, "views"), 0755)
|
|
||||||
fmt.Println("create conf app.conf:", path.Join(apppath, "conf", "app.conf"))
|
|
||||||
writetofile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", args[0], -1))
|
|
||||||
|
|
||||||
fmt.Println("create controllers default.go:", path.Join(apppath, "controllers", "default.go"))
|
|
||||||
writetofile(path.Join(apppath, "controllers", "default.go"), controllers)
|
|
||||||
|
|
||||||
fmt.Println("create views index.tpl:", path.Join(apppath, "views", "index.tpl"))
|
|
||||||
writetofile(path.Join(apppath, "views", "index.tpl"), indextpl)
|
|
||||||
|
|
||||||
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
|
|
||||||
writetofile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", strings.Join(strings.Split(apppath[len(appsrcpath)+1:], string(path.Separator)), "/"), -1))
|
|
||||||
}
|
|
||||||
|
|
||||||
var appconf = `
|
|
||||||
appname = {{.Appname}}
|
|
||||||
httpport = 8080
|
|
||||||
runmode = dev
|
|
||||||
`
|
|
||||||
|
|
||||||
var maingo = `package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"{{.Appname}}/controllers"
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
beego.Router("/", &controllers.MainController{})
|
|
||||||
beego.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
`
|
|
||||||
var controllers = `package controllers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MainController struct {
|
|
||||||
beego.Controller
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *MainController) Get() {
|
|
||||||
this.Data["Username"] = "astaxie"
|
|
||||||
this.Data["Email"] = "astaxie@gmail.com"
|
|
||||||
this.TplNames = "index.tpl"
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
var indextpl = `<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>beego welcome template</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Hello, world!{{.Username}},{{.Email}}</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`
|
|
||||||
|
|
||||||
func writetofile(filename, content string) {
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
f.WriteString(content)
|
|
||||||
}
|
|
242
new.go
Normal file
242
new.go
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
path "path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdNew = &Command{
|
||||||
|
UsageLine: "new [appname]",
|
||||||
|
Short: "create an application base on beego framework",
|
||||||
|
Long: `
|
||||||
|
create an application base on beego framework,
|
||||||
|
|
||||||
|
which in the current path with folder named [appname].
|
||||||
|
|
||||||
|
The [appname] folder has following structure:
|
||||||
|
|
||||||
|
|- main.go
|
||||||
|
|- conf
|
||||||
|
|- app.conf
|
||||||
|
|- controllers
|
||||||
|
|- default.go
|
||||||
|
|- models
|
||||||
|
|- static
|
||||||
|
|- js
|
||||||
|
|- css
|
||||||
|
|- img
|
||||||
|
|- views
|
||||||
|
index.tpl
|
||||||
|
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdNew.Run = createApp
|
||||||
|
}
|
||||||
|
|
||||||
|
func createApp(cmd *Command, args []string) {
|
||||||
|
curpath, _ := os.Getwd()
|
||||||
|
if len(args) != 1 {
|
||||||
|
colorLog("[ERRO] Argument [appname] is missing\n")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
gopath := os.Getenv("GOPATH")
|
||||||
|
Debugf("gopath:%s", gopath)
|
||||||
|
if gopath == "" {
|
||||||
|
colorLog("[ERRO] $GOPATH not found\n")
|
||||||
|
colorLog("[HINT] Set $GOPATH in your environment vairables\n")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
haspath := false
|
||||||
|
appsrcpath := ""
|
||||||
|
|
||||||
|
wgopath := path.SplitList(gopath)
|
||||||
|
for _, wg := range wgopath {
|
||||||
|
wg, _ = path.EvalSymlinks(path.Join(wg, "src"))
|
||||||
|
|
||||||
|
if strings.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) {
|
||||||
|
haspath = true
|
||||||
|
appsrcpath = wg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haspath {
|
||||||
|
colorLog("[ERRO] Unable to create an application outside of $GOPATH(%s)\n", gopath)
|
||||||
|
colorLog("[HINT] Change your work directory by `cd ($GOPATH%ssrc)`\n", string(path.Separator))
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
apppath := path.Join(curpath, args[0])
|
||||||
|
|
||||||
|
if _, err := os.Stat(apppath); os.IsNotExist(err) == false {
|
||||||
|
fmt.Printf("[ERRO] Path(%s) has alreay existed\n", apppath)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("[INFO] Creating application...")
|
||||||
|
|
||||||
|
os.MkdirAll(apppath, 0755)
|
||||||
|
fmt.Println(apppath + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "conf"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "conf") + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "controllers"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "controllers") + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "models"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "models") + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "static"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "static") + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "static", "js"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "static", "js") + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "static", "css"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "static", "css") + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "static", "img"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "static", "img") + string(path.Separator))
|
||||||
|
fmt.Println(path.Join(apppath, "views") + string(path.Separator))
|
||||||
|
os.Mkdir(path.Join(apppath, "views"), 0755)
|
||||||
|
fmt.Println(path.Join(apppath, "conf", "app.conf"))
|
||||||
|
writetofile(path.Join(apppath, "conf", "app.conf"), strings.Replace(appconf, "{{.Appname}}", args[0], -1))
|
||||||
|
|
||||||
|
fmt.Println(path.Join(apppath, "controllers", "default.go"))
|
||||||
|
writetofile(path.Join(apppath, "controllers", "default.go"), controllers)
|
||||||
|
|
||||||
|
fmt.Println(path.Join(apppath, "views", "index.tpl"))
|
||||||
|
writetofile(path.Join(apppath, "views", "index.tpl"), indextpl)
|
||||||
|
|
||||||
|
fmt.Println(path.Join(apppath, "main.go"))
|
||||||
|
writetofile(path.Join(apppath, "main.go"), strings.Replace(maingo, "{{.Appname}}", strings.Join(strings.Split(apppath[len(appsrcpath)+1:], string(path.Separator)), string(path.Separator)), -1))
|
||||||
|
|
||||||
|
colorLog("[SUCC] New application successfully created!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var appconf = `appname = {{.Appname}}
|
||||||
|
httpport = 8080
|
||||||
|
runmode = dev
|
||||||
|
`
|
||||||
|
|
||||||
|
var maingo = `package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"{{.Appname}}/controllers"
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
beego.Router("/", &controllers.MainController{})
|
||||||
|
beego.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
var controllers = `package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MainController struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *MainController) Get() {
|
||||||
|
this.Data["Website"] = "beego.me"
|
||||||
|
this.Data["Email"] = "astaxie@gmail.com"
|
||||||
|
this.TplNames = "index.tpl"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var indextpl = `<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Beego</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: rgb(51, 51, 51);
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-unit {
|
||||||
|
padding: 60px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
border-radius: 6px 6px 6px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 940px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-left: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 10px 0px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: bold;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-unit h1 {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
font-size: 60px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-left: 5px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 200;
|
||||||
|
line-height: 30px;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0px 0px 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header class="hero-unit" style="background-color:#A9F16C">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="hero-text">
|
||||||
|
<h1>Welcome to Beego!</h1>
|
||||||
|
<p class="description">
|
||||||
|
Beego is a simple & powerful Go web framework which is inspired by tornado and sinatra.
|
||||||
|
<br />
|
||||||
|
Official website: <a href="http://{{.Website}}">{{.Website}}</a>
|
||||||
|
<br />
|
||||||
|
Contact me: {{.Email}}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
func writetofile(filename, content string) {
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
f.WriteString(content)
|
||||||
|
}
|
132
run.go
Normal file
132
run.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
path "path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdRun = &Command{
|
||||||
|
UsageLine: "run [appname]",
|
||||||
|
Short: "run the app which can hot compile",
|
||||||
|
Long: `
|
||||||
|
start the appname throw exec.Command
|
||||||
|
|
||||||
|
then start a inotify watch for current dir
|
||||||
|
|
||||||
|
when the file has changed bee will auto go build and restart the app
|
||||||
|
|
||||||
|
file changed
|
||||||
|
|
|
||||||
|
check if it's go file
|
||||||
|
|
|
||||||
|
yes no
|
||||||
|
| |
|
||||||
|
go build do nothing
|
||||||
|
|
|
||||||
|
restart app
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultJson = `
|
||||||
|
{
|
||||||
|
"go_install": false,
|
||||||
|
"dir_structure":{
|
||||||
|
"controllers": "",
|
||||||
|
"models": "",
|
||||||
|
"others": []
|
||||||
|
},
|
||||||
|
"main_files":{
|
||||||
|
"main.go": "",
|
||||||
|
"others": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdRun.Run = runApp
|
||||||
|
}
|
||||||
|
|
||||||
|
var appname string
|
||||||
|
var conf struct {
|
||||||
|
// Indicates whether execute "go install" before "go build".
|
||||||
|
GoInstall bool `json:"go_install"`
|
||||||
|
|
||||||
|
DirStruct struct {
|
||||||
|
Controllers string
|
||||||
|
Models string
|
||||||
|
Others []string // Other directories.
|
||||||
|
} `json:"dir_structure"`
|
||||||
|
|
||||||
|
MainFiles struct {
|
||||||
|
Main string `json:"main.go"`
|
||||||
|
Others []string // Others files of package main.
|
||||||
|
} `json:"main_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func runApp(cmd *Command, args []string) {
|
||||||
|
exit := make(chan bool)
|
||||||
|
if len(args) != 1 {
|
||||||
|
colorLog("[ERRO] Cannot start running[ %s ]\n",
|
||||||
|
"argument 'appname' is missing")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
crupath, _ := os.Getwd()
|
||||||
|
Debugf("current path:%s\n", crupath)
|
||||||
|
|
||||||
|
err := loadConfig()
|
||||||
|
if err != nil {
|
||||||
|
colorLog("[ERRO] Fail to parse bee.json[ %s ]", err)
|
||||||
|
}
|
||||||
|
var paths []string
|
||||||
|
paths = append(paths,
|
||||||
|
path.Join(crupath, conf.DirStruct.Controllers),
|
||||||
|
path.Join(crupath, conf.DirStruct.Models),
|
||||||
|
path.Join(crupath, "./")) // Current path.
|
||||||
|
// Because monitor files has some issues, we watch current directory
|
||||||
|
// and ignore non-go files.
|
||||||
|
paths = append(paths, conf.DirStruct.Others...)
|
||||||
|
paths = append(paths, conf.MainFiles.Others...)
|
||||||
|
|
||||||
|
NewWatcher(paths)
|
||||||
|
appname = args[0]
|
||||||
|
Autobuild()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-exit:
|
||||||
|
runtime.Goexit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadConfig loads customized configuration.
|
||||||
|
func loadConfig() error {
|
||||||
|
f, err := os.Open("bee.json")
|
||||||
|
if err != nil {
|
||||||
|
// Use default.
|
||||||
|
err = json.Unmarshal([]byte(defaultJson), &conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defer f.Close()
|
||||||
|
colorLog("[INFO] Detected bee.json\n")
|
||||||
|
d := json.NewDecoder(f)
|
||||||
|
err = d.Decode(&conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set variables.
|
||||||
|
if len(conf.DirStruct.Controllers) == 0 {
|
||||||
|
conf.DirStruct.Controllers = "controllers"
|
||||||
|
}
|
||||||
|
if len(conf.DirStruct.Models) == 0 {
|
||||||
|
conf.DirStruct.Models = "models"
|
||||||
|
}
|
||||||
|
if len(conf.MainFiles.Main) == 0 {
|
||||||
|
conf.MainFiles.Main = "main.go"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
54
start.go
54
start.go
@ -1,54 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
path "path/filepath"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmdStart = &Command{
|
|
||||||
UsageLine: "start [appname]",
|
|
||||||
Short: "start the app which can hot compile",
|
|
||||||
Long: `
|
|
||||||
start the appname throw exec.Command
|
|
||||||
|
|
||||||
then start a inotify watch for current dir
|
|
||||||
|
|
||||||
when the file has changed bee will auto go build and restart the app
|
|
||||||
|
|
||||||
file changed
|
|
||||||
|
|
|
||||||
checked is go file
|
|
||||||
|
|
|
||||||
yes no
|
|
||||||
| |
|
|
||||||
go build do nothing
|
|
||||||
|
|
|
||||||
restart app
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
cmdStart.Run = startapp
|
|
||||||
}
|
|
||||||
|
|
||||||
var appname string
|
|
||||||
|
|
||||||
func startapp(cmd *Command, args []string) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
fmt.Println("error args")
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
crupath, _ := os.Getwd()
|
|
||||||
Debugf("current path:%s\n", crupath)
|
|
||||||
|
|
||||||
var paths []string
|
|
||||||
paths = append(paths, path.Join(crupath, "controllers"), path.Join(crupath, "models"))
|
|
||||||
NewWatcher(paths)
|
|
||||||
appname = args[0]
|
|
||||||
Autobuild()
|
|
||||||
for {
|
|
||||||
runtime.Gosched()
|
|
||||||
}
|
|
||||||
}
|
|
82
test.go
Normal file
82
test.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
path "path/filepath"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdTest = &Command{
|
||||||
|
UsageLine: "test [appname]",
|
||||||
|
Short: "test the app",
|
||||||
|
Long: ``,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdTest.Run = testApp
|
||||||
|
}
|
||||||
|
|
||||||
|
var started= make(chan bool)
|
||||||
|
|
||||||
|
func testApp(cmd *Command, args []string) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
colorLog("[ERRO] Cannot start running[ %s ]\n",
|
||||||
|
"argument 'appname' is missing")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
crupath, _ := os.Getwd()
|
||||||
|
Debugf("current path:%s\n", crupath)
|
||||||
|
|
||||||
|
err := loadConfig()
|
||||||
|
if err != nil {
|
||||||
|
colorLog("[ERRO] Fail to parse bee.json[ %s ]", err)
|
||||||
|
}
|
||||||
|
var paths []string
|
||||||
|
paths = append(paths,
|
||||||
|
path.Join(crupath, conf.DirStruct.Controllers),
|
||||||
|
path.Join(crupath, conf.DirStruct.Models),
|
||||||
|
path.Join(crupath, "./")) // Current path.
|
||||||
|
// Because monitor files has some issues, we watch current directory
|
||||||
|
// and ignore non-go files.
|
||||||
|
paths = append(paths, conf.DirStruct.Others...)
|
||||||
|
paths = append(paths, conf.MainFiles.Others...)
|
||||||
|
|
||||||
|
NewWatcher(paths)
|
||||||
|
appname = args[0]
|
||||||
|
Autobuild()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-started:
|
||||||
|
runTest()
|
||||||
|
Kill()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTest(){
|
||||||
|
colorLog("[INFO] Start testing...\n")
|
||||||
|
time.Sleep(time.Second*5)
|
||||||
|
path, _ := os.Getwd()
|
||||||
|
os.Chdir(path+"/tests")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
icmd := exec.Command("go", "test")
|
||||||
|
var out,errbuffer bytes.Buffer
|
||||||
|
icmd.Stdout = &out
|
||||||
|
icmd.Stderr = &errbuffer
|
||||||
|
colorLog("[INFO] ============== Test Begin ===================\n")
|
||||||
|
err = icmd.Run()
|
||||||
|
colorLog(out.String())
|
||||||
|
colorLog(errbuffer.String())
|
||||||
|
colorLog("[INFO] ============== Test End ===================\n")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
colorLog("[ERRO] ============== Test failed ===================\n")
|
||||||
|
colorLog("[ERRO] " ,err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
colorLog("[SUCC] Test finish\n")
|
||||||
|
}
|
28
testdata/router/router.go
vendored
Normal file
28
testdata/router/router.go
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Router struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Router) Get() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Router) Post() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Controller) Put() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Controller) Delete() {
|
||||||
|
|
||||||
|
}
|
79
util.go
79
util.go
@ -1,9 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
import "os"
|
"fmt"
|
||||||
import "runtime"
|
"os"
|
||||||
import "path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// Go is a basic promise implementation: it wraps calls a function in a goroutine
|
// Go is a basic promise implementation: it wraps calls a function in a goroutine
|
||||||
// and returns a channel which will later return the function's return value.
|
// and returns a channel which will later return the function's return value.
|
||||||
@ -28,3 +31,71 @@ func Debugf(format string, a ...interface{}) {
|
|||||||
fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...)
|
fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Gray = uint8(iota + 90)
|
||||||
|
Red
|
||||||
|
Green
|
||||||
|
Yellow
|
||||||
|
Blue
|
||||||
|
Magenta
|
||||||
|
//NRed = uint8(31) // Normal
|
||||||
|
EndColor = "\033[0m"
|
||||||
|
)
|
||||||
|
|
||||||
|
// colorLog colors log and print to stdout.
|
||||||
|
// Log format: [<level>] <content [path]> [ error ].
|
||||||
|
// Level: ERRO -> red; WARN -> Magenta; SUCC -> green; others -> default.
|
||||||
|
// Content: default; path: yellow; error -> red.
|
||||||
|
// Errors have to surrounded by "[ " and " ]"(space).
|
||||||
|
func colorLog(format string, a ...interface{}) {
|
||||||
|
log := fmt.Sprintf(format, a...)
|
||||||
|
if len(log) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
var clog string
|
||||||
|
|
||||||
|
// Level.
|
||||||
|
i := strings.Index(log, "]")
|
||||||
|
if log[0] == '[' && i > -1 {
|
||||||
|
clog += "[" + getColorLevel(log[1:i]) + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
log = log[i+1:]
|
||||||
|
|
||||||
|
// Error.
|
||||||
|
log = strings.Replace(log, "[ ", fmt.Sprintf("[\033[%dm", Red), -1)
|
||||||
|
log = strings.Replace(log, " ]", EndColor+"]", -1)
|
||||||
|
|
||||||
|
// Path.
|
||||||
|
log = strings.Replace(log, "( ", fmt.Sprintf("(\033[%dm", Yellow), -1)
|
||||||
|
log = strings.Replace(log, " )", EndColor+")", -1)
|
||||||
|
|
||||||
|
// Highlights.
|
||||||
|
log = strings.Replace(log, "# ", fmt.Sprintf("\033[%dm", Gray), -1)
|
||||||
|
log = strings.Replace(log, " #", EndColor, -1)
|
||||||
|
|
||||||
|
log = clog + log
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(log)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getColorLevel returns colored level string by given level.
|
||||||
|
func getColorLevel(level string) string {
|
||||||
|
level = strings.ToUpper(level)
|
||||||
|
switch level {
|
||||||
|
case "TRAC":
|
||||||
|
return fmt.Sprintf("\033[%dm%s\033[0m", Blue, level)
|
||||||
|
case "ERRO":
|
||||||
|
return fmt.Sprintf("\033[%dm%s\033[0m", Red, level)
|
||||||
|
case "WARN":
|
||||||
|
return fmt.Sprintf("\033[%dm%s\033[0m", Magenta, level)
|
||||||
|
case "SUCC":
|
||||||
|
return fmt.Sprintf("\033[%dm%s\033[0m", Green, level)
|
||||||
|
default:
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
96
watch.go
96
watch.go
@ -6,6 +6,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -13,13 +14,14 @@ import (
|
|||||||
var (
|
var (
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
state sync.Mutex
|
state sync.Mutex
|
||||||
eventTime = make(map[string]time.Time)
|
eventTime = make(map[string]int64)
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewWatcher(paths []string) {
|
func NewWatcher(paths []string) {
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
colorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err)
|
||||||
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -27,17 +29,25 @@ func NewWatcher(paths []string) {
|
|||||||
select {
|
select {
|
||||||
case e := <-watcher.Event:
|
case e := <-watcher.Event:
|
||||||
isbuild := true
|
isbuild := true
|
||||||
if t, ok := eventTime[e.String()]; ok {
|
|
||||||
// if 500ms change many times, then ignore it.
|
// Skip TMP files for Sublime Text.
|
||||||
// for liteide often gofmt code after save.
|
if checkTMPFile(e.Name) {
|
||||||
if t.Add(time.Millisecond * 500).After(time.Now()) {
|
continue
|
||||||
|
}
|
||||||
|
if !checkIsGoFile(e.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := getFileModTime(e.Name)
|
||||||
|
if t := eventTime[e.Name]; mt == t {
|
||||||
|
colorLog("[SKIP] # %s #\n", e.String())
|
||||||
isbuild = false
|
isbuild = false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
eventTime[e.String()] = time.Now()
|
eventTime[e.Name] = mt
|
||||||
|
|
||||||
if isbuild {
|
if isbuild {
|
||||||
fmt.Println(e)
|
colorLog("[EVEN] %s\n", e)
|
||||||
go Autobuild()
|
go Autobuild()
|
||||||
}
|
}
|
||||||
case err := <-watcher.Error:
|
case err := <-watcher.Error:
|
||||||
@ -45,33 +55,67 @@ func NewWatcher(paths []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
colorLog("[INFO] Initializing watcher...\n")
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
fmt.Println(path)
|
colorLog("[TRAC] Directory( %s )\n", path)
|
||||||
err = watcher.Watch(path)
|
err = watcher.Watch(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
colorLog("[ERRO] Fail to watch directory[ %s ]\n", err)
|
||||||
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getFileModTime retuens unix timestamp of `os.File.ModTime` by given path.
|
||||||
|
func getFileModTime(path string) int64 {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
colorLog("[ERRO] Fail to open file[ %s ]", err)
|
||||||
|
return time.Now().Unix()
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
colorLog("[ERRO] Fail to get file information[ %s ]", err)
|
||||||
|
return time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fi.ModTime().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
func Autobuild() {
|
func Autobuild() {
|
||||||
state.Lock()
|
state.Lock()
|
||||||
defer state.Unlock()
|
defer state.Unlock()
|
||||||
|
|
||||||
fmt.Println("start autobuild")
|
colorLog("[INFO] Start building...\n")
|
||||||
path, _ := os.Getwd()
|
path, _ := os.Getwd()
|
||||||
os.Chdir(path)
|
os.Chdir(path)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
// For applications use full import path like "github.com/.../.."
|
||||||
|
// are able to use "go install" to reduce build time.
|
||||||
|
if conf.GoInstall {
|
||||||
|
icmd := exec.Command("go", "install")
|
||||||
|
icmd.Stdout = os.Stdout
|
||||||
|
icmd.Stderr = os.Stderr
|
||||||
|
err = icmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
bcmd := exec.Command("go", "build")
|
bcmd := exec.Command("go", "build")
|
||||||
bcmd.Stdout = os.Stdout
|
bcmd.Stdout = os.Stdout
|
||||||
bcmd.Stderr = os.Stderr
|
bcmd.Stderr = os.Stderr
|
||||||
err := bcmd.Run()
|
err = bcmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("============== build failed ===================")
|
colorLog("[ERRO] ============== Build failed ===================\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("build success")
|
colorLog("[SUCC] Build was successful\n")
|
||||||
Restart(appname)
|
Restart(appname)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +137,31 @@ func Restart(appname string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Start(appname string) {
|
func Start(appname string) {
|
||||||
fmt.Println("start", appname)
|
colorLog("[INFO] Restarting %s ...\n", appname)
|
||||||
|
if strings.Index(appname, "./") == -1 {
|
||||||
|
appname = "./" + appname
|
||||||
|
}
|
||||||
|
|
||||||
cmd = exec.Command(appname)
|
cmd = exec.Command(appname)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
go cmd.Run()
|
go cmd.Run()
|
||||||
|
started<-true
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkTMPFile returns true if the event was for TMP files.
|
||||||
|
func checkTMPFile(name string) bool {
|
||||||
|
if strings.HasSuffix(strings.ToLower(name), ".tmp") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkIsGoFile returns true if the name HasSuffix ".go".
|
||||||
|
func checkIsGoFile(name string) bool {
|
||||||
|
if strings.HasSuffix(name, ".go") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user