mirror of
https://github.com/beego/bee.git
synced 2024-11-26 16:41:30 +00:00
commit
4525474bd1
29
.gitignore
vendored
29
.gitignore
vendored
@ -1,4 +1,31 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
#Custom
|
||||||
.DS_Store
|
.DS_Store
|
||||||
bee
|
bee
|
||||||
*.exe
|
|
||||||
*.exe~
|
*.exe~
|
||||||
|
.goxc.local.json
|
||||||
|
14
.goxc.json
Normal file
14
.goxc.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Tasks": [
|
||||||
|
"default",
|
||||||
|
"publish-github"
|
||||||
|
],
|
||||||
|
"PackageVersion": "0.0.1",
|
||||||
|
"TaskSettings": {
|
||||||
|
"publish-github": {
|
||||||
|
"owner": "beego",
|
||||||
|
"repository": "bee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConfigVersion": "0.9"
|
||||||
|
}
|
6
.travis.yml
Normal file
6
.travis.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.5.3
|
||||||
|
- 1.4.3
|
||||||
|
- 1.3.3
|
15
Beefile
Normal file
15
Beefile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
version: 0
|
||||||
|
gopm:
|
||||||
|
enable: false
|
||||||
|
install: false
|
||||||
|
go_install: false
|
||||||
|
watch_ext: []
|
||||||
|
dir_structure:
|
||||||
|
watch_all: false
|
||||||
|
controllers: ""
|
||||||
|
models: ""
|
||||||
|
others: []
|
||||||
|
cmd_args: []
|
||||||
|
envs: []
|
||||||
|
database:
|
||||||
|
driver: "mysql"
|
20
Makefile
Normal file
20
Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.PHONY: all test clean build install
|
||||||
|
|
||||||
|
GOFLAGS ?= $(GOFLAGS:)
|
||||||
|
|
||||||
|
all: install test
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build $(GOFLAGS) ./...
|
||||||
|
|
||||||
|
install:
|
||||||
|
go get $(GOFLAGS) ./...
|
||||||
|
|
||||||
|
test: install
|
||||||
|
go test $(GOFLAGS) ./...
|
||||||
|
|
||||||
|
bench: install
|
||||||
|
go test -run=NONE -bench=. $(GOFLAGS) ./...
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean $(GOFLAGS) -i ./...
|
519
README.md
519
README.md
@ -1,18 +1,17 @@
|
|||||||
bee
|
bee
|
||||||
===
|
===
|
||||||
|
|
||||||
[![Build Status](https://drone.io/github.com/beego/bee/status.png)](https://drone.io/github.com/beego/bee/latest)
|
Bee is a command-line tool facilitating development of Beego-based application.
|
||||||
|
|
||||||
Bee is a command line tool facilitating development with beego framework.
|
[![Build Status](https://drone.io/github.com/beego/bee/status.png)](https://drone.io/github.com/beego/bee/latest)
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Go version >= 1.1.
|
- Go version >= 1.3.
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Begin by installing `bee` using `go get` command.
|
To install `bee` use the `go get` command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go get github.com/beego/bee
|
go get github.com/beego/bee
|
||||||
@ -21,7 +20,7 @@ go get github.com/beego/bee
|
|||||||
Then you can add `bee` binary to PATH environment variable in your `~/.bashrc` or `~/.bash_profile` file:
|
Then you can add `bee` binary to PATH environment variable in your `~/.bashrc` or `~/.bash_profile` file:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export PATH=$PATH:<your_main_gopath>/bin/bee
|
export PATH=$PATH:<your_main_gopath>/bin
|
||||||
```
|
```
|
||||||
|
|
||||||
> If you already have `bee` installed, updating `bee` is simple:
|
> If you already have `bee` installed, updating `bee` is simple:
|
||||||
@ -32,242 +31,225 @@ go get -u github.com/beego/bee
|
|||||||
|
|
||||||
## Basic commands
|
## Basic commands
|
||||||
|
|
||||||
Bee provides a variety of commands which can be helpful at various stage of development. The top level commands include:
|
Bee provides a variety of commands which can be helpful at various stages of development. The top level commands include:
|
||||||
```base
|
|
||||||
new create an application base on beego framework
|
|
||||||
run run the app which can hot compile
|
|
||||||
pack compress an beego project
|
|
||||||
api create an api application base on beego framework
|
|
||||||
bale packs non-Go files to Go source files
|
|
||||||
version show the bee & beego version
|
|
||||||
generate source code generator
|
|
||||||
migrate run database migrations
|
|
||||||
hprose create an rpc application use hprose base on beego framework
|
|
||||||
```
|
```
|
||||||
## bee version
|
new Create a Beego application
|
||||||
|
run Run the app and start a Web server for development
|
||||||
|
pack Compress a beego project into a single file
|
||||||
|
api Create an API beego application
|
||||||
|
hprose Create an rpc application use hprose base on beego framework
|
||||||
|
bale Packs non-Go files to Go source files
|
||||||
|
version Prints the current Bee version
|
||||||
|
generate Source code generator
|
||||||
|
migrate Run database migrations
|
||||||
|
fix Fix the Beego application to make it compatible with Beego 1.6
|
||||||
|
```
|
||||||
|
### bee version
|
||||||
|
|
||||||
The first command is the easiest: displaying which version of `bee`, `beego` and `go` is installed on your machine:
|
To display the current version of `bee`, `beego` and `go` installed on your machine:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ bee version
|
$ bee version
|
||||||
bee :1.2.2
|
______
|
||||||
beego :1.4.0
|
| ___ \
|
||||||
Go :go version go1.2.1 linux/amd64
|
| |_/ / ___ ___
|
||||||
|
| ___ \ / _ \ / _ \
|
||||||
|
| |_/ /| __/| __/
|
||||||
|
\____/ \___| \___| v1.5.0
|
||||||
|
|
||||||
|
├── Beego : 1.7.0
|
||||||
|
├── GoVersion : go1.6.2
|
||||||
|
├── GOOS : windows
|
||||||
|
├── GOARCH : amd64
|
||||||
|
├── NumCPU : 4
|
||||||
|
├── GOPATH : C:\Users\beeuser\go
|
||||||
|
├── GOROOT : C:\go
|
||||||
|
├── Compiler : gc
|
||||||
|
└── Date : Monday, 22 Aug 2016
|
||||||
```
|
```
|
||||||
|
|
||||||
## bee new
|
### bee new
|
||||||
|
|
||||||
Creating a new beego web application is no big deal, too.
|
To create a new Beego web application:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ bee new myapp
|
$ bee new my-web-app
|
||||||
[INFO] Creating application...
|
______
|
||||||
/home/zheng/gopath/src/myapp/
|
| ___ \
|
||||||
/home/zheng/gopath/src/myapp/conf/
|
| |_/ / ___ ___
|
||||||
/home/zheng/gopath/src/myapp/controllers/
|
| ___ \ / _ \ / _ \
|
||||||
/home/zheng/gopath/src/myapp/models/
|
| |_/ /| __/| __/
|
||||||
/home/zheng/gopath/src/myapp/routers/
|
\____/ \___| \___| v1.5.0
|
||||||
/home/zheng/gopath/src/myapp/tests/
|
2016/08/22 14:53:45 [INFO] Creating application...
|
||||||
/home/zheng/gopath/src/myapp/static/
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\
|
||||||
/home/zheng/gopath/src/myapp/static/js/
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\conf\
|
||||||
/home/zheng/gopath/src/myapp/static/css/
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\controllers\
|
||||||
/home/zheng/gopath/src/myapp/static/img/
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\models\
|
||||||
/home/zheng/gopath/src/myapp/views/
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\routers\
|
||||||
/home/zheng/gopath/src/myapp/conf/app.conf
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\tests\
|
||||||
/home/zheng/gopath/src/myapp/controllers/default.go
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\
|
||||||
/home/zheng/gopath/src/myapp/views/index.tpl
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\js\
|
||||||
/home/zheng/gopath/src/myapp/routers/router.go
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\css\
|
||||||
/home/zheng/gopath/src/myapp/tests/default_test.go
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\img\
|
||||||
/home/zheng/gopath/src/myapp/main.go
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\views\
|
||||||
2014/08/29 15:45:47 [SUCC] New application successfully created!
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\conf\app.conf
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\controllers\default.go
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\views\index.tpl
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\routers\router.go
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\tests\default_test.go
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app\main.go
|
||||||
|
2016/08/22 14:53:45 [SUCC] New application successfully created!
|
||||||
```
|
```
|
||||||
|
|
||||||
## bee run
|
For more information on the usage, run `bee help new`.
|
||||||
|
|
||||||
To run the application we just created, navigate to the application folder and execute `bee run`.
|
### bee run
|
||||||
|
|
||||||
|
To run the application we just created, you can navigate to the application folder and execute:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cd myapp
|
$ cd my-web-app && bee run
|
||||||
$ bee run
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## bee pack
|
Or from anywhere in your machine:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bee run github.com/user/my-web-app
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information on the usage, run `bee help run`.
|
||||||
|
|
||||||
|
### bee pack
|
||||||
|
|
||||||
|
To compress a Beego application into a single deployable file:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
usage: bee pack
|
$ bee pack
|
||||||
|
______
|
||||||
compress an beego project
|
| ___ \
|
||||||
|
| |_/ / ___ ___
|
||||||
-p app path. default is current path
|
| ___ \ / _ \ / _ \
|
||||||
-b build specify platform app. default true
|
| |_/ /| __/| __/
|
||||||
-ba additional args of go build
|
\____/ \___| \___| v1.5.0
|
||||||
-be=[] additional ENV Variables of go build. eg: GOARCH=arm
|
2016/08/22 15:11:01 Packaging application: C:\Users\beeuser\go\src\github.com\user\my-web-app
|
||||||
-o compressed file output dir. default use current path
|
2016/08/22 15:11:01 Building application...
|
||||||
-f="" format. [ tar.gz / zip ]. default tar.gz
|
2016/08/22 15:11:01 Env: GOOS=windows GOARCH=amd64
|
||||||
-exp="" relpath exclude prefix. default: .
|
2016/08/22 15:11:08 Build successful
|
||||||
-exs="" relpath exclude suffix. default: .go:.DS_Store:.tmp
|
2016/08/22 15:11:08 Excluding relpath prefix: .
|
||||||
all path use : as separator
|
2016/08/22 15:11:08 Excluding relpath suffix: .go:.DS_Store:.tmp
|
||||||
-exr=[] file/directory name exclude by Regexp. default: ^.
|
2016/08/22 15:11:10 Writing to output: `C:\Users\beeuser\go\src\github.com\user\my-web-app\my-web-app.tar.gz`
|
||||||
-fs=false follow symlink. default false
|
|
||||||
-ss=false skip symlink. default false
|
|
||||||
default embed symlink into compressed file
|
|
||||||
-v=false verbose
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## bee api
|
For more information on the usage, run `bee help pack`.
|
||||||
|
|
||||||
|
### bee api
|
||||||
|
|
||||||
|
To create a Beego API application:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
usage: bee api [appname]
|
$ bee api my-api
|
||||||
|
______
|
||||||
create an api application base on beego framework
|
| ___ \
|
||||||
|
| |_/ / ___ ___
|
||||||
bee api [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
|
| ___ \ / _ \ / _ \
|
||||||
-tables: a list of table names separated by ',', default is empty, indicating all tables
|
| |_/ /| __/| __/
|
||||||
-driver: [mysql | postgres | sqlite], the default is mysql
|
\____/ \___| \___| v1.5.0
|
||||||
-conn: the connection string used by the driver, the default is ''
|
2016/08/22 15:14:10 [INFO] Creating API...
|
||||||
e.g. for mysql: root:@tcp(127.0.0.1:3306)/test
|
create C:\Users\beeuser\go\src\github.com\user\my-api
|
||||||
e.g. for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres
|
create C:\Users\beeuser\go\src\github.com\user\my-api\conf
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-api\controllers
|
||||||
if conn is empty will create a example api application. otherwise generate api application based on an existing database.
|
create C:\Users\beeuser\go\src\github.com\user\my-api\tests
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-api\conf\app.conf
|
||||||
In the current path, will create a folder named [appname]
|
create C:\Users\beeuser\go\src\github.com\user\my-api\models
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-api\routers\
|
||||||
In the appname folder has the follow struct:
|
create C:\Users\beeuser\go\src\github.com\user\my-api\controllers\object.go
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-api\controllers\user.go
|
||||||
├── conf
|
create C:\Users\beeuser\go\src\github.com\user\my-api\tests\default_test.go
|
||||||
│ └── app.conf
|
create C:\Users\beeuser\go\src\github.com\user\my-api\routers\router.go
|
||||||
├── controllers
|
create C:\Users\beeuser\go\src\github.com\user\my-api\models\object.go
|
||||||
│ └── object.go
|
create C:\Users\beeuser\go\src\github.com\user\my-api\models\user.go
|
||||||
│ └── user.go
|
create C:\Users\beeuser\go\src\github.com\user\my-api\main.go
|
||||||
├── routers
|
2016/08/22 15:14:10 [SUCC] New API successfully created!
|
||||||
│ └── router.go
|
|
||||||
├── tests
|
|
||||||
│ └── default_test.go
|
|
||||||
├── main.go
|
|
||||||
└── models
|
|
||||||
└── object.go
|
|
||||||
└── user.go
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## bee hprose
|
For more information on the usage, run `bee help api`.
|
||||||
|
|
||||||
|
### bee hprose
|
||||||
|
|
||||||
|
To create an Hprose RPC application based on Beego:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
usage: bee hprose [appname]
|
$ bee hprose my-rpc-app
|
||||||
|
______
|
||||||
create an rpc application use hprose base on beego framework
|
| ___ \
|
||||||
|
| |_/ / ___ ___
|
||||||
bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
|
| ___ \ / _ \ / _ \
|
||||||
-tables: a list of table names separated by ',', default is empty, indicating all tables
|
| |_/ /| __/| __/
|
||||||
-driver: [mysql | postgres | sqlite], the default is mysql
|
\____/ \___| \___| v1.5.0
|
||||||
-conn: the connection string used by the driver, the default is ''
|
2016/08/22 16:09:13 [INFO] Creating Hprose application...
|
||||||
e.g. for mysql: root:@tcp(127.0.0.1:3306)/test
|
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app
|
||||||
e.g. for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres
|
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\conf
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\conf\app.conf
|
||||||
if conn is empty will create a example rpc application. otherwise generate rpc application use hprose based on an existing database.
|
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\models
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\models\object.go
|
||||||
In the current path, will create a folder named [appname]
|
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\models\user.go
|
||||||
|
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\main.go
|
||||||
In the appname folder has the follow struct:
|
2016/08/22 16:09:13 [SUCC] New Hprose application successfully created!
|
||||||
|
|
||||||
├── conf
|
|
||||||
│ └── app.conf
|
|
||||||
├── main.go
|
|
||||||
└── models
|
|
||||||
└── object.go
|
|
||||||
└── user.go
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## bee bale
|
For more information on the usage, run `bee help hprose`.
|
||||||
|
|
||||||
|
### bee bale
|
||||||
|
|
||||||
|
To pack all the static files into Go source files:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
usage: bee bale
|
$ bee bale
|
||||||
|
______
|
||||||
bale packs non-Go files to Go source files and
|
| ___ \
|
||||||
|
| |_/ / ___ ___
|
||||||
auto-generate unpack function to main package then run it
|
| ___ \ / _ \ / _ \
|
||||||
|
| |_/ /| __/| __/
|
||||||
during the runtime.
|
\____/ \___| \___| v1.5.0
|
||||||
|
2016/08/22 16:37:24 [INFO] Detected bee.json
|
||||||
This is mainly used for zealots who are requiring 100% Go code.
|
2016/08/22 16:37:24 [INFO] Packaging directory(static/js)
|
||||||
|
2016/08/22 16:37:24 [INFO] Packaging directory(static/css)
|
||||||
|
2016/08/22 16:37:24 [SUCC] Baled resources successfully!
|
||||||
```
|
```
|
||||||
|
|
||||||
## bee migrate
|
For more information on the usage, run `bee help bale`.
|
||||||
|
|
||||||
|
### bee migrate
|
||||||
|
|
||||||
|
For database migrations, use `bee migrate`.
|
||||||
|
|
||||||
|
For more information on the usage, run `bee help migrate`.
|
||||||
|
|
||||||
|
### bee generate
|
||||||
|
|
||||||
|
Bee also comes with a source code generator which speeds up the development.
|
||||||
|
|
||||||
|
For example, to generate a new controller named `hello`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
usage: bee migrate [Command]
|
$ bee generate controller hello
|
||||||
|
______
|
||||||
bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
| ___ \
|
||||||
run all outstanding migrations
|
| |_/ / ___ ___
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
| ___ \ / _ \ / _ \
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
| |_/ /| __/| __/
|
||||||
|
\____/ \___| \___| v1.5.0
|
||||||
bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
2016/08/22 16:55:30 [INFO] Using 'Hello' as controller name
|
||||||
rollback the last migration operation
|
2016/08/22 16:55:30 [INFO] Using 'controllers' as package name
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
create C:\Users\beeuser\go\src\github.com\user\my-web-app/controllers/hello.go
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
2016/08/22 16:55:30 [SUCC] Controller successfully generated!
|
||||||
|
|
||||||
bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
|
||||||
rollback all migrations
|
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
|
||||||
|
|
||||||
bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
|
||||||
rollback all migrations and run them all again
|
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## bee generate
|
|
||||||
|
|
||||||
Bee also comes with a souce code generator which speeds up the development.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
usage: bee generate [Command]
|
|
||||||
|
|
||||||
bee generate scaffold [scaffoldname] [-fields=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
|
||||||
The generate scaffold command will do a number of things for you.
|
|
||||||
-fields: a list of table fields. Format: field:type, ...
|
|
||||||
-driver: [mysql | postgres | sqlite], the default is mysql
|
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
|
||||||
example: bee generate scaffold post -fields="title:string,body:text"
|
|
||||||
|
|
||||||
bee generate model [modelname] [-fields=""]
|
|
||||||
generate RESTFul model based on fields
|
|
||||||
-fields: a list of table fields. Format: field:type, ...
|
|
||||||
|
|
||||||
bee generate controller [controllerfile]
|
|
||||||
generate RESTFul controllers
|
|
||||||
|
|
||||||
bee generate view [viewpath]
|
|
||||||
generate CRUD view in viewpath
|
|
||||||
|
|
||||||
bee generate migration [migrationfile] [-fields=""]
|
|
||||||
generate migration file for making database schema update
|
|
||||||
-fields: a list of table fields. Format: field:type, ...
|
|
||||||
|
|
||||||
bee generate docs
|
|
||||||
generate swagger doc file
|
|
||||||
|
|
||||||
bee generate test [routerfile]
|
|
||||||
generate testcase
|
|
||||||
|
|
||||||
bee generate appcode [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3]
|
|
||||||
generate appcode based on an existing database
|
|
||||||
-tables: a list of table names separated by ',', default is empty, indicating all tables
|
|
||||||
-driver: [mysql | postgres | sqlite], the default is mysql
|
|
||||||
-conn: the connection string used by the driver.
|
|
||||||
default for mysql: root:@tcp(127.0.0.1:3306)/test
|
|
||||||
default for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres
|
|
||||||
-level: [1 | 2 | 3], 1 = models; 2 = models,controllers; 3 = models,controllers,router
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For more information on the usage, run `bee help generate`.
|
||||||
|
|
||||||
## Shortcuts
|
## Shortcuts
|
||||||
|
|
||||||
Because you'll likely type these generator commands over and over, it makes sense to create aliases.
|
Because you'll likely type these generator commands over and over, it makes sense to create aliases:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generator Stuff
|
# Generator Stuff
|
||||||
@ -278,32 +260,135 @@ alias g:v="bee generate view"
|
|||||||
alias g:mi="bee generate migration"
|
alias g:mi="bee generate migration"
|
||||||
```
|
```
|
||||||
|
|
||||||
These can be stored in, for example, your `~/.bash_profile` or `~/.bashrc` files.
|
These can be stored , for example, in your `~/.bash_profile` or `~/.bashrc` files.
|
||||||
|
|
||||||
## Help
|
## Help
|
||||||
|
|
||||||
If you happend to forget the usage of a command, you can always find the usage information by `bee help <command>`.
|
To print more information on the usage of a particular command, use `bee help <command>`.
|
||||||
|
|
||||||
For instance, to get more information about the `run` command:
|
For instance, to get more information about the `run` command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ bee help run
|
$ bee help run
|
||||||
usage: bee run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true]
|
usage: bee run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-tags=goBuildTags]
|
||||||
|
|
||||||
start the appname throw exec.Command
|
Run command will supervise the file system of the beego project using inotify,
|
||||||
|
it will recompile and restart the app after any modifications.
|
||||||
|
```
|
||||||
|
|
||||||
then start a inotify watch for current dir
|
## Contributing
|
||||||
|
Bug reports, feature requests and pull requests are always welcome.
|
||||||
|
|
||||||
when the file has changed bee will auto go build and restart the app
|
We work on two branches: `master` for stable, released code and `develop`, a development branch.
|
||||||
|
It might be important to distinguish them when you are reading the commit history searching for a feature or a bugfix,
|
||||||
|
or when you are unsure of where to base your work from when contributing.
|
||||||
|
|
||||||
file changed
|
### Found a bug?
|
||||||
|
|
|
||||||
check if it's go file
|
Please [submit an issue][new-issue] on GitHub and we will follow up.
|
||||||
|
|
Even better, we would appreciate a [Pull Request][new-pr] with a fix for it!
|
||||||
yes no
|
|
||||||
| |
|
- If the bug was found in a release, it is best to base your work on `master` and submit your PR against it.
|
||||||
go build do nothing
|
- If the bug was found on `develop` (the development branch), base your work on `develop` and submit your PR against it.
|
||||||
|
|
|
||||||
restart app
|
Please follow the [Pull Request Guidelines][new-pr].
|
||||||
|
|
||||||
|
### Want a feature?
|
||||||
|
|
||||||
|
Feel free to request a feature by [submitting an issue][new-issue] on GitHub and open the discussion.
|
||||||
|
|
||||||
|
If you'd like to implement a new feature, please consider opening an issue first to talk about it.
|
||||||
|
It may be that somebody is already working on it, or that there are particular issues that you should be aware of
|
||||||
|
before implementing the change. If you are about to open a Pull Request, please make sure to follow the [submissions guidelines][new-pr].
|
||||||
|
|
||||||
|
## Submission Guidelines
|
||||||
|
|
||||||
|
### Submitting an issue
|
||||||
|
|
||||||
|
Before you submit an issue, search the archive, maybe you will find that a similar one already exists.
|
||||||
|
|
||||||
|
If you are submitting an issue for a bug, please include the following:
|
||||||
|
|
||||||
|
- An overview of the issue
|
||||||
|
- Your use case (why is this a bug for you?)
|
||||||
|
- The version of `bee` you are running (include the output of `bee version`)
|
||||||
|
- Steps to reproduce the issue
|
||||||
|
- Eventually, logs from your application.
|
||||||
|
- Ideally, a suggested fix
|
||||||
|
|
||||||
|
The more information you give us, the more able to help we will be!
|
||||||
|
|
||||||
|
### Submitting a Pull Request
|
||||||
|
|
||||||
|
- First of all, make sure to base your work on the `develop` branch (the development branch):
|
||||||
|
|
||||||
|
```
|
||||||
|
# a bugfix branch for develop would be prefixed by fix/
|
||||||
|
# a bugfix branch for master would be prefixed by hotfix/
|
||||||
|
$ git checkout -b feature/my-feature develop
|
||||||
|
```
|
||||||
|
|
||||||
|
- Please create commits containing **related changes**. For example, two different bugfixes should produce two separate commits.
|
||||||
|
A feature should be made of commits splitted by **logical chunks** (no half-done changes). Use your best judgement as to
|
||||||
|
how many commits your changes require.
|
||||||
|
|
||||||
|
- Write insightful and descriptive commit messages. It lets us and future contributors quickly understand your changes
|
||||||
|
without having to read your changes. Please provide a summary in the first line (50-72 characters) and eventually,
|
||||||
|
go to greater lengths in your message's body. A good example can be found in [Angular commit message format](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format).
|
||||||
|
|
||||||
|
- Please **include the appropriate test cases** for your patch.
|
||||||
|
|
||||||
|
- Make sure all tests pass before submitting your changes.
|
||||||
|
|
||||||
|
- Rebase your commits. It may be that new commits have been introduced on `develop`.
|
||||||
|
Rebasing will update your branch with the most recent code and make your changes easier to review:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git fetch
|
||||||
|
$ git rebase origin/develop
|
||||||
|
```
|
||||||
|
|
||||||
|
- Push your changes:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git push origin -u feature/my-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
- Open a pull request against the `develop` branch.
|
||||||
|
|
||||||
|
- If we suggest changes:
|
||||||
|
- Please make the required updates (after discussion if any)
|
||||||
|
- Only create new commits if it makes sense. Generally, you will want to amend your latest commit or rebase your branch after the new changes:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git rebase -i develop
|
||||||
|
# choose which commits to edit and perform the updates
|
||||||
|
```
|
||||||
|
|
||||||
|
- Re-run the tests
|
||||||
|
- Force push to your branch:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git push origin feature/my-feature -f
|
||||||
|
```
|
||||||
|
|
||||||
|
[new-issue]: #submitting-an-issue
|
||||||
|
[new-pr]: #submitting-a-pull-request
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
```text
|
||||||
|
Copyright 2016 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.
|
||||||
```
|
```
|
241
apiapp.go
241
apiapp.go
@ -24,22 +24,23 @@ import (
|
|||||||
var cmdApiapp = &Command{
|
var cmdApiapp = &Command{
|
||||||
// CustomFlags: true,
|
// CustomFlags: true,
|
||||||
UsageLine: "api [appname]",
|
UsageLine: "api [appname]",
|
||||||
Short: "create an api application base on beego framework",
|
Short: "create an API beego application",
|
||||||
Long: `
|
Long: `
|
||||||
create an api application base on beego framework
|
Create an API beego application.
|
||||||
|
|
||||||
bee api [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
|
bee api [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
|
||||||
-tables: a list of table names separated by ',', default is empty, indicating all tables
|
-tables: a list of table names separated by ',' (default is empty, indicating all tables)
|
||||||
-driver: [mysql | postgres | sqlite], the default is mysql
|
-driver: [mysql | postgres | sqlite] (default: mysql)
|
||||||
-conn: the connection string used by the driver, the default is ''
|
-conn: the connection string used by the driver, the default is ''
|
||||||
e.g. for mysql: root:@tcp(127.0.0.1:3306)/test
|
e.g. for mysql: root:@tcp(127.0.0.1:3306)/test
|
||||||
e.g. for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres
|
e.g. for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres
|
||||||
|
|
||||||
if conn is empty will create a example api application. otherwise generate api application based on an existing database.
|
If 'conn' argument is empty, bee api creates an example API application,
|
||||||
|
when 'conn' argument is provided, bee api generates an API application based
|
||||||
|
on the existing database.
|
||||||
|
|
||||||
In the current path, will create a folder named [appname]
|
The command 'api' creates a folder named [appname] and inside the folder deploy
|
||||||
|
the following files/directories structure:
|
||||||
In the appname folder has the follow struct:
|
|
||||||
|
|
||||||
├── conf
|
├── conf
|
||||||
│ └── app.conf
|
│ └── app.conf
|
||||||
@ -68,16 +69,15 @@ EnableDocs = true
|
|||||||
var apiMaingo = `package main
|
var apiMaingo = `package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "{{.Appname}}/docs"
|
|
||||||
_ "{{.Appname}}/routers"
|
_ "{{.Appname}}/routers"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if beego.RunMode == "dev" {
|
if beego.BConfig.RunMode == "dev" {
|
||||||
beego.DirectoryIndex = true
|
beego.BConfig.WebConfig.DirectoryIndex = true
|
||||||
beego.StaticDir["/swagger"] = "swagger"
|
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
|
||||||
}
|
}
|
||||||
beego.Run()
|
beego.Run()
|
||||||
}
|
}
|
||||||
@ -86,7 +86,6 @@ func main() {
|
|||||||
var apiMainconngo = `package main
|
var apiMainconngo = `package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "{{.Appname}}/docs"
|
|
||||||
_ "{{.Appname}}/routers"
|
_ "{{.Appname}}/routers"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
@ -99,9 +98,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if beego.RunMode == "dev" {
|
if beego.BConfig.RunMode == "dev" {
|
||||||
beego.DirectoryIndex = true
|
beego.BConfig.WebConfig.DirectoryIndex = true
|
||||||
beego.StaticDir["/swagger"] = "swagger"
|
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
|
||||||
}
|
}
|
||||||
beego.Run()
|
beego.Run()
|
||||||
}
|
}
|
||||||
@ -297,18 +296,18 @@ type ObjectController struct {
|
|||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title create
|
// @Title Create
|
||||||
// @Description create object
|
// @Description create object
|
||||||
// @Param body body models.Object true "The object content"
|
// @Param body body models.Object true "The object content"
|
||||||
// @Success 200 {string} models.Object.Id
|
// @Success 200 {string} models.Object.Id
|
||||||
// @Failure 403 body is empty
|
// @Failure 403 body is empty
|
||||||
// @router / [post]
|
// @router / [post]
|
||||||
func (this *ObjectController) Post() {
|
func (o *ObjectController) Post() {
|
||||||
var ob models.Object
|
var ob models.Object
|
||||||
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
|
||||||
objectid := models.AddOne(ob)
|
objectid := models.AddOne(ob)
|
||||||
this.Data["json"] = map[string]string{"ObjectId": objectid}
|
o.Data["json"] = map[string]string{"ObjectId": objectid}
|
||||||
this.ServeJson()
|
o.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Get
|
// @Title Get
|
||||||
@ -317,17 +316,17 @@ func (this *ObjectController) Post() {
|
|||||||
// @Success 200 {object} models.Object
|
// @Success 200 {object} models.Object
|
||||||
// @Failure 403 :objectId is empty
|
// @Failure 403 :objectId is empty
|
||||||
// @router /:objectId [get]
|
// @router /:objectId [get]
|
||||||
func (this *ObjectController) Get() {
|
func (o *ObjectController) Get() {
|
||||||
objectId := this.Ctx.Input.Params[":objectId"]
|
objectId := o.Ctx.Input.Param(":objectId")
|
||||||
if objectId != "" {
|
if objectId != "" {
|
||||||
ob, err := models.GetOne(objectId)
|
ob, err := models.GetOne(objectId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Data["json"] = err
|
o.Data["json"] = err.Error()
|
||||||
} else {
|
} else {
|
||||||
this.Data["json"] = ob
|
o.Data["json"] = ob
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.ServeJson()
|
o.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title GetAll
|
// @Title GetAll
|
||||||
@ -335,44 +334,44 @@ func (this *ObjectController) Get() {
|
|||||||
// @Success 200 {object} models.Object
|
// @Success 200 {object} models.Object
|
||||||
// @Failure 403 :objectId is empty
|
// @Failure 403 :objectId is empty
|
||||||
// @router / [get]
|
// @router / [get]
|
||||||
func (this *ObjectController) GetAll() {
|
func (o *ObjectController) GetAll() {
|
||||||
obs := models.GetAll()
|
obs := models.GetAll()
|
||||||
this.Data["json"] = obs
|
o.Data["json"] = obs
|
||||||
this.ServeJson()
|
o.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title update
|
// @Title Update
|
||||||
// @Description update the object
|
// @Description update the object
|
||||||
// @Param objectId path string true "The objectid you want to update"
|
// @Param objectId path string true "The objectid you want to update"
|
||||||
// @Param body body models.Object true "The body"
|
// @Param body body models.Object true "The body"
|
||||||
// @Success 200 {object} models.Object
|
// @Success 200 {object} models.Object
|
||||||
// @Failure 403 :objectId is empty
|
// @Failure 403 :objectId is empty
|
||||||
// @router /:objectId [put]
|
// @router /:objectId [put]
|
||||||
func (this *ObjectController) Put() {
|
func (o *ObjectController) Put() {
|
||||||
objectId := this.Ctx.Input.Params[":objectId"]
|
objectId := o.Ctx.Input.Param(":objectId")
|
||||||
var ob models.Object
|
var ob models.Object
|
||||||
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
|
||||||
|
|
||||||
err := models.Update(objectId, ob.Score)
|
err := models.Update(objectId, ob.Score)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Data["json"] = err
|
o.Data["json"] = err.Error()
|
||||||
} else {
|
} else {
|
||||||
this.Data["json"] = "update success!"
|
o.Data["json"] = "update success!"
|
||||||
}
|
}
|
||||||
this.ServeJson()
|
o.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title delete
|
// @Title Delete
|
||||||
// @Description delete the object
|
// @Description delete the object
|
||||||
// @Param objectId path string true "The objectId you want to delete"
|
// @Param objectId path string true "The objectId you want to delete"
|
||||||
// @Success 200 {string} delete success!
|
// @Success 200 {string} delete success!
|
||||||
// @Failure 403 objectId is empty
|
// @Failure 403 objectId is empty
|
||||||
// @router /:objectId [delete]
|
// @router /:objectId [delete]
|
||||||
func (this *ObjectController) Delete() {
|
func (o *ObjectController) Delete() {
|
||||||
objectId := this.Ctx.Input.Params[":objectId"]
|
objectId := o.Ctx.Input.Param(":objectId")
|
||||||
models.Delete(objectId)
|
models.Delete(objectId)
|
||||||
this.Data["json"] = "delete success!"
|
o.Data["json"] = "delete success!"
|
||||||
this.ServeJson()
|
o.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
`
|
`
|
||||||
@ -390,7 +389,7 @@ type UserController struct {
|
|||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title createUser
|
// @Title CreateUser
|
||||||
// @Description create users
|
// @Description create users
|
||||||
// @Param body body models.User true "body for user content"
|
// @Param body body models.User true "body for user content"
|
||||||
// @Success 200 {int} models.User.Id
|
// @Success 200 {int} models.User.Id
|
||||||
@ -401,17 +400,17 @@ func (u *UserController) Post() {
|
|||||||
json.Unmarshal(u.Ctx.Input.RequestBody, &user)
|
json.Unmarshal(u.Ctx.Input.RequestBody, &user)
|
||||||
uid := models.AddUser(user)
|
uid := models.AddUser(user)
|
||||||
u.Data["json"] = map[string]string{"uid": uid}
|
u.Data["json"] = map[string]string{"uid": uid}
|
||||||
u.ServeJson()
|
u.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Get
|
// @Title GetAll
|
||||||
// @Description get all Users
|
// @Description get all Users
|
||||||
// @Success 200 {object} models.User
|
// @Success 200 {object} models.User
|
||||||
// @router / [get]
|
// @router / [get]
|
||||||
func (u *UserController) GetAll() {
|
func (u *UserController) GetAll() {
|
||||||
users := models.GetAllUsers()
|
users := models.GetAllUsers()
|
||||||
u.Data["json"] = users
|
u.Data["json"] = users
|
||||||
u.ServeJson()
|
u.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Get
|
// @Title Get
|
||||||
@ -425,15 +424,15 @@ func (u *UserController) Get() {
|
|||||||
if uid != "" {
|
if uid != "" {
|
||||||
user, err := models.GetUser(uid)
|
user, err := models.GetUser(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.Data["json"] = err
|
u.Data["json"] = err.Error()
|
||||||
} else {
|
} else {
|
||||||
u.Data["json"] = user
|
u.Data["json"] = user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u.ServeJson()
|
u.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title update
|
// @Title Update
|
||||||
// @Description update the user
|
// @Description update the user
|
||||||
// @Param uid path string true "The uid you want to update"
|
// @Param uid path string true "The uid you want to update"
|
||||||
// @Param body body models.User true "body for user content"
|
// @Param body body models.User true "body for user content"
|
||||||
@ -447,15 +446,15 @@ func (u *UserController) Put() {
|
|||||||
json.Unmarshal(u.Ctx.Input.RequestBody, &user)
|
json.Unmarshal(u.Ctx.Input.RequestBody, &user)
|
||||||
uu, err := models.UpdateUser(uid, &user)
|
uu, err := models.UpdateUser(uid, &user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.Data["json"] = err
|
u.Data["json"] = err.Error()
|
||||||
} else {
|
} else {
|
||||||
u.Data["json"] = uu
|
u.Data["json"] = uu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u.ServeJson()
|
u.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title delete
|
// @Title Delete
|
||||||
// @Description delete the user
|
// @Description delete the user
|
||||||
// @Param uid path string true "The uid you want to delete"
|
// @Param uid path string true "The uid you want to delete"
|
||||||
// @Success 200 {string} delete success!
|
// @Success 200 {string} delete success!
|
||||||
@ -465,14 +464,14 @@ func (u *UserController) Delete() {
|
|||||||
uid := u.GetString(":uid")
|
uid := u.GetString(":uid")
|
||||||
models.DeleteUser(uid)
|
models.DeleteUser(uid)
|
||||||
u.Data["json"] = "delete success!"
|
u.Data["json"] = "delete success!"
|
||||||
u.ServeJson()
|
u.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title login
|
// @Title Login
|
||||||
// @Description Logs user into the system
|
// @Description Logs user into the system
|
||||||
// @Param username query string true "The username for login"
|
// @Param username query string true "The username for login"
|
||||||
// @Param password query string true "The password for login"
|
// @Param password query string true "The password for login"
|
||||||
// @Success 200 {string} lonin success
|
// @Success 200 {string} login success
|
||||||
// @Failure 403 user not exist
|
// @Failure 403 user not exist
|
||||||
// @router /login [get]
|
// @router /login [get]
|
||||||
func (u *UserController) Login() {
|
func (u *UserController) Login() {
|
||||||
@ -483,7 +482,7 @@ func (u *UserController) Login() {
|
|||||||
} else {
|
} else {
|
||||||
u.Data["json"] = "user not exist"
|
u.Data["json"] = "user not exist"
|
||||||
}
|
}
|
||||||
u.ServeJson()
|
u.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title logout
|
// @Title logout
|
||||||
@ -492,7 +491,7 @@ func (u *UserController) Login() {
|
|||||||
// @router /logout [get]
|
// @router /logout [get]
|
||||||
func (u *UserController) Logout() {
|
func (u *UserController) Logout() {
|
||||||
u.Data["json"] = "logout success"
|
u.Data["json"] = "logout success"
|
||||||
u.ServeJson()
|
u.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
`
|
`
|
||||||
@ -545,10 +544,19 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createapi(cmd *Command, args []string) int {
|
func createapi(cmd *Command, args []string) int {
|
||||||
curpath, _ := os.Getwd()
|
ShowShortVersionBanner()
|
||||||
|
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
|
if len(args) < 1 {
|
||||||
|
ColorLog("[ERRO] Argument [appname] is missing\n")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
cmd.Flag.Parse(args[1:])
|
cmd.Flag.Parse(args[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
apppath, packpath, err := checkEnv(args[0])
|
apppath, packpath, err := checkEnv(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@ -559,23 +567,23 @@ func createapi(cmd *Command, args []string) int {
|
|||||||
}
|
}
|
||||||
if conn == "" {
|
if conn == "" {
|
||||||
}
|
}
|
||||||
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, "docs"), 0755)
|
|
||||||
fmt.Println("create docs:", path.Join(apppath, "docs"))
|
|
||||||
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"))
|
ColorLog("[INFO] Creating API...\n")
|
||||||
writetofile(path.Join(apppath, "conf", "app.conf"),
|
|
||||||
strings.Replace(apiconf, "{{.Appname}}", args[0], -1))
|
os.MkdirAll(apppath, 0755)
|
||||||
|
fmt.Fprintf(w, "\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(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m")
|
||||||
|
os.Mkdir(path.Join(apppath, "controllers"), 0755)
|
||||||
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers"), "\x1b[0m")
|
||||||
|
os.Mkdir(path.Join(apppath, "tests"), 0755)
|
||||||
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests"), "\x1b[0m")
|
||||||
|
fmt.Fprintf(w, "\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(apiconf, "{{.Appname}}", path.Base(args[0]), -1))
|
||||||
|
|
||||||
if conn != "" {
|
if conn != "" {
|
||||||
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
|
fmt.Fprintf(w, "\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(apiMainconngo, "{{.Appname}}", packpath, -1)
|
||||||
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1)
|
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1)
|
||||||
if driver == "mysql" {
|
if driver == "mysql" {
|
||||||
@ -583,7 +591,7 @@ func createapi(cmd *Command, args []string) int {
|
|||||||
} else if driver == "postgres" {
|
} else if driver == "postgres" {
|
||||||
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
|
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
|
||||||
}
|
}
|
||||||
writetofile(path.Join(apppath, "main.go"),
|
WriteToFile(path.Join(apppath, "main.go"),
|
||||||
strings.Replace(
|
strings.Replace(
|
||||||
maingoContent,
|
maingoContent,
|
||||||
"{{.conn}}",
|
"{{.conn}}",
|
||||||
@ -594,82 +602,73 @@ func createapi(cmd *Command, args []string) int {
|
|||||||
ColorLog("[INFO] Using '%s' as 'driver'\n", driver)
|
ColorLog("[INFO] Using '%s' as 'driver'\n", driver)
|
||||||
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
||||||
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
||||||
generateAppcode(string(driver), string(conn), "3", string(tables), path.Join(curpath, args[0]))
|
generateAppcode(string(driver), string(conn), "3", string(tables), apppath)
|
||||||
} else {
|
} else {
|
||||||
os.Mkdir(path.Join(apppath, "models"), 0755)
|
os.Mkdir(path.Join(apppath, "models"), 0755)
|
||||||
fmt.Println("create models:", path.Join(apppath, "models"))
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m")
|
||||||
os.Mkdir(path.Join(apppath, "routers"), 0755)
|
os.Mkdir(path.Join(apppath, "routers"), 0755)
|
||||||
fmt.Println(path.Join(apppath, "routers") + string(path.Separator))
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m")
|
||||||
|
|
||||||
fmt.Println("create controllers object.go:", path.Join(apppath, "controllers", "object.go"))
|
fmt.Fprintf(w, "\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"),
|
WriteToFile(path.Join(apppath, "controllers", "object.go"),
|
||||||
strings.Replace(apiControllers, "{{.Appname}}", packpath, -1))
|
strings.Replace(apiControllers, "{{.Appname}}", packpath, -1))
|
||||||
|
|
||||||
fmt.Println("create controllers user.go:", path.Join(apppath, "controllers", "user.go"))
|
fmt.Fprintf(w, "\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"),
|
WriteToFile(path.Join(apppath, "controllers", "user.go"),
|
||||||
strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1))
|
strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1))
|
||||||
|
|
||||||
fmt.Println("create tests default.go:", path.Join(apppath, "tests", "default_test.go"))
|
fmt.Fprintf(w, "\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"),
|
WriteToFile(path.Join(apppath, "tests", "default_test.go"),
|
||||||
strings.Replace(apiTests, "{{.Appname}}", packpath, -1))
|
strings.Replace(apiTests, "{{.Appname}}", packpath, -1))
|
||||||
|
|
||||||
fmt.Println("create routers router.go:", path.Join(apppath, "routers", "router.go"))
|
fmt.Fprintf(w, "\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"),
|
WriteToFile(path.Join(apppath, "routers", "router.go"),
|
||||||
strings.Replace(apirouter, "{{.Appname}}", packpath, -1))
|
strings.Replace(apirouter, "{{.Appname}}", packpath, -1))
|
||||||
|
|
||||||
fmt.Println("create models object.go:", path.Join(apppath, "models", "object.go"))
|
fmt.Fprintf(w, "\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)
|
WriteToFile(path.Join(apppath, "models", "object.go"), apiModels)
|
||||||
|
|
||||||
fmt.Println("create models user.go:", path.Join(apppath, "models", "user.go"))
|
fmt.Fprintf(w, "\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)
|
WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2)
|
||||||
|
|
||||||
fmt.Println("create docs doc.go:", path.Join(apppath, "docs", "doc.go"))
|
fmt.Fprintf(w, "\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, "docs", "doc.go"), "package docs")
|
WriteToFile(path.Join(apppath, "main.go"),
|
||||||
|
|
||||||
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
|
|
||||||
writetofile(path.Join(apppath, "main.go"),
|
|
||||||
strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1))
|
strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1))
|
||||||
}
|
}
|
||||||
|
ColorLog("[SUCC] New API successfully created!\n")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkEnv(appname string) (apppath, packpath string, err error) {
|
func checkEnv(appname string) (apppath, packpath string, err error) {
|
||||||
curpath, err := os.Getwd()
|
gps := GetGOPATHs()
|
||||||
if err != nil {
|
if len(gps) == 0 {
|
||||||
return
|
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||||
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
currpath, _ := os.Getwd()
|
||||||
gopath := os.Getenv("GOPATH")
|
currpath = path.Join(currpath, appname)
|
||||||
Debugf("gopath:%s", gopath)
|
for _, gpath := range gps {
|
||||||
if gopath == "" {
|
gsrcpath := path.Join(gpath, "src")
|
||||||
err = fmt.Errorf("you should set GOPATH in the env")
|
if strings.HasPrefix(currpath, gsrcpath) {
|
||||||
return
|
packpath = strings.Replace(currpath[len(gsrcpath)+1:], string(path.Separator), "/", -1)
|
||||||
}
|
return currpath, packpath, nil
|
||||||
|
|
||||||
appsrcpath := ""
|
|
||||||
haspath := false
|
|
||||||
wgopath := path.SplitList(gopath)
|
|
||||||
for _, wg := range wgopath {
|
|
||||||
wg, _ = path.EvalSymlinks(path.Join(wg, "src"))
|
|
||||||
|
|
||||||
if path.HasPrefix(strings.ToLower(curpath), strings.ToLower(wg)) {
|
|
||||||
haspath = true
|
|
||||||
appsrcpath = wg
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !haspath {
|
// In case of multiple paths in the GOPATH, by default
|
||||||
err = fmt.Errorf("can't create application outside of GOPATH `%s`\n"+
|
// we use the first path
|
||||||
"you first should `cd $GOPATH%ssrc` then use create\n", gopath, string(path.Separator))
|
gopath := gps[0]
|
||||||
return
|
ColorLog("[%s]You current workdir is not a $GOPATH/src, bee will create the application in GOPATH: %s\n", WARN, gopath)
|
||||||
}
|
Debugf("GOPATH: %s", gopath)
|
||||||
apppath = path.Join(curpath, appname)
|
|
||||||
|
gosrcpath := path.Join(gopath, "src")
|
||||||
|
apppath = path.Join(gosrcpath, appname)
|
||||||
|
|
||||||
if _, e := os.Stat(apppath); os.IsNotExist(e) == false {
|
if _, e := os.Stat(apppath); os.IsNotExist(e) == false {
|
||||||
err = fmt.Errorf("path `%s` exists, can not create app without remove it\n", apppath)
|
err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath)
|
||||||
|
ColorLog("[ERRO] Path '%s' already exists\n", apppath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
packpath = strings.Join(strings.Split(apppath[len(appsrcpath)+1:], string(path.Separator)), "/")
|
packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
26
bale.go
26
bale.go
@ -30,13 +30,15 @@ var cmdBale = &Command{
|
|||||||
UsageLine: "bale",
|
UsageLine: "bale",
|
||||||
Short: "packs non-Go files to Go source files",
|
Short: "packs non-Go files to Go source files",
|
||||||
Long: `
|
Long: `
|
||||||
bale packs non-Go files to Go source files and
|
Bale command compress all the static files in to a single binary file.
|
||||||
|
|
||||||
auto-generate unpack function to main package then run it
|
This is usefull to not have to carry static files including js, css, images
|
||||||
|
and views when publishing a project.
|
||||||
|
|
||||||
during the runtime.
|
auto-generate unpack function to main package then run it during the runtime.
|
||||||
|
This is mainly used for zealots who are requiring 100% Go code.
|
||||||
|
|
||||||
This is mainly used for zealots who are requiring 100% Go code.`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -44,6 +46,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runBale(cmd *Command, args []string) int {
|
func runBale(cmd *Command, args []string) int {
|
||||||
|
ShowShortVersionBanner()
|
||||||
|
|
||||||
err := loadConfig()
|
err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
ColorLog("[ERRO] Fail to parse bee.json[ %s ]\n", err)
|
||||||
@ -58,13 +62,13 @@ func runBale(cmd *Command, args []string) int {
|
|||||||
ColorLog("[WARN] Skipped directory( %s )\n", p)
|
ColorLog("[WARN] Skipped directory( %s )\n", p)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ColorLog("[INFO] Packing directory( %s )\n", p)
|
ColorLog("[INFO] Packaging directory( %s )\n", p)
|
||||||
filepath.Walk(p, walkFn)
|
filepath.Walk(p, walkFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate auto-uncompress function.
|
// Generate auto-uncompress function.
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
buf.WriteString(fmt.Sprintf(_BALE_HEADER, conf.Bale.Import,
|
buf.WriteString(fmt.Sprintf(BaleHeader, conf.Bale.Import,
|
||||||
strings.Join(resFiles, "\",\n\t\t\""),
|
strings.Join(resFiles, "\",\n\t\t\""),
|
||||||
strings.Join(resFiles, ",\n\t\tbale.R")))
|
strings.Join(resFiles, ",\n\t\tbale.R")))
|
||||||
|
|
||||||
@ -86,7 +90,7 @@ func runBale(cmd *Command, args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_BALE_HEADER = `package main
|
BaleHeader = `package main
|
||||||
|
|
||||||
import(
|
import(
|
||||||
"os"
|
"os"
|
||||||
@ -174,7 +178,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
|
|||||||
defer fw.Close()
|
defer fw.Close()
|
||||||
|
|
||||||
// Write header.
|
// Write header.
|
||||||
fmt.Fprintf(fw, _HEADER, resPath)
|
fmt.Fprintf(fw, Header, resPath)
|
||||||
|
|
||||||
// Copy and compress data.
|
// Copy and compress data.
|
||||||
gz := gzip.NewWriter(&ByteWriter{Writer: fw})
|
gz := gzip.NewWriter(&ByteWriter{Writer: fw})
|
||||||
@ -182,7 +186,7 @@ func walkFn(resPath string, info os.FileInfo, err error) error {
|
|||||||
gz.Close()
|
gz.Close()
|
||||||
|
|
||||||
// Write footer.
|
// Write footer.
|
||||||
fmt.Fprint(fw, _FOOTER)
|
fmt.Fprint(fw, Footer)
|
||||||
|
|
||||||
resFiles = append(resFiles, resPath)
|
resFiles = append(resFiles, resPath)
|
||||||
return nil
|
return nil
|
||||||
@ -198,7 +202,7 @@ func filterSuffix(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_HEADER = `package bale
|
Header = `package bale
|
||||||
|
|
||||||
import(
|
import(
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -208,7 +212,7 @@ import(
|
|||||||
|
|
||||||
func R%s() []byte {
|
func R%s() []byte {
|
||||||
gz, err := gzip.NewReader(bytes.NewBuffer([]byte{`
|
gz, err := gzip.NewReader(bytes.NewBuffer([]byte{`
|
||||||
_FOOTER = `
|
Footer = `
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
73
banner.go
Normal file
73
banner.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type vars struct {
|
||||||
|
GoVersion string
|
||||||
|
GOOS string
|
||||||
|
GOARCH string
|
||||||
|
NumCPU int
|
||||||
|
GOPATH string
|
||||||
|
GOROOT string
|
||||||
|
Compiler string
|
||||||
|
BeeVersion string
|
||||||
|
BeegoVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now returns the current local time in the specified layout
|
||||||
|
func Now(layout string) string {
|
||||||
|
return time.Now().Format(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitBanner loads the banner and prints it to output
|
||||||
|
// All errors are ignored, the application will not
|
||||||
|
// print the banner in case of error.
|
||||||
|
func InitBanner(out io.Writer, in io.Reader) {
|
||||||
|
if in == nil {
|
||||||
|
ColorLog("[ERRO] The input is nil\n")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
banner, err := ioutil.ReadAll(in)
|
||||||
|
if err != nil {
|
||||||
|
ColorLog("[ERRO] Error trying to read the banner\n")
|
||||||
|
ColorLog("[HINT] %v\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
show(out, string(banner))
|
||||||
|
}
|
||||||
|
|
||||||
|
func show(out io.Writer, content string) {
|
||||||
|
t, err := template.New("banner").
|
||||||
|
Funcs(template.FuncMap{"Now": Now}).
|
||||||
|
Parse(content)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ColorLog("[ERRO] Cannot parse the banner template\n")
|
||||||
|
ColorLog("[HINT] %v\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Execute(out, vars{
|
||||||
|
runtime.Version(),
|
||||||
|
runtime.GOOS,
|
||||||
|
runtime.GOARCH,
|
||||||
|
runtime.NumCPU(),
|
||||||
|
os.Getenv("GOPATH"),
|
||||||
|
runtime.GOROOT(),
|
||||||
|
runtime.Compiler,
|
||||||
|
version,
|
||||||
|
getBeegoVersion(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
3
bee.go
3
bee.go
@ -25,7 +25,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "1.2.2"
|
const version = "1.5.2"
|
||||||
|
|
||||||
type Command struct {
|
type Command struct {
|
||||||
// Run runs the command.
|
// Run runs the command.
|
||||||
@ -85,6 +85,7 @@ var commands = []*Command{
|
|||||||
cmdGenerate,
|
cmdGenerate,
|
||||||
//cmdRundocs,
|
//cmdRundocs,
|
||||||
cmdMigrate,
|
cmdMigrate,
|
||||||
|
cmdFix,
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
1
code.go
1
code.go
@ -251,7 +251,6 @@ func commentAnnotations(src string) []Annotation {
|
|||||||
annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)})
|
annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type sliceWriter struct{ p *[]byte }
|
type sliceWriter struct{ p *[]byte }
|
||||||
|
51
color.go
Normal file
51
color.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 "io"
|
||||||
|
|
||||||
|
type outputMode int
|
||||||
|
|
||||||
|
// DiscardNonColorEscSeq supports the divided color escape sequence.
|
||||||
|
// But non-color escape sequence is not output.
|
||||||
|
// Please use the OutputNonColorEscSeq If you want to output a non-color
|
||||||
|
// escape sequences such as ncurses. However, it does not support the divided
|
||||||
|
// color escape sequence.
|
||||||
|
const (
|
||||||
|
_ outputMode = iota
|
||||||
|
DiscardNonColorEscSeq
|
||||||
|
OutputNonColorEscSeq
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewColorWriter creates and initializes a new ansiColorWriter
|
||||||
|
// using io.Writer w as its initial contents.
|
||||||
|
// In the console of Windows, which change the foreground and background
|
||||||
|
// colors of the text by the escape sequence.
|
||||||
|
// In the console of other systems, which writes to w all text.
|
||||||
|
func NewColorWriter(w io.Writer) io.Writer {
|
||||||
|
return NewModeColorWriter(w, DiscardNonColorEscSeq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewModeColorWriter create and initializes a new ansiColorWriter
|
||||||
|
// by specifying the outputMode.
|
||||||
|
func NewModeColorWriter(w io.Writer, mode outputMode) io.Writer {
|
||||||
|
if _, ok := w.(*colorWriter); !ok {
|
||||||
|
return &colorWriter{
|
||||||
|
w: w,
|
||||||
|
mode: mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
28
colorwriter.go
Normal file
28
colorwriter.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type colorWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
mode outputMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *colorWriter) Write(p []byte) (int, error) {
|
||||||
|
return cw.w.Write(p)
|
||||||
|
}
|
427
colorwriter_windows.go
Normal file
427
colorwriter_windows.go
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type csiState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
outsideCsiCode csiState = iota
|
||||||
|
firstCsiCode
|
||||||
|
secondCsiCode
|
||||||
|
)
|
||||||
|
|
||||||
|
type parseResult int
|
||||||
|
|
||||||
|
const (
|
||||||
|
noConsole parseResult = iota
|
||||||
|
changedColor
|
||||||
|
unknown
|
||||||
|
)
|
||||||
|
|
||||||
|
type colorWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
mode outputMode
|
||||||
|
state csiState
|
||||||
|
paramStartBuf bytes.Buffer
|
||||||
|
paramBuf bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
firstCsiChar byte = '\x1b'
|
||||||
|
secondeCsiChar byte = '['
|
||||||
|
separatorChar byte = ';'
|
||||||
|
sgrCode byte = 'm'
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
foregroundBlue = uint16(0x0001)
|
||||||
|
foregroundGreen = uint16(0x0002)
|
||||||
|
foregroundRed = uint16(0x0004)
|
||||||
|
foregroundIntensity = uint16(0x0008)
|
||||||
|
backgroundBlue = uint16(0x0010)
|
||||||
|
backgroundGreen = uint16(0x0020)
|
||||||
|
backgroundRed = uint16(0x0040)
|
||||||
|
backgroundIntensity = uint16(0x0080)
|
||||||
|
underscore = uint16(0x8000)
|
||||||
|
|
||||||
|
foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
|
||||||
|
backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ansiReset = "0"
|
||||||
|
ansiIntensityOn = "1"
|
||||||
|
ansiIntensityOff = "21"
|
||||||
|
ansiUnderlineOn = "4"
|
||||||
|
ansiUnderlineOff = "24"
|
||||||
|
ansiBlinkOn = "5"
|
||||||
|
ansiBlinkOff = "25"
|
||||||
|
|
||||||
|
ansiForegroundBlack = "30"
|
||||||
|
ansiForegroundRed = "31"
|
||||||
|
ansiForegroundGreen = "32"
|
||||||
|
ansiForegroundYellow = "33"
|
||||||
|
ansiForegroundBlue = "34"
|
||||||
|
ansiForegroundMagenta = "35"
|
||||||
|
ansiForegroundCyan = "36"
|
||||||
|
ansiForegroundWhite = "37"
|
||||||
|
ansiForegroundDefault = "39"
|
||||||
|
|
||||||
|
ansiBackgroundBlack = "40"
|
||||||
|
ansiBackgroundRed = "41"
|
||||||
|
ansiBackgroundGreen = "42"
|
||||||
|
ansiBackgroundYellow = "43"
|
||||||
|
ansiBackgroundBlue = "44"
|
||||||
|
ansiBackgroundMagenta = "45"
|
||||||
|
ansiBackgroundCyan = "46"
|
||||||
|
ansiBackgroundWhite = "47"
|
||||||
|
ansiBackgroundDefault = "49"
|
||||||
|
|
||||||
|
ansiLightForegroundGray = "90"
|
||||||
|
ansiLightForegroundRed = "91"
|
||||||
|
ansiLightForegroundGreen = "92"
|
||||||
|
ansiLightForegroundYellow = "93"
|
||||||
|
ansiLightForegroundBlue = "94"
|
||||||
|
ansiLightForegroundMagenta = "95"
|
||||||
|
ansiLightForegroundCyan = "96"
|
||||||
|
ansiLightForegroundWhite = "97"
|
||||||
|
|
||||||
|
ansiLightBackgroundGray = "100"
|
||||||
|
ansiLightBackgroundRed = "101"
|
||||||
|
ansiLightBackgroundGreen = "102"
|
||||||
|
ansiLightBackgroundYellow = "103"
|
||||||
|
ansiLightBackgroundBlue = "104"
|
||||||
|
ansiLightBackgroundMagenta = "105"
|
||||||
|
ansiLightBackgroundCyan = "106"
|
||||||
|
ansiLightBackgroundWhite = "107"
|
||||||
|
)
|
||||||
|
|
||||||
|
type drawType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
foreground drawType = iota
|
||||||
|
background
|
||||||
|
)
|
||||||
|
|
||||||
|
type winColor struct {
|
||||||
|
code uint16
|
||||||
|
drawType drawType
|
||||||
|
}
|
||||||
|
|
||||||
|
var colorMap = map[string]winColor{
|
||||||
|
ansiForegroundBlack: {0, foreground},
|
||||||
|
ansiForegroundRed: {foregroundRed, foreground},
|
||||||
|
ansiForegroundGreen: {foregroundGreen, foreground},
|
||||||
|
ansiForegroundYellow: {foregroundRed | foregroundGreen, foreground},
|
||||||
|
ansiForegroundBlue: {foregroundBlue, foreground},
|
||||||
|
ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
|
||||||
|
ansiForegroundCyan: {foregroundGreen | foregroundBlue, foreground},
|
||||||
|
ansiForegroundWhite: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
|
||||||
|
ansiBackgroundBlack: {0, background},
|
||||||
|
ansiBackgroundRed: {backgroundRed, background},
|
||||||
|
ansiBackgroundGreen: {backgroundGreen, background},
|
||||||
|
ansiBackgroundYellow: {backgroundRed | backgroundGreen, background},
|
||||||
|
ansiBackgroundBlue: {backgroundBlue, background},
|
||||||
|
ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
|
||||||
|
ansiBackgroundCyan: {backgroundGreen | backgroundBlue, background},
|
||||||
|
ansiBackgroundWhite: {backgroundRed | backgroundGreen | backgroundBlue, background},
|
||||||
|
ansiBackgroundDefault: {0, background},
|
||||||
|
|
||||||
|
ansiLightForegroundGray: {foregroundIntensity, foreground},
|
||||||
|
ansiLightForegroundRed: {foregroundIntensity | foregroundRed, foreground},
|
||||||
|
ansiLightForegroundGreen: {foregroundIntensity | foregroundGreen, foreground},
|
||||||
|
ansiLightForegroundYellow: {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
|
||||||
|
ansiLightForegroundBlue: {foregroundIntensity | foregroundBlue, foreground},
|
||||||
|
ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
|
||||||
|
ansiLightForegroundCyan: {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
ansiLightForegroundWhite: {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},
|
||||||
|
|
||||||
|
ansiLightBackgroundGray: {backgroundIntensity, background},
|
||||||
|
ansiLightBackgroundRed: {backgroundIntensity | backgroundRed, background},
|
||||||
|
ansiLightBackgroundGreen: {backgroundIntensity | backgroundGreen, background},
|
||||||
|
ansiLightBackgroundYellow: {backgroundIntensity | backgroundRed | backgroundGreen, background},
|
||||||
|
ansiLightBackgroundBlue: {backgroundIntensity | backgroundBlue, background},
|
||||||
|
ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
|
||||||
|
ansiLightBackgroundCyan: {backgroundIntensity | backgroundGreen | backgroundBlue, background},
|
||||||
|
ansiLightBackgroundWhite: {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||||
|
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||||
|
defaultAttr *textAttributes
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||||
|
if screenInfo != nil {
|
||||||
|
colorMap[ansiForegroundDefault] = winColor{
|
||||||
|
screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
|
||||||
|
foreground,
|
||||||
|
}
|
||||||
|
colorMap[ansiBackgroundDefault] = winColor{
|
||||||
|
screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
|
||||||
|
background,
|
||||||
|
}
|
||||||
|
defaultAttr = convertTextAttr(screenInfo.WAttributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type coord struct {
|
||||||
|
X, Y int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type smallRect struct {
|
||||||
|
Left, Top, Right, Bottom int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleScreenBufferInfo struct {
|
||||||
|
DwSize coord
|
||||||
|
DwCursorPosition coord
|
||||||
|
WAttributes uint16
|
||||||
|
SrWindow smallRect
|
||||||
|
DwMaximumWindowSize coord
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
|
||||||
|
var csbi consoleScreenBufferInfo
|
||||||
|
ret, _, _ := procGetConsoleScreenBufferInfo.Call(
|
||||||
|
hConsoleOutput,
|
||||||
|
uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
if ret == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &csbi
|
||||||
|
}
|
||||||
|
|
||||||
|
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
|
||||||
|
ret, _, _ := procSetConsoleTextAttribute.Call(
|
||||||
|
hConsoleOutput,
|
||||||
|
uintptr(wAttributes))
|
||||||
|
return ret != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type textAttributes struct {
|
||||||
|
foregroundColor uint16
|
||||||
|
backgroundColor uint16
|
||||||
|
foregroundIntensity uint16
|
||||||
|
backgroundIntensity uint16
|
||||||
|
underscore uint16
|
||||||
|
otherAttributes uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertTextAttr(winAttr uint16) *textAttributes {
|
||||||
|
fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
|
||||||
|
bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
|
||||||
|
fgIntensity := winAttr & foregroundIntensity
|
||||||
|
bgIntensity := winAttr & backgroundIntensity
|
||||||
|
underline := winAttr & underscore
|
||||||
|
otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
|
||||||
|
return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertWinAttr(textAttr *textAttributes) uint16 {
|
||||||
|
var winAttr uint16
|
||||||
|
winAttr |= textAttr.foregroundColor
|
||||||
|
winAttr |= textAttr.backgroundColor
|
||||||
|
winAttr |= textAttr.foregroundIntensity
|
||||||
|
winAttr |= textAttr.backgroundIntensity
|
||||||
|
winAttr |= textAttr.underscore
|
||||||
|
winAttr |= textAttr.otherAttributes
|
||||||
|
return winAttr
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeColor(param []byte) parseResult {
|
||||||
|
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
|
||||||
|
if screenInfo == nil {
|
||||||
|
return noConsole
|
||||||
|
}
|
||||||
|
|
||||||
|
winAttr := convertTextAttr(screenInfo.WAttributes)
|
||||||
|
strParam := string(param)
|
||||||
|
if len(strParam) <= 0 {
|
||||||
|
strParam = "0"
|
||||||
|
}
|
||||||
|
csiParam := strings.Split(strParam, string(separatorChar))
|
||||||
|
for _, p := range csiParam {
|
||||||
|
c, ok := colorMap[p]
|
||||||
|
switch {
|
||||||
|
case !ok:
|
||||||
|
switch p {
|
||||||
|
case ansiReset:
|
||||||
|
winAttr.foregroundColor = defaultAttr.foregroundColor
|
||||||
|
winAttr.backgroundColor = defaultAttr.backgroundColor
|
||||||
|
winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
|
||||||
|
winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
|
||||||
|
winAttr.underscore = 0
|
||||||
|
winAttr.otherAttributes = 0
|
||||||
|
case ansiIntensityOn:
|
||||||
|
winAttr.foregroundIntensity = foregroundIntensity
|
||||||
|
case ansiIntensityOff:
|
||||||
|
winAttr.foregroundIntensity = 0
|
||||||
|
case ansiUnderlineOn:
|
||||||
|
winAttr.underscore = underscore
|
||||||
|
case ansiUnderlineOff:
|
||||||
|
winAttr.underscore = 0
|
||||||
|
case ansiBlinkOn:
|
||||||
|
winAttr.backgroundIntensity = backgroundIntensity
|
||||||
|
case ansiBlinkOff:
|
||||||
|
winAttr.backgroundIntensity = 0
|
||||||
|
default:
|
||||||
|
// unknown code
|
||||||
|
}
|
||||||
|
case c.drawType == foreground:
|
||||||
|
winAttr.foregroundColor = c.code
|
||||||
|
case c.drawType == background:
|
||||||
|
winAttr.backgroundColor = c.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
winTextAttribute := convertWinAttr(winAttr)
|
||||||
|
setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)
|
||||||
|
|
||||||
|
return changedColor
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEscapeSequence(command byte, param []byte) parseResult {
|
||||||
|
if defaultAttr == nil {
|
||||||
|
return noConsole
|
||||||
|
}
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case sgrCode:
|
||||||
|
return changeColor(param)
|
||||||
|
default:
|
||||||
|
return unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *colorWriter) flushBuffer() (int, error) {
|
||||||
|
return cw.flushTo(cw.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *colorWriter) resetBuffer() (int, error) {
|
||||||
|
return cw.flushTo(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *colorWriter) flushTo(w io.Writer) (int, error) {
|
||||||
|
var n1, n2 int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
startBytes := cw.paramStartBuf.Bytes()
|
||||||
|
cw.paramStartBuf.Reset()
|
||||||
|
if w != nil {
|
||||||
|
n1, err = cw.w.Write(startBytes)
|
||||||
|
if err != nil {
|
||||||
|
return n1, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n1 = len(startBytes)
|
||||||
|
}
|
||||||
|
paramBytes := cw.paramBuf.Bytes()
|
||||||
|
cw.paramBuf.Reset()
|
||||||
|
if w != nil {
|
||||||
|
n2, err = cw.w.Write(paramBytes)
|
||||||
|
if err != nil {
|
||||||
|
return n1 + n2, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n2 = len(paramBytes)
|
||||||
|
}
|
||||||
|
return n1 + n2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isParameterChar(b byte) bool {
|
||||||
|
return ('0' <= b && b <= '9') || b == separatorChar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *colorWriter) Write(p []byte) (int, error) {
|
||||||
|
r, nw, first, last := 0, 0, 0, 0
|
||||||
|
if cw.mode != DiscardNonColorEscSeq {
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
cw.resetBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for i, ch := range p {
|
||||||
|
switch cw.state {
|
||||||
|
case outsideCsiCode:
|
||||||
|
if ch == firstCsiChar {
|
||||||
|
cw.paramStartBuf.WriteByte(ch)
|
||||||
|
cw.state = firstCsiCode
|
||||||
|
}
|
||||||
|
case firstCsiCode:
|
||||||
|
switch ch {
|
||||||
|
case firstCsiChar:
|
||||||
|
cw.paramStartBuf.WriteByte(ch)
|
||||||
|
break
|
||||||
|
case secondeCsiChar:
|
||||||
|
cw.paramStartBuf.WriteByte(ch)
|
||||||
|
cw.state = secondCsiCode
|
||||||
|
last = i - 1
|
||||||
|
default:
|
||||||
|
cw.resetBuffer()
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
}
|
||||||
|
case secondCsiCode:
|
||||||
|
if isParameterChar(ch) {
|
||||||
|
cw.paramBuf.WriteByte(ch)
|
||||||
|
} else {
|
||||||
|
nw, err = cw.w.Write(p[first:last])
|
||||||
|
r += nw
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
first = i + 1
|
||||||
|
result := parseEscapeSequence(ch, cw.paramBuf.Bytes())
|
||||||
|
if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) {
|
||||||
|
cw.paramBuf.WriteByte(ch)
|
||||||
|
nw, err := cw.flushBuffer()
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
r += nw
|
||||||
|
} else {
|
||||||
|
n, _ := cw.resetBuffer()
|
||||||
|
// Add one more to the size of the buffer for the last ch
|
||||||
|
r += n + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
cw.state = outsideCsiCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
|
||||||
|
nw, err = cw.w.Write(p[first:len(p)])
|
||||||
|
r += nw
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, err
|
||||||
|
}
|
46
conf.go
46
conf.go
@ -16,10 +16,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CONF_VER = 0
|
const ConfVer = 0
|
||||||
|
|
||||||
var defaultConf = `{
|
var defaultConf = `{
|
||||||
"version": 0,
|
"version": 0,
|
||||||
@ -50,20 +53,20 @@ var conf struct {
|
|||||||
Install bool
|
Install bool
|
||||||
}
|
}
|
||||||
// Indicates whether execute "go install" before "go build".
|
// Indicates whether execute "go install" before "go build".
|
||||||
GoInstall bool `json:"go_install"`
|
GoInstall bool `json:"go_install" yaml:"go_install"`
|
||||||
WatchExt []string `json:"watch_ext"`
|
WatchExt []string `json:"watch_ext" yaml:"watch_ext"`
|
||||||
DirStruct struct {
|
DirStruct struct {
|
||||||
WatchAll bool `json:"watch_all"`
|
WatchAll bool `json:"watch_all" yaml:"watch_all"`
|
||||||
Controllers string
|
Controllers string
|
||||||
Models string
|
Models string
|
||||||
Others []string // Other directories.
|
Others []string // Other directories.
|
||||||
} `json:"dir_structure"`
|
} `json:"dir_structure" yaml:"dir_structure"`
|
||||||
CmdArgs []string `json:"cmd_args"`
|
CmdArgs []string `json:"cmd_args" yaml:"cmd_args"`
|
||||||
Envs []string
|
Envs []string
|
||||||
Bale struct {
|
Bale struct {
|
||||||
Import string
|
Import string
|
||||||
Dirs []string
|
Dirs []string
|
||||||
IngExt []string `json:"ignore_ext"`
|
IngExt []string `json:"ignore_ext" yaml:"ignore_ext"`
|
||||||
}
|
}
|
||||||
Database struct {
|
Database struct {
|
||||||
Driver string
|
Driver string
|
||||||
@ -73,14 +76,9 @@ var conf struct {
|
|||||||
|
|
||||||
// loadConfig loads customized configuration.
|
// loadConfig loads customized configuration.
|
||||||
func loadConfig() error {
|
func loadConfig() error {
|
||||||
|
foundConf := false
|
||||||
f, err := os.Open("bee.json")
|
f, err := os.Open("bee.json")
|
||||||
if err != nil {
|
if err == nil {
|
||||||
// Use default.
|
|
||||||
err = json.Unmarshal([]byte(defaultConf), &conf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
ColorLog("[INFO] Detected bee.json\n")
|
ColorLog("[INFO] Detected bee.json\n")
|
||||||
d := json.NewDecoder(f)
|
d := json.NewDecoder(f)
|
||||||
@ -88,10 +86,26 @@ func loadConfig() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
foundConf = true
|
||||||
|
}
|
||||||
|
byml, erryml := ioutil.ReadFile("Beefile")
|
||||||
|
if erryml == nil {
|
||||||
|
ColorLog("[INFO] Detected Beefile\n")
|
||||||
|
err = yaml.Unmarshal(byml, &conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
foundConf = true
|
||||||
|
}
|
||||||
|
if !foundConf {
|
||||||
|
// Use default.
|
||||||
|
err = json.Unmarshal([]byte(defaultConf), &conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check format version.
|
// Check format version.
|
||||||
if conf.Version != CONF_VER {
|
if conf.Version != ConfVer {
|
||||||
ColorLog("[WARN] Your bee.json is out-of-date, please update!\n")
|
ColorLog("[WARN] Your bee.json is out-of-date, please update!\n")
|
||||||
ColorLog("[HINT] Compare bee.json under bee source code path and yours\n")
|
ColorLog("[HINT] Compare bee.json under bee source code path and yours\n")
|
||||||
}
|
}
|
||||||
|
223
fix.go
Normal file
223
fix.go
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdFix = &Command{
|
||||||
|
UsageLine: "fix",
|
||||||
|
Short: "fix the beego application to make it compatible with beego 1.6",
|
||||||
|
Long: `
|
||||||
|
As from beego1.6, there's some incompatible code with the old version.
|
||||||
|
|
||||||
|
bee fix help to upgrade the application to beego 1.6
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdFix.Run = runFix
|
||||||
|
}
|
||||||
|
|
||||||
|
func runFix(cmd *Command, args []string) int {
|
||||||
|
ShowShortVersionBanner()
|
||||||
|
|
||||||
|
ColorLog("[INFO] Upgrading the application...\n")
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
ColorLog("[ERRO] GetCurrent Path:%s\n", err)
|
||||||
|
}
|
||||||
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if info.IsDir() {
|
||||||
|
if strings.HasPrefix(info.Name(), ".") {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(info.Name(), ".exe") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = fixFile(path)
|
||||||
|
fmt.Println("\tfix\t", path)
|
||||||
|
if err != nil {
|
||||||
|
ColorLog("[ERRO] Could not fix file: %s\n", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
ColorLog("[INFO] Upgrade done!\n")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var rules = []string{
|
||||||
|
"beego.AppName", "beego.BConfig.AppName",
|
||||||
|
"beego.RunMode", "beego.BConfig.RunMode",
|
||||||
|
"beego.RecoverPanic", "beego.BConfig.RecoverPanic",
|
||||||
|
"beego.RouterCaseSensitive", "beego.BConfig.RouterCaseSensitive",
|
||||||
|
"beego.BeegoServerName", "beego.BConfig.ServerName",
|
||||||
|
"beego.EnableGzip", "beego.BConfig.EnableGzip",
|
||||||
|
"beego.ErrorsShow", "beego.BConfig.EnableErrorsShow",
|
||||||
|
"beego.CopyRequestBody", "beego.BConfig.CopyRequestBody",
|
||||||
|
"beego.MaxMemory", "beego.BConfig.MaxMemory",
|
||||||
|
"beego.Graceful", "beego.BConfig.Listen.Graceful",
|
||||||
|
"beego.HttpAddr", "beego.BConfig.Listen.HTTPAddr",
|
||||||
|
"beego.HttpPort", "beego.BConfig.Listen.HTTPPort",
|
||||||
|
"beego.ListenTCP4", "beego.BConfig.Listen.ListenTCP4",
|
||||||
|
"beego.EnableHttpListen", "beego.BConfig.Listen.EnableHTTP",
|
||||||
|
"beego.EnableHttpTLS", "beego.BConfig.Listen.EnableHTTPS",
|
||||||
|
"beego.HttpsAddr", "beego.BConfig.Listen.HTTPSAddr",
|
||||||
|
"beego.HttpsPort", "beego.BConfig.Listen.HTTPSPort",
|
||||||
|
"beego.HttpCertFile", "beego.BConfig.Listen.HTTPSCertFile",
|
||||||
|
"beego.HttpKeyFile", "beego.BConfig.Listen.HTTPSKeyFile",
|
||||||
|
"beego.EnableAdmin", "beego.BConfig.Listen.EnableAdmin",
|
||||||
|
"beego.AdminHttpAddr", "beego.BConfig.Listen.AdminAddr",
|
||||||
|
"beego.AdminHttpPort", "beego.BConfig.Listen.AdminPort",
|
||||||
|
"beego.UseFcgi", "beego.BConfig.Listen.EnableFcgi",
|
||||||
|
"beego.HttpServerTimeOut", "beego.BConfig.Listen.ServerTimeOut",
|
||||||
|
"beego.AutoRender", "beego.BConfig.WebConfig.AutoRender",
|
||||||
|
"beego.ViewsPath", "beego.BConfig.WebConfig.ViewsPath",
|
||||||
|
"beego.StaticDir", "beego.BConfig.WebConfig.StaticDir",
|
||||||
|
"beego.StaticExtensionsToGzip", "beego.BConfig.WebConfig.StaticExtensionsToGzip",
|
||||||
|
"beego.DirectoryIndex", "beego.BConfig.WebConfig.DirectoryIndex",
|
||||||
|
"beego.FlashName", "beego.BConfig.WebConfig.FlashName",
|
||||||
|
"beego.FlashSeperator", "beego.BConfig.WebConfig.FlashSeparator",
|
||||||
|
"beego.EnableDocs", "beego.BConfig.WebConfig.EnableDocs",
|
||||||
|
"beego.XSRFKEY", "beego.BConfig.WebConfig.XSRFKey",
|
||||||
|
"beego.EnableXSRF", "beego.BConfig.WebConfig.EnableXSRF",
|
||||||
|
"beego.XSRFExpire", "beego.BConfig.WebConfig.XSRFExpire",
|
||||||
|
"beego.TemplateLeft", "beego.BConfig.WebConfig.TemplateLeft",
|
||||||
|
"beego.TemplateRight", "beego.BConfig.WebConfig.TemplateRight",
|
||||||
|
"beego.SessionOn", "beego.BConfig.WebConfig.Session.SessionOn",
|
||||||
|
"beego.SessionProvider", "beego.BConfig.WebConfig.Session.SessionProvider",
|
||||||
|
"beego.SessionName", "beego.BConfig.WebConfig.Session.SessionName",
|
||||||
|
"beego.SessionGCMaxLifetime", "beego.BConfig.WebConfig.Session.SessionGCMaxLifetime",
|
||||||
|
"beego.SessionSavePath", "beego.BConfig.WebConfig.Session.SessionProviderConfig",
|
||||||
|
"beego.SessionCookieLifeTime", "beego.BConfig.WebConfig.Session.SessionCookieLifeTime",
|
||||||
|
"beego.SessionAutoSetCookie", "beego.BConfig.WebConfig.Session.SessionAutoSetCookie",
|
||||||
|
"beego.SessionDomain", "beego.BConfig.WebConfig.Session.SessionDomain",
|
||||||
|
"Ctx.Input.CopyBody(", "Ctx.Input.CopyBody(beego.BConfig.MaxMemory",
|
||||||
|
".UrlFor(", ".URLFor(",
|
||||||
|
".ServeJson(", ".ServeJSON(",
|
||||||
|
".ServeXml(", ".ServeXML(",
|
||||||
|
".ServeJsonp(", ".ServeJSONP(",
|
||||||
|
".XsrfToken(", ".XSRFToken(",
|
||||||
|
".CheckXsrfCookie(", ".CheckXSRFCookie(",
|
||||||
|
".XsrfFormHtml(", ".XSRFFormHTML(",
|
||||||
|
"beego.UrlFor(", "beego.URLFor(",
|
||||||
|
"beego.GlobalDocApi", "beego.GlobalDocAPI",
|
||||||
|
"beego.Errorhandler", "beego.ErrorHandler",
|
||||||
|
"Output.Jsonp(", "Output.JSONP(",
|
||||||
|
"Output.Json(", "Output.JSON(",
|
||||||
|
"Output.Xml(", "Output.XML(",
|
||||||
|
"Input.Uri()", "Input.URI()",
|
||||||
|
"Input.Url()", "Input.URL()",
|
||||||
|
"Input.AcceptsHtml()", "Input.AcceptsHTML()",
|
||||||
|
"Input.AcceptsXml()", "Input.AcceptsXML()",
|
||||||
|
"Input.AcceptsJson()", "Input.AcceptsJSON()",
|
||||||
|
"Ctx.XsrfToken()", "Ctx.XSRFToken()",
|
||||||
|
"Ctx.CheckXsrfCookie()", "Ctx.CheckXSRFCookie()",
|
||||||
|
"session.SessionStore", "session.Store",
|
||||||
|
".TplNames", ".TplName",
|
||||||
|
"swagger.ApiRef", "swagger.APIRef",
|
||||||
|
"swagger.ApiDeclaration", "swagger.APIDeclaration",
|
||||||
|
"swagger.Api", "swagger.API",
|
||||||
|
"swagger.ApiRef", "swagger.APIRef",
|
||||||
|
"swagger.Infomation", "swagger.Information",
|
||||||
|
"toolbox.UrlMap", "toolbox.URLMap",
|
||||||
|
"logs.LoggerInterface", "logs.Logger",
|
||||||
|
"Input.Request", "Input.Context.Request",
|
||||||
|
"Input.Params)", "Input.Params())",
|
||||||
|
"httplib.BeegoHttpSettings", "httplib.BeegoHTTPSettings",
|
||||||
|
"httplib.BeegoHttpRequest", "httplib.BeegoHTTPRequest",
|
||||||
|
".TlsClientConfig", ".TLSClientConfig",
|
||||||
|
".JsonBody", ".JSONBody",
|
||||||
|
".ToJson", ".ToJSON",
|
||||||
|
".ToXml", ".ToXML",
|
||||||
|
"beego.Html2str", "beego.HTML2str",
|
||||||
|
"beego.AssetsCss", "beego.AssetsCSS",
|
||||||
|
"orm.DR_Sqlite", "orm.DRSqlite",
|
||||||
|
"orm.DR_Postgres", "orm.DRPostgres",
|
||||||
|
"orm.DR_MySQL", "orm.DRMySQL",
|
||||||
|
"orm.DR_Oracle", "orm.DROracle",
|
||||||
|
"orm.Col_Add", "orm.ColAdd",
|
||||||
|
"orm.Col_Minus", "orm.ColMinus",
|
||||||
|
"orm.Col_Multiply", "orm.ColMultiply",
|
||||||
|
"orm.Col_Except", "orm.ColExcept",
|
||||||
|
"GenerateOperatorSql", "GenerateOperatorSQL",
|
||||||
|
"OperatorSql", "OperatorSQL",
|
||||||
|
"orm.Debug_Queries", "orm.DebugQueries",
|
||||||
|
"orm.COMMA_SPACE", "orm.CommaSpace",
|
||||||
|
".SendOut()", ".DoRequest()",
|
||||||
|
"validation.ValidationError", "validation.Error",
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixFile(file string) error {
|
||||||
|
rp := strings.NewReplacer(rules...)
|
||||||
|
content, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fixed := rp.Replace(string(content))
|
||||||
|
|
||||||
|
// forword the RequestBody from the replace
|
||||||
|
// "Input.Request", "Input.Context.Request",
|
||||||
|
fixed = strings.Replace(fixed, "Input.Context.RequestBody", "Input.RequestBody", -1)
|
||||||
|
|
||||||
|
// regexp replace
|
||||||
|
pareg := regexp.MustCompile(`(Input.Params\[")(.*)("])`)
|
||||||
|
fixed = pareg.ReplaceAllString(fixed, "Input.Param(\"$2\")")
|
||||||
|
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])(\s)(=)(\s)(.*)`)
|
||||||
|
fixed = pareg.ReplaceAllString(fixed, "Input.SetData(\"$2\", $7)")
|
||||||
|
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])`)
|
||||||
|
fixed = pareg.ReplaceAllString(fixed, "Input.Data(\"$2\")")
|
||||||
|
// fix the cache object Put method
|
||||||
|
pareg = regexp.MustCompile(`(\.Put\(\")(.*)(\",)(\s)(.*)(,\s*)([^\*.]*)(\))`)
|
||||||
|
if pareg.MatchString(fixed) && strings.HasSuffix(file, ".go") {
|
||||||
|
fixed = pareg.ReplaceAllString(fixed, ".Put(\"$2\", $5, $7*time.Second)")
|
||||||
|
fset := token.NewFileSet() // positions are relative to fset
|
||||||
|
f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Print the imports from the file's AST.
|
||||||
|
hasTimepkg := false
|
||||||
|
for _, s := range f.Imports {
|
||||||
|
if s.Path.Value == `"time"` {
|
||||||
|
hasTimepkg = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasTimepkg {
|
||||||
|
fixed = strings.Replace(fixed, "import (", "import (\n\t\"time\"", 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// replace the v.Apis in docs.go
|
||||||
|
if strings.Contains(file, "docs.go") {
|
||||||
|
fixed = strings.Replace(fixed, "v.Apis", "v.APIs", -1)
|
||||||
|
}
|
||||||
|
// replace the config file
|
||||||
|
if strings.HasSuffix(file, ".conf") {
|
||||||
|
fixed = strings.Replace(fixed, "HttpCertFile", "HTTPSCertFile", -1)
|
||||||
|
fixed = strings.Replace(fixed, "HttpKeyFile", "HTTPSKeyFile", -1)
|
||||||
|
fixed = strings.Replace(fixed, "EnableHttpListen", "HTTPEnable", -1)
|
||||||
|
fixed = strings.Replace(fixed, "EnableHttpTLS", "EnableHTTPS", -1)
|
||||||
|
fixed = strings.Replace(fixed, "EnableHttpTLS", "EnableHTTPS", -1)
|
||||||
|
fixed = strings.Replace(fixed, "BeegoServerName", "ServerName", -1)
|
||||||
|
fixed = strings.Replace(fixed, "AdminHttpAddr", "AdminAddr", -1)
|
||||||
|
fixed = strings.Replace(fixed, "AdminHttpPort", "AdminPort", -1)
|
||||||
|
fixed = strings.Replace(fixed, "HttpServerTimeOut", "ServerTimeOut", -1)
|
||||||
|
}
|
||||||
|
err = os.Truncate(file, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(file, []byte(fixed), 0666)
|
||||||
|
}
|
46
g.go
46
g.go
@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
var cmdGenerate = &Command{
|
var cmdGenerate = &Command{
|
||||||
UsageLine: "generate [Command]",
|
UsageLine: "generate [Command]",
|
||||||
@ -32,7 +35,7 @@ bee generate model [modelname] [-fields=""]
|
|||||||
-fields: a list of table fields. Format: field:type, ...
|
-fields: a list of table fields. Format: field:type, ...
|
||||||
|
|
||||||
bee generate controller [controllerfile]
|
bee generate controller [controllerfile]
|
||||||
generate RESTFul controllers
|
generate RESTful controllers
|
||||||
|
|
||||||
bee generate view [viewpath]
|
bee generate view [viewpath]
|
||||||
generate CRUD view in viewpath
|
generate CRUD view in viewpath
|
||||||
@ -74,19 +77,21 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateCode(cmd *Command, args []string) int {
|
func generateCode(cmd *Command, args []string) int {
|
||||||
curpath, _ := os.Getwd()
|
ShowShortVersionBanner()
|
||||||
|
|
||||||
|
currpath, _ := os.Getwd()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
ColorLog("[ERRO] command is missing\n")
|
ColorLog("[ERRO] command is missing\n")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
gopath := os.Getenv("GOPATH")
|
gps := GetGOPATHs()
|
||||||
Debugf("gopath:%s", gopath)
|
if len(gps) == 0 {
|
||||||
if gopath == "" {
|
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||||
ColorLog("[ERRO] $GOPATH not found\n")
|
|
||||||
ColorLog("[HINT] Set $GOPATH in your environment vairables\n")
|
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
gopath := gps[0]
|
||||||
|
Debugf("GOPATH: %s", gopath)
|
||||||
|
|
||||||
gcmd := args[0]
|
gcmd := args[0]
|
||||||
switch gcmd {
|
switch gcmd {
|
||||||
@ -119,10 +124,9 @@ func generateCode(cmd *Command, args []string) int {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
sname := args[1]
|
sname := args[1]
|
||||||
ColorLog("[INFO] Using '%s' as scaffold name\n", sname)
|
generateScaffold(sname, fields.String(), currpath, driver.String(), conn.String())
|
||||||
generateScaffold(sname, fields.String(), curpath, driver.String(), conn.String())
|
|
||||||
case "docs":
|
case "docs":
|
||||||
generateDocs(curpath)
|
generateDocs(currpath)
|
||||||
case "appcode":
|
case "appcode":
|
||||||
// load config
|
// load config
|
||||||
err := loadConfig()
|
err := loadConfig()
|
||||||
@ -153,7 +157,7 @@ func generateCode(cmd *Command, args []string) int {
|
|||||||
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
||||||
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
||||||
ColorLog("[INFO] Using '%s' as 'level'\n", level)
|
ColorLog("[INFO] Using '%s' as 'level'\n", level)
|
||||||
generateAppcode(driver.String(), conn.String(), level.String(), tables.String(), curpath)
|
generateAppcode(driver.String(), conn.String(), level.String(), tables.String(), currpath)
|
||||||
case "migration":
|
case "migration":
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||||
@ -166,14 +170,15 @@ func generateCode(cmd *Command, args []string) int {
|
|||||||
upsql := ""
|
upsql := ""
|
||||||
downsql := ""
|
downsql := ""
|
||||||
if fields != "" {
|
if fields != "" {
|
||||||
upsql = `m.Sql("CREATE TABLE ` + mname + "(" + generateSQLFromFields(fields.String()) + `)");`
|
dbMigrator := newDBDriver()
|
||||||
downsql = `m.Sql("DROP TABLE ` + "`" + mname + "`" + `")`
|
upsql = dbMigrator.generateCreateUp(mname)
|
||||||
|
downsql = dbMigrator.generateCreateDown(mname)
|
||||||
}
|
}
|
||||||
generateMigration(mname, upsql, downsql, curpath)
|
generateMigration(mname, upsql, downsql, currpath)
|
||||||
case "controller":
|
case "controller":
|
||||||
if len(args) == 2 {
|
if len(args) == 2 {
|
||||||
cname := args[1]
|
cname := args[1]
|
||||||
generateController(cname, curpath)
|
generateController(cname, currpath)
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||||
ColorLog("[HINT] Usage: bee generate controller [controllername]\n")
|
ColorLog("[HINT] Usage: bee generate controller [controllername]\n")
|
||||||
@ -192,20 +197,19 @@ func generateCode(cmd *Command, args []string) int {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
sname := args[1]
|
sname := args[1]
|
||||||
ColorLog("[INFO] Using '%s' as model name\n", sname)
|
generateModel(sname, fields.String(), currpath)
|
||||||
generateModel(sname, fields.String(), curpath)
|
|
||||||
case "view":
|
case "view":
|
||||||
if len(args) == 2 {
|
if len(args) == 2 {
|
||||||
cname := args[1]
|
cname := args[1]
|
||||||
generateView(cname, curpath)
|
generateView(cname, currpath)
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] Wrong number of arguments\n")
|
ColorLog("[ERRO] Wrong number of arguments\n")
|
||||||
ColorLog("[HINT] Usage: bee generate view [viewpath]\n")
|
ColorLog("[HINT] Usage: bee generate view [viewpath]\n")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
ColorLog("[ERRO] command is missing\n")
|
ColorLog("[ERRO] Command is missing\n")
|
||||||
}
|
}
|
||||||
ColorLog("[SUCC] generate successfully created!\n")
|
ColorLog("[SUCC] %s successfully generated!\n", strings.Title(gcmd))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
233
g_appcode.go
233
g_appcode.go
@ -29,9 +29,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
O_MODEL byte = 1 << iota
|
OModel byte = 1 << iota
|
||||||
O_CONTROLLER
|
OController
|
||||||
O_ROUTER
|
ORouter
|
||||||
)
|
)
|
||||||
|
|
||||||
// DbTransformer has method to reverse engineer a database schema to restful api code
|
// DbTransformer has method to reverse engineer a database schema to restful api code
|
||||||
@ -118,6 +118,7 @@ var typeMappingPostgres = map[string]string{
|
|||||||
"time": "time.Time",
|
"time": "time.Time",
|
||||||
"timestamp": "time.Time",
|
"timestamp": "time.Time",
|
||||||
"timestamp without time zone": "time.Time",
|
"timestamp without time zone": "time.Time",
|
||||||
|
"timestamp with time zone": "time.Time",
|
||||||
"interval": "string", // time interval, string for now
|
"interval": "string", // time interval, string for now
|
||||||
"real": "float32", // float & decimal
|
"real": "float32", // float & decimal
|
||||||
"double precision": "float64",
|
"double precision": "float64",
|
||||||
@ -130,6 +131,7 @@ var typeMappingPostgres = map[string]string{
|
|||||||
"USER-DEFINED": "string", // user defined
|
"USER-DEFINED": "string", // user defined
|
||||||
"uuid": "string", // uuid
|
"uuid": "string", // uuid
|
||||||
"json": "string", // json
|
"json": "string", // json
|
||||||
|
"jsonb": "string",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table represent a table in a database
|
// Table represent a table in a database
|
||||||
@ -257,11 +259,11 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
|
|||||||
var mode byte
|
var mode byte
|
||||||
switch level {
|
switch level {
|
||||||
case "1":
|
case "1":
|
||||||
mode = O_MODEL
|
mode = OModel
|
||||||
case "2":
|
case "2":
|
||||||
mode = O_MODEL | O_CONTROLLER
|
mode = OModel | OController
|
||||||
case "3":
|
case "3":
|
||||||
mode = O_MODEL | O_CONTROLLER | O_ROUTER
|
mode = OModel | OController | ORouter
|
||||||
default:
|
default:
|
||||||
ColorLog("[ERRO] Invalid 'level' option: %s\n", level)
|
ColorLog("[ERRO] Invalid 'level' option: %s\n", level)
|
||||||
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
|
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
|
||||||
@ -290,7 +292,7 @@ func generateAppcode(driver, connStr, level, tables, currpath string) {
|
|||||||
|
|
||||||
// Generate takes table, column and foreign key information from database connection
|
// Generate takes table, column and foreign key information from database connection
|
||||||
// and generate corresponding golang source files
|
// and generate corresponding golang source files
|
||||||
func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) {
|
func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, apppath string) {
|
||||||
db, err := sql.Open(dbms, connStr)
|
db, err := sql.Open(dbms, connStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err)
|
ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err)
|
||||||
@ -302,11 +304,11 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, cu
|
|||||||
tableNames := trans.GetTableNames(db)
|
tableNames := trans.GetTableNames(db)
|
||||||
tables := getTableObjects(tableNames, db, trans)
|
tables := getTableObjects(tableNames, db, trans)
|
||||||
mvcPath := new(MvcPath)
|
mvcPath := new(MvcPath)
|
||||||
mvcPath.ModelPath = path.Join(currpath, "models")
|
mvcPath.ModelPath = path.Join(apppath, "models")
|
||||||
mvcPath.ControllerPath = path.Join(currpath, "controllers")
|
mvcPath.ControllerPath = path.Join(apppath, "controllers")
|
||||||
mvcPath.RouterPath = path.Join(currpath, "routers")
|
mvcPath.RouterPath = path.Join(apppath, "routers")
|
||||||
createPaths(mode, mvcPath)
|
createPaths(mode, mvcPath)
|
||||||
pkgPath := getPackagePath(currpath)
|
pkgPath := getPackagePath(apppath)
|
||||||
writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
|
writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms)
|
ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms)
|
||||||
@ -497,16 +499,15 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getGoDataType maps an SQL data type to Golang data type
|
// GetGoDataType maps an SQL data type to Golang data type
|
||||||
func (*MysqlDB) GetGoDataType(sqlType string) (goType string) {
|
func (*MysqlDB) GetGoDataType(sqlType string) (goType string) {
|
||||||
var typeMapping = map[string]string{}
|
var typeMapping = map[string]string{}
|
||||||
typeMapping = typeMappingMysql
|
typeMapping = typeMappingMysql
|
||||||
if v, ok := typeMapping[sqlType]; ok {
|
if v, ok := typeMapping[sqlType]; ok {
|
||||||
return v
|
return v
|
||||||
} else {
|
}
|
||||||
ColorLog("[ERRO] data type (%s) not found!\n", sqlType)
|
ColorLog("[ERRO] data type (%s) not found!\n", sqlType)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
|
||||||
return goType
|
return goType
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,7 +515,9 @@ func (*MysqlDB) GetGoDataType(sqlType string) (goType string) {
|
|||||||
func (*PostgresDB) GetTableNames(db *sql.DB) (tables []string) {
|
func (*PostgresDB) GetTableNames(db *sql.DB) (tables []string) {
|
||||||
rows, err := db.Query(`
|
rows, err := db.Query(`
|
||||||
SELECT table_name FROM information_schema.tables
|
SELECT table_name FROM information_schema.tables
|
||||||
WHERE table_catalog = current_database() and table_schema = 'public'`)
|
WHERE table_catalog = current_database() AND
|
||||||
|
table_type = 'BASE TABLE' AND
|
||||||
|
table_schema NOT IN ('pg_catalog', 'information_schema')`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ColorLog("[ERRO] Could not show tables: %s\n", err)
|
ColorLog("[ERRO] Could not show tables: %s\n", err)
|
||||||
ColorLog("[HINT] Check your connection string\n")
|
ColorLog("[HINT] Check your connection string\n")
|
||||||
@ -549,8 +552,10 @@ func (*PostgresDB) GetConstraints(db *sql.DB, table *Table, blackList map[string
|
|||||||
INNER JOIN
|
INNER JOIN
|
||||||
information_schema.constraint_column_usage cu ON cu.constraint_name = c.constraint_name
|
information_schema.constraint_column_usage cu ON cu.constraint_name = c.constraint_name
|
||||||
WHERE
|
WHERE
|
||||||
c.table_catalog = current_database() AND c.table_schema = 'public' AND c.table_name = $1
|
c.table_catalog = current_database() AND c.table_schema NOT IN ('pg_catalog', 'information_schema')
|
||||||
AND u.table_catalog = current_database() AND u.table_schema = 'public' AND u.table_name = $2`,
|
AND c.table_name = $1
|
||||||
|
AND u.table_catalog = current_database() AND u.table_schema NOT IN ('pg_catalog', 'information_schema')
|
||||||
|
AND u.table_name = $2`,
|
||||||
table.Name, table.Name) // u.position_in_unique_constraint,
|
table.Name, table.Name) // u.position_in_unique_constraint,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s\n", err)
|
ColorLog("[ERRO] Could not query INFORMATION_SCHEMA for PK/UK/FK information: %s\n", err)
|
||||||
@ -606,7 +611,8 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map
|
|||||||
FROM
|
FROM
|
||||||
information_schema.columns
|
information_schema.columns
|
||||||
WHERE
|
WHERE
|
||||||
table_catalog = current_database() AND table_schema = 'public' AND table_name = $1`,
|
table_catalog = current_database() AND table_schema NOT IN ('pg_catalog', 'information_schema')
|
||||||
|
AND table_name = $1`,
|
||||||
table.Name)
|
table.Name)
|
||||||
defer colDefRows.Close()
|
defer colDefRows.Close()
|
||||||
for colDefRows.Next() {
|
for colDefRows.Next() {
|
||||||
@ -682,25 +688,26 @@ func (postgresDB *PostgresDB) GetColumns(db *sql.DB, table *Table, blackList map
|
|||||||
table.Columns = append(table.Columns, col)
|
table.Columns = append(table.Columns, col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetGoDataType returns the Go type from the mapped Postgres type
|
||||||
func (*PostgresDB) GetGoDataType(sqlType string) (goType string) {
|
func (*PostgresDB) GetGoDataType(sqlType string) (goType string) {
|
||||||
if v, ok := typeMappingPostgres[sqlType]; ok {
|
if v, ok := typeMappingPostgres[sqlType]; ok {
|
||||||
return v
|
return v
|
||||||
} else {
|
}
|
||||||
ColorLog("[ERRO] data type (%s) not found!\n", sqlType)
|
ColorLog("[ERRO] data type (%s) not found!\n", sqlType)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
|
||||||
return goType
|
return goType
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteAndRecreatePaths removes several directories completely
|
// deleteAndRecreatePaths removes several directories completely
|
||||||
func createPaths(mode byte, paths *MvcPath) {
|
func createPaths(mode byte, paths *MvcPath) {
|
||||||
if (mode & O_MODEL) == O_MODEL {
|
if (mode & OModel) == OModel {
|
||||||
os.Mkdir(paths.ModelPath, 0777)
|
os.Mkdir(paths.ModelPath, 0777)
|
||||||
}
|
}
|
||||||
if (mode & O_CONTROLLER) == O_CONTROLLER {
|
if (mode & OController) == OController {
|
||||||
os.Mkdir(paths.ControllerPath, 0777)
|
os.Mkdir(paths.ControllerPath, 0777)
|
||||||
}
|
}
|
||||||
if (mode & O_ROUTER) == O_ROUTER {
|
if (mode & ORouter) == ORouter {
|
||||||
os.Mkdir(paths.RouterPath, 0777)
|
os.Mkdir(paths.RouterPath, 0777)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -709,15 +716,15 @@ func createPaths(mode byte, paths *MvcPath) {
|
|||||||
// It will wipe the following directories and recreate them:./models, ./controllers, ./routers
|
// It will wipe the following directories and recreate them:./models, ./controllers, ./routers
|
||||||
// Newly geneated files will be inside these folders.
|
// Newly geneated files will be inside these folders.
|
||||||
func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
|
func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
|
||||||
if (O_MODEL & mode) == O_MODEL {
|
if (OModel & mode) == OModel {
|
||||||
ColorLog("[INFO] Creating model files...\n")
|
ColorLog("[INFO] Creating model files...\n")
|
||||||
writeModelFiles(tables, paths.ModelPath, selectedTables)
|
writeModelFiles(tables, paths.ModelPath, selectedTables)
|
||||||
}
|
}
|
||||||
if (O_CONTROLLER & mode) == O_CONTROLLER {
|
if (OController & mode) == OController {
|
||||||
ColorLog("[INFO] Creating controller files...\n")
|
ColorLog("[INFO] Creating controller files...\n")
|
||||||
writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath)
|
writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath)
|
||||||
}
|
}
|
||||||
if (O_ROUTER & mode) == O_ROUTER {
|
if (ORouter & mode) == ORouter {
|
||||||
ColorLog("[INFO] Creating router files...\n")
|
ColorLog("[INFO] Creating router files...\n")
|
||||||
writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath)
|
writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath)
|
||||||
}
|
}
|
||||||
@ -725,6 +732,8 @@ func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath
|
|||||||
|
|
||||||
// writeModelFiles generates model files
|
// writeModelFiles generates model files
|
||||||
func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
|
func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
for _, tb := range tables {
|
for _, tb := range tables {
|
||||||
// if selectedTables map is not nil and this table is not selected, ignore it
|
// if selectedTables map is not nil and this table is not selected, ignore it
|
||||||
if selectedTables != nil {
|
if selectedTables != nil {
|
||||||
@ -737,7 +746,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
|
|||||||
var f *os.File
|
var f *os.File
|
||||||
var err error
|
var err error
|
||||||
if isExist(fpath) {
|
if isExist(fpath) {
|
||||||
ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath)
|
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -745,7 +754,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[WARN] skip create file\n")
|
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -757,12 +766,13 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
|
|||||||
}
|
}
|
||||||
template := ""
|
template := ""
|
||||||
if tb.Pk == "" {
|
if tb.Pk == "" {
|
||||||
template = STRUCT_MODEL_TPL
|
template = StructModelTPL
|
||||||
} else {
|
} else {
|
||||||
template = MODEL_TPL
|
template = ModelTPL
|
||||||
}
|
}
|
||||||
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
|
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
|
||||||
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1)
|
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1)
|
||||||
|
fileStr = strings.Replace(fileStr, "{{tableName}}", tb.Name, -1)
|
||||||
// if table contains time field, import time.Time package
|
// if table contains time field, import time.Time package
|
||||||
timePkg := ""
|
timePkg := ""
|
||||||
importTimePkg := ""
|
importTimePkg := ""
|
||||||
@ -776,14 +786,16 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
|
|||||||
ColorLog("[ERRO] Could not write model file to %s\n", fpath)
|
ColorLog("[ERRO] Could not write model file to %s\n", fpath)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
f.Close()
|
CloseFile(f)
|
||||||
ColorLog("[INFO] model => %s\n", fpath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||||
formatSourceCode(fpath)
|
formatSourceCode(fpath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeControllerFiles generates controller files
|
// writeControllerFiles generates controller files
|
||||||
func writeControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath string) {
|
func writeControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath string) {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
for _, tb := range tables {
|
for _, tb := range tables {
|
||||||
// if selectedTables map is not nil and this table is not selected, ignore it
|
// if selectedTables map is not nil and this table is not selected, ignore it
|
||||||
if selectedTables != nil {
|
if selectedTables != nil {
|
||||||
@ -799,7 +811,7 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
|
|||||||
var f *os.File
|
var f *os.File
|
||||||
var err error
|
var err error
|
||||||
if isExist(fpath) {
|
if isExist(fpath) {
|
||||||
ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath)
|
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -807,7 +819,7 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[WARN] skip create file\n")
|
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -817,20 +829,22 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileStr := strings.Replace(CTRL_TPL, "{{ctrlName}}", camelCase(tb.Name), -1)
|
fileStr := strings.Replace(CtrlTPL, "{{ctrlName}}", camelCase(tb.Name), -1)
|
||||||
fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1)
|
fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1)
|
||||||
if _, err := f.WriteString(fileStr); err != nil {
|
if _, err := f.WriteString(fileStr); err != nil {
|
||||||
ColorLog("[ERRO] Could not write controller file to %s\n", fpath)
|
ColorLog("[ERRO] Could not write controller file to %s\n", fpath)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
f.Close()
|
CloseFile(f)
|
||||||
ColorLog("[INFO] controller => %s\n", fpath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||||
formatSourceCode(fpath)
|
formatSourceCode(fpath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeRouterFile generates router file
|
// writeRouterFile generates router file
|
||||||
func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bool, pkgPath string) {
|
func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bool, pkgPath string) {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
var nameSpaces []string
|
var nameSpaces []string
|
||||||
for _, tb := range tables {
|
for _, tb := range tables {
|
||||||
// if selectedTables map is not nil and this table is not selected, ignore it
|
// if selectedTables map is not nil and this table is not selected, ignore it
|
||||||
@ -843,18 +857,18 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// add namespaces
|
// add namespaces
|
||||||
nameSpace := strings.Replace(NAMESPACE_TPL, "{{nameSpace}}", tb.Name, -1)
|
nameSpace := strings.Replace(NamespaceTPL, "{{nameSpace}}", tb.Name, -1)
|
||||||
nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1)
|
nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1)
|
||||||
nameSpaces = append(nameSpaces, nameSpace)
|
nameSpaces = append(nameSpaces, nameSpace)
|
||||||
}
|
}
|
||||||
// add export controller
|
// add export controller
|
||||||
fpath := path.Join(rPath, "router.go")
|
fpath := path.Join(rPath, "router.go")
|
||||||
routerStr := strings.Replace(ROUTER_TPL, "{{nameSpaces}}", strings.Join(nameSpaces, ""), 1)
|
routerStr := strings.Replace(RouterTPL, "{{nameSpaces}}", strings.Join(nameSpaces, ""), 1)
|
||||||
routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1)
|
routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1)
|
||||||
var f *os.File
|
var f *os.File
|
||||||
var err error
|
var err error
|
||||||
if isExist(fpath) {
|
if isExist(fpath) {
|
||||||
ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath)
|
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -862,7 +876,7 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[WARN] skip create file\n")
|
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -873,11 +887,11 @@ func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := f.WriteString(routerStr); err != nil {
|
if _, err := f.WriteString(routerStr); err != nil {
|
||||||
ColorLog("[ERRO] Could not write router file to %s\n", fpath)
|
ColorLog("[ERRO] Could not write router file to '%s'\n", fpath)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
f.Close()
|
CloseFile(f)
|
||||||
ColorLog("[INFO] router => %s\n", fpath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||||
formatSourceCode(fpath)
|
formatSourceCode(fpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -960,7 +974,7 @@ func getPackagePath(curpath string) (packpath string) {
|
|||||||
gopath := os.Getenv("GOPATH")
|
gopath := os.Getenv("GOPATH")
|
||||||
Debugf("gopath:%s", gopath)
|
Debugf("gopath:%s", gopath)
|
||||||
if gopath == "" {
|
if gopath == "" {
|
||||||
ColorLog("[ERRO] you should set GOPATH in the env")
|
ColorLog("[ERRO] You should set GOPATH in the env")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -982,17 +996,23 @@ func getPackagePath(curpath string) (packpath string) {
|
|||||||
ColorLog("[ERRO] Can't generate application code outside of GOPATH '%s'\n", gopath)
|
ColorLog("[ERRO] Can't generate application code outside of GOPATH '%s'\n", gopath)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if curpath == appsrcpath {
|
||||||
|
ColorLog("[ERRO] Can't generate application code outside of application PATH \n")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/")
|
packpath = strings.Join(strings.Split(curpath[len(appsrcpath)+1:], string(filepath.Separator)), "/")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
STRUCT_MODEL_TPL = `package models
|
StructModelTPL = `package models
|
||||||
{{importTimePkg}}
|
{{importTimePkg}}
|
||||||
{{modelStruct}}
|
{{modelStruct}}
|
||||||
`
|
`
|
||||||
|
|
||||||
MODEL_TPL = `package models
|
ModelTPL = `package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -1005,6 +1025,10 @@ import (
|
|||||||
|
|
||||||
{{modelStruct}}
|
{{modelStruct}}
|
||||||
|
|
||||||
|
func (t *{{modelName}}) TableName() string {
|
||||||
|
return "{{tableName}}"
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
orm.RegisterModel(new({{modelName}}))
|
orm.RegisterModel(new({{modelName}}))
|
||||||
}
|
}
|
||||||
@ -1081,7 +1105,7 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri
|
|||||||
|
|
||||||
var l []{{modelName}}
|
var l []{{modelName}}
|
||||||
qs = qs.OrderBy(sortFields...)
|
qs = qs.OrderBy(sortFields...)
|
||||||
if _, err := qs.Limit(limit, offset).All(&l, fields...); err == nil {
|
if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
for _, v := range l {
|
for _, v := range l {
|
||||||
ml = append(ml, v)
|
ml = append(ml, v)
|
||||||
@ -1132,7 +1156,7 @@ func Delete{{modelName}}(id int) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
CTRL_TPL = `package controllers
|
CtrlTPL = `package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"{{pkgPath}}/models"
|
"{{pkgPath}}/models"
|
||||||
@ -1144,54 +1168,62 @@ import (
|
|||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
)
|
)
|
||||||
|
|
||||||
// oprations for {{ctrlName}}
|
// {{ctrlName}}Controller oprations for {{ctrlName}}
|
||||||
type {{ctrlName}}Controller struct {
|
type {{ctrlName}}Controller struct {
|
||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *{{ctrlName}}Controller) URLMapping() {
|
// URLMapping ...
|
||||||
this.Mapping("Post", this.Post)
|
func (c *{{ctrlName}}Controller) URLMapping() {
|
||||||
this.Mapping("GetOne", this.GetOne)
|
c.Mapping("Post", c.Post)
|
||||||
this.Mapping("GetAll", this.GetAll)
|
c.Mapping("GetOne", c.GetOne)
|
||||||
this.Mapping("Put", this.Put)
|
c.Mapping("GetAll", c.GetAll)
|
||||||
this.Mapping("Delete", this.Delete)
|
c.Mapping("Put", c.Put)
|
||||||
|
c.Mapping("Delete", c.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post ...
|
||||||
// @Title Post
|
// @Title Post
|
||||||
// @Description create {{ctrlName}}
|
// @Description create {{ctrlName}}
|
||||||
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
|
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
|
||||||
// @Success 200 {int} models.{{ctrlName}}.Id
|
// @Success 201 {int} models.{{ctrlName}}
|
||||||
// @Failure 403 body is empty
|
// @Failure 403 body is empty
|
||||||
// @router / [post]
|
// @router / [post]
|
||||||
func (this *{{ctrlName}}Controller) Post() {
|
func (c *{{ctrlName}}Controller) Post() {
|
||||||
var v models.{{ctrlName}}
|
var v models.{{ctrlName}}
|
||||||
json.Unmarshal(this.Ctx.Input.RequestBody, &v)
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &v); err == nil {
|
||||||
if id, err := models.Add{{ctrlName}}(&v); err == nil {
|
if _, err := models.Add{{ctrlName}}(&v); err == nil {
|
||||||
this.Data["json"] = map[string]int64{"id": id}
|
c.Ctx.Output.SetStatus(201)
|
||||||
|
c.Data["json"] = v
|
||||||
} else {
|
} else {
|
||||||
this.Data["json"] = err.Error()
|
c.Data["json"] = err.Error()
|
||||||
}
|
}
|
||||||
this.ServeJson()
|
} else {
|
||||||
|
c.Data["json"] = err.Error()
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Get
|
// GetOne ...
|
||||||
|
// @Title Get One
|
||||||
// @Description get {{ctrlName}} by id
|
// @Description get {{ctrlName}} by id
|
||||||
// @Param id path string true "The key for staticblock"
|
// @Param id path string true "The key for staticblock"
|
||||||
// @Success 200 {object} models.{{ctrlName}}
|
// @Success 200 {object} models.{{ctrlName}}
|
||||||
// @Failure 403 :id is empty
|
// @Failure 403 :id is empty
|
||||||
// @router /:id [get]
|
// @router /:id [get]
|
||||||
func (this *{{ctrlName}}Controller) GetOne() {
|
func (c *{{ctrlName}}Controller) GetOne() {
|
||||||
idStr := this.Ctx.Input.Params[":id"]
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
id, _ := strconv.Atoi(idStr)
|
id, _ := strconv.Atoi(idStr)
|
||||||
v, err := models.Get{{ctrlName}}ById(id)
|
v, err := models.Get{{ctrlName}}ById(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Data["json"] = err.Error()
|
c.Data["json"] = err.Error()
|
||||||
} else {
|
} else {
|
||||||
this.Data["json"] = v
|
c.Data["json"] = v
|
||||||
}
|
}
|
||||||
this.ServeJson()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAll ...
|
||||||
// @Title Get All
|
// @Title Get All
|
||||||
// @Description get {{ctrlName}}
|
// @Description get {{ctrlName}}
|
||||||
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
||||||
@ -1203,41 +1235,41 @@ func (this *{{ctrlName}}Controller) GetOne() {
|
|||||||
// @Success 200 {object} models.{{ctrlName}}
|
// @Success 200 {object} models.{{ctrlName}}
|
||||||
// @Failure 403
|
// @Failure 403
|
||||||
// @router / [get]
|
// @router / [get]
|
||||||
func (this *{{ctrlName}}Controller) GetAll() {
|
func (c *{{ctrlName}}Controller) GetAll() {
|
||||||
var fields []string
|
var fields []string
|
||||||
var sortby []string
|
var sortby []string
|
||||||
var order []string
|
var order []string
|
||||||
var query map[string]string = make(map[string]string)
|
var query = make(map[string]string)
|
||||||
var limit int64 = 10
|
var limit int64 = 10
|
||||||
var offset int64 = 0
|
var offset int64
|
||||||
|
|
||||||
// fields: col1,col2,entity.col3
|
// fields: col1,col2,entity.col3
|
||||||
if v := this.GetString("fields"); v != "" {
|
if v := c.GetString("fields"); v != "" {
|
||||||
fields = strings.Split(v, ",")
|
fields = strings.Split(v, ",")
|
||||||
}
|
}
|
||||||
// limit: 10 (default is 10)
|
// limit: 10 (default is 10)
|
||||||
if v, err := this.GetInt("limit"); err == nil {
|
if v, err := c.GetInt64("limit"); err == nil {
|
||||||
limit = v
|
limit = v
|
||||||
}
|
}
|
||||||
// offset: 0 (default is 0)
|
// offset: 0 (default is 0)
|
||||||
if v, err := this.GetInt("offset"); err == nil {
|
if v, err := c.GetInt64("offset"); err == nil {
|
||||||
offset = v
|
offset = v
|
||||||
}
|
}
|
||||||
// sortby: col1,col2
|
// sortby: col1,col2
|
||||||
if v := this.GetString("sortby"); v != "" {
|
if v := c.GetString("sortby"); v != "" {
|
||||||
sortby = strings.Split(v, ",")
|
sortby = strings.Split(v, ",")
|
||||||
}
|
}
|
||||||
// order: desc,asc
|
// order: desc,asc
|
||||||
if v := this.GetString("order"); v != "" {
|
if v := c.GetString("order"); v != "" {
|
||||||
order = strings.Split(v, ",")
|
order = strings.Split(v, ",")
|
||||||
}
|
}
|
||||||
// query: k:v,k:v
|
// query: k:v,k:v
|
||||||
if v := this.GetString("query"); v != "" {
|
if v := c.GetString("query"); v != "" {
|
||||||
for _, cond := range strings.Split(v, ",") {
|
for _, cond := range strings.Split(v, ",") {
|
||||||
kv := strings.Split(cond, ":")
|
kv := strings.SplitN(cond, ":", 2)
|
||||||
if len(kv) != 2 {
|
if len(kv) != 2 {
|
||||||
this.Data["json"] = errors.New("Error: invalid query key/value pair")
|
c.Data["json"] = errors.New("Error: invalid query key/value pair")
|
||||||
this.ServeJson()
|
c.ServeJSON()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
k, v := kv[0], kv[1]
|
k, v := kv[0], kv[1]
|
||||||
@ -1247,51 +1279,56 @@ func (this *{{ctrlName}}Controller) GetAll() {
|
|||||||
|
|
||||||
l, err := models.GetAll{{ctrlName}}(query, fields, sortby, order, offset, limit)
|
l, err := models.GetAll{{ctrlName}}(query, fields, sortby, order, offset, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.Data["json"] = err.Error()
|
c.Data["json"] = err.Error()
|
||||||
} else {
|
} else {
|
||||||
this.Data["json"] = l
|
c.Data["json"] = l
|
||||||
}
|
}
|
||||||
this.ServeJson()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Update
|
// Put ...
|
||||||
|
// @Title Put
|
||||||
// @Description update the {{ctrlName}}
|
// @Description update the {{ctrlName}}
|
||||||
// @Param id path string true "The id you want to update"
|
// @Param id path string true "The id you want to update"
|
||||||
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
|
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
|
||||||
// @Success 200 {object} models.{{ctrlName}}
|
// @Success 200 {object} models.{{ctrlName}}
|
||||||
// @Failure 403 :id is not int
|
// @Failure 403 :id is not int
|
||||||
// @router /:id [put]
|
// @router /:id [put]
|
||||||
func (this *{{ctrlName}}Controller) Put() {
|
func (c *{{ctrlName}}Controller) Put() {
|
||||||
idStr := this.Ctx.Input.Params[":id"]
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
id, _ := strconv.Atoi(idStr)
|
id, _ := strconv.Atoi(idStr)
|
||||||
v := models.{{ctrlName}}{Id: id}
|
v := models.{{ctrlName}}{Id: id}
|
||||||
json.Unmarshal(this.Ctx.Input.RequestBody, &v)
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &v); err == nil {
|
||||||
if err := models.Update{{ctrlName}}ById(&v); err == nil {
|
if err := models.Update{{ctrlName}}ById(&v); err == nil {
|
||||||
this.Data["json"] = "OK"
|
c.Data["json"] = "OK"
|
||||||
} else {
|
} else {
|
||||||
this.Data["json"] = err.Error()
|
c.Data["json"] = err.Error()
|
||||||
}
|
}
|
||||||
this.ServeJson()
|
} else {
|
||||||
|
c.Data["json"] = err.Error()
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete ...
|
||||||
// @Title Delete
|
// @Title Delete
|
||||||
// @Description delete the {{ctrlName}}
|
// @Description delete the {{ctrlName}}
|
||||||
// @Param id path string true "The id you want to delete"
|
// @Param id path string true "The id you want to delete"
|
||||||
// @Success 200 {string} delete success!
|
// @Success 200 {string} delete success!
|
||||||
// @Failure 403 id is empty
|
// @Failure 403 id is empty
|
||||||
// @router /:id [delete]
|
// @router /:id [delete]
|
||||||
func (this *{{ctrlName}}Controller) Delete() {
|
func (c *{{ctrlName}}Controller) Delete() {
|
||||||
idStr := this.Ctx.Input.Params[":id"]
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
id, _ := strconv.Atoi(idStr)
|
id, _ := strconv.Atoi(idStr)
|
||||||
if err := models.Delete{{ctrlName}}(id); err == nil {
|
if err := models.Delete{{ctrlName}}(id); err == nil {
|
||||||
this.Data["json"] = "OK"
|
c.Data["json"] = "OK"
|
||||||
} else {
|
} else {
|
||||||
this.Data["json"] = err.Error()
|
c.Data["json"] = err.Error()
|
||||||
}
|
}
|
||||||
this.ServeJson()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
ROUTER_TPL = `// @APIVersion 1.0.0
|
RouterTPL = `// @APIVersion 1.0.0
|
||||||
// @Title beego Test API
|
// @Title beego Test API
|
||||||
// @Description beego has a very cool tools to autogenerate documents for your API
|
// @Description beego has a very cool tools to autogenerate documents for your API
|
||||||
// @Contact astaxie@gmail.com
|
// @Contact astaxie@gmail.com
|
||||||
@ -1313,7 +1350,7 @@ func init() {
|
|||||||
beego.AddNamespace(ns)
|
beego.AddNamespace(ns)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
NAMESPACE_TPL = `
|
NamespaceTPL = `
|
||||||
beego.NSNamespace("/{{nameSpace}}",
|
beego.NSNamespace("/{{nameSpace}}",
|
||||||
beego.NSInclude(
|
beego.NSInclude(
|
||||||
&controllers.{{ctrlName}}Controller{},
|
&controllers.{{ctrlName}}Controller{},
|
||||||
|
240
g_controllers.go
240
g_controllers.go
@ -15,6 +15,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
@ -23,35 +24,53 @@ import (
|
|||||||
// article
|
// article
|
||||||
// cms/article
|
// cms/article
|
||||||
//
|
//
|
||||||
func generateController(cname, crupath string) {
|
func generateController(cname, currpath string) {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
p, f := path.Split(cname)
|
p, f := path.Split(cname)
|
||||||
controllerName := strings.Title(f)
|
controllerName := strings.Title(f)
|
||||||
packageName := "controllers"
|
packageName := "controllers"
|
||||||
|
|
||||||
if p != "" {
|
if p != "" {
|
||||||
i := strings.LastIndex(p[:len(p)-1], "/")
|
i := strings.LastIndex(p[:len(p)-1], "/")
|
||||||
packageName = p[i+1 : len(p)-1]
|
packageName = p[i+1 : len(p)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorLog("[INFO] Using '%s' as controller name\n", controllerName)
|
ColorLog("[INFO] Using '%s' as controller name\n", controllerName)
|
||||||
ColorLog("[INFO] Using '%s' as package name\n", packageName)
|
ColorLog("[INFO] Using '%s' as package name\n", packageName)
|
||||||
fp := path.Join(crupath, "controllers", p)
|
|
||||||
|
fp := path.Join(currpath, "controllers", p)
|
||||||
if _, err := os.Stat(fp); os.IsNotExist(err) {
|
if _, err := os.Stat(fp); os.IsNotExist(err) {
|
||||||
// create controller directory
|
// Create the controller's directory
|
||||||
if err := os.MkdirAll(fp, 0777); err != nil {
|
if err := os.MkdirAll(fp, 0777); err != nil {
|
||||||
ColorLog("[ERRO] Could not create controllers directory: %s\n", err)
|
ColorLog("[ERRO] Could not create controllers directory: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fpath := path.Join(fp, strings.ToLower(controllerName)+".go")
|
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 {
|
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
||||||
defer f.Close()
|
defer CloseFile(f)
|
||||||
content := strings.Replace(controllerTpl, "{{packageName}}", packageName, -1)
|
|
||||||
|
modelPath := path.Join(currpath, "models", strings.ToLower(controllerName)+".go")
|
||||||
|
|
||||||
|
var content string
|
||||||
|
if _, err := os.Stat(modelPath); err == nil {
|
||||||
|
ColorLog("[INFO] Using matching model '%s'\n", controllerName)
|
||||||
|
content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1)
|
||||||
|
pkgPath := getPackagePath(currpath)
|
||||||
|
content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1)
|
||||||
|
} else {
|
||||||
|
content = strings.Replace(controllerTpl, "{{packageName}}", packageName, -1)
|
||||||
|
}
|
||||||
|
|
||||||
content = strings.Replace(content, "{{controllerName}}", controllerName, -1)
|
content = strings.Replace(content, "{{controllerName}}", controllerName, -1)
|
||||||
f.WriteString(content)
|
f.WriteString(content)
|
||||||
// gofmt generated source code
|
|
||||||
|
// Run 'gofmt' on the generated source code
|
||||||
formatSourceCode(fpath)
|
formatSourceCode(fpath)
|
||||||
ColorLog("[INFO] controller file generated: %s\n", fpath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||||
} else {
|
} else {
|
||||||
// error creating file
|
|
||||||
ColorLog("[ERRO] Could not create controller file: %s\n", err)
|
ColorLog("[ERRO] Could not create controller file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
@ -63,39 +82,43 @@ import (
|
|||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
)
|
)
|
||||||
|
|
||||||
// oprations for {{controllerName}}
|
// {{controllerName}}Controller operations for {{controllerName}}
|
||||||
type {{controllerName}}Controller struct {
|
type {{controllerName}}Controller struct {
|
||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *{{controllerName}}Controller) URLMapping() {
|
// URLMapping ...
|
||||||
this.Mapping("Post", this.Post)
|
func (c *{{controllerName}}Controller) URLMapping() {
|
||||||
this.Mapping("GetOne", this.GetOne)
|
c.Mapping("Post", c.Post)
|
||||||
this.Mapping("GetAll", this.GetAll)
|
c.Mapping("GetOne", c.GetOne)
|
||||||
this.Mapping("Put", this.Put)
|
c.Mapping("GetAll", c.GetAll)
|
||||||
this.Mapping("Delete", this.Delete)
|
c.Mapping("Put", c.Put)
|
||||||
|
c.Mapping("Delete", c.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Post
|
// Post ...
|
||||||
|
// @Title Create
|
||||||
// @Description create {{controllerName}}
|
// @Description create {{controllerName}}
|
||||||
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||||
// @Success 200 {int} models.{{controllerName}}.Id
|
// @Success 201 {object} models.{{controllerName}}
|
||||||
// @Failure 403 body is empty
|
// @Failure 403 body is empty
|
||||||
// @router / [post]
|
// @router / [post]
|
||||||
func (this *{{controllerName}}Controller) Post() {
|
func (c *{{controllerName}}Controller) Post() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Get
|
// GetOne ...
|
||||||
|
// @Title GetOne
|
||||||
// @Description get {{controllerName}} by id
|
// @Description get {{controllerName}} by id
|
||||||
// @Param id path string true "The key for staticblock"
|
// @Param id path string true "The key for staticblock"
|
||||||
// @Success 200 {object} models.{{controllerName}}
|
// @Success 200 {object} models.{{controllerName}}
|
||||||
// @Failure 403 :id is empty
|
// @Failure 403 :id is empty
|
||||||
// @router /:id [get]
|
// @router /:id [get]
|
||||||
func (this *{{controllerName}}Controller) GetOne() {
|
func (c *{{controllerName}}Controller) GetOne() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAll ...
|
||||||
// @Title GetAll
|
// @Title GetAll
|
||||||
// @Description get {{controllerName}}
|
// @Description get {{controllerName}}
|
||||||
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
||||||
@ -107,28 +130,197 @@ func (this *{{controllerName}}Controller) GetOne() {
|
|||||||
// @Success 200 {object} models.{{controllerName}}
|
// @Success 200 {object} models.{{controllerName}}
|
||||||
// @Failure 403
|
// @Failure 403
|
||||||
// @router / [get]
|
// @router / [get]
|
||||||
func (this *{{controllerName}}Controller) GetAll() {
|
func (c *{{controllerName}}Controller) GetAll() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title Update
|
// Put ...
|
||||||
|
// @Title Put
|
||||||
// @Description update the {{controllerName}}
|
// @Description update the {{controllerName}}
|
||||||
// @Param id path string true "The id you want to update"
|
// @Param id path string true "The id you want to update"
|
||||||
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||||
// @Success 200 {object} models.{{controllerName}}
|
// @Success 200 {object} models.{{controllerName}}
|
||||||
// @Failure 403 :id is not int
|
// @Failure 403 :id is not int
|
||||||
// @router /:id [put]
|
// @router /:id [put]
|
||||||
func (this *{{controllerName}}Controller) Put() {
|
func (c *{{controllerName}}Controller) Put() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete ...
|
||||||
// @Title Delete
|
// @Title Delete
|
||||||
// @Description delete the {{controllerName}}
|
// @Description delete the {{controllerName}}
|
||||||
// @Param id path string true "The id you want to delete"
|
// @Param id path string true "The id you want to delete"
|
||||||
// @Success 200 {string} delete success!
|
// @Success 200 {string} delete success!
|
||||||
// @Failure 403 id is empty
|
// @Failure 403 id is empty
|
||||||
// @router /:id [delete]
|
// @router /:id [delete]
|
||||||
func (this *{{controllerName}}Controller) Delete() {
|
func (c *{{controllerName}}Controller) Delete() {
|
||||||
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var controllerModelTpl = `package {{packageName}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
"{{pkgPath}}/models"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
)
|
||||||
|
|
||||||
|
// {{controllerName}}Controller oprations for {{controllerName}}
|
||||||
|
type {{controllerName}}Controller struct {
|
||||||
|
beego.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLMapping ...
|
||||||
|
func (c *{{controllerName}}Controller) URLMapping() {
|
||||||
|
c.Mapping("Post", c.Post)
|
||||||
|
c.Mapping("GetOne", c.GetOne)
|
||||||
|
c.Mapping("GetAll", c.GetAll)
|
||||||
|
c.Mapping("Put", c.Put)
|
||||||
|
c.Mapping("Delete", c.Delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post ...
|
||||||
|
// @Title Post
|
||||||
|
// @Description create {{controllerName}}
|
||||||
|
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||||
|
// @Success 201 {int} models.{{controllerName}}
|
||||||
|
// @Failure 403 body is empty
|
||||||
|
// @router / [post]
|
||||||
|
func (c *{{controllerName}}Controller) Post() {
|
||||||
|
var v models.{{controllerName}}
|
||||||
|
json.Unmarshal(c.Ctx.Input.RequestBody, &v)
|
||||||
|
if _, err := models.Add{{controllerName}}(&v); err == nil {
|
||||||
|
c.Ctx.Output.SetStatus(201)
|
||||||
|
c.Data["json"] = v
|
||||||
|
} else {
|
||||||
|
c.Data["json"] = err.Error()
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOne ...
|
||||||
|
// @Title Get One
|
||||||
|
// @Description get {{controllerName}} by id
|
||||||
|
// @Param id path string true "The key for staticblock"
|
||||||
|
// @Success 200 {object} models.{{controllerName}}
|
||||||
|
// @Failure 403 :id is empty
|
||||||
|
// @router /:id [get]
|
||||||
|
func (c *{{controllerName}}Controller) GetOne() {
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseInt(idStr, 0, 64)
|
||||||
|
v, err := models.Get{{controllerName}}ById(id)
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = err.Error()
|
||||||
|
} else {
|
||||||
|
c.Data["json"] = v
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAll ...
|
||||||
|
// @Title Get All
|
||||||
|
// @Description get {{controllerName}}
|
||||||
|
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
|
||||||
|
// @Param fields query string false "Fields returned. e.g. col1,col2 ..."
|
||||||
|
// @Param sortby query string false "Sorted-by fields. e.g. col1,col2 ..."
|
||||||
|
// @Param order query string false "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..."
|
||||||
|
// @Param limit query string false "Limit the size of result set. Must be an integer"
|
||||||
|
// @Param offset query string false "Start position of result set. Must be an integer"
|
||||||
|
// @Success 200 {object} models.{{controllerName}}
|
||||||
|
// @Failure 403
|
||||||
|
// @router / [get]
|
||||||
|
func (c *{{controllerName}}Controller) GetAll() {
|
||||||
|
var fields []string
|
||||||
|
var sortby []string
|
||||||
|
var order []string
|
||||||
|
var query = make(map[string]string)
|
||||||
|
var limit int64 = 10
|
||||||
|
var offset int64
|
||||||
|
|
||||||
|
// fields: col1,col2,entity.col3
|
||||||
|
if v := c.GetString("fields"); v != "" {
|
||||||
|
fields = strings.Split(v, ",")
|
||||||
|
}
|
||||||
|
// limit: 10 (default is 10)
|
||||||
|
if v, err := c.GetInt64("limit"); err == nil {
|
||||||
|
limit = v
|
||||||
|
}
|
||||||
|
// offset: 0 (default is 0)
|
||||||
|
if v, err := c.GetInt64("offset"); err == nil {
|
||||||
|
offset = v
|
||||||
|
}
|
||||||
|
// sortby: col1,col2
|
||||||
|
if v := c.GetString("sortby"); v != "" {
|
||||||
|
sortby = strings.Split(v, ",")
|
||||||
|
}
|
||||||
|
// order: desc,asc
|
||||||
|
if v := c.GetString("order"); v != "" {
|
||||||
|
order = strings.Split(v, ",")
|
||||||
|
}
|
||||||
|
// query: k:v,k:v
|
||||||
|
if v := c.GetString("query"); v != "" {
|
||||||
|
for _, cond := range strings.Split(v, ",") {
|
||||||
|
kv := strings.SplitN(cond, ":", 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
c.Data["json"] = errors.New("Error: invalid query key/value pair")
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
k, v := kv[0], kv[1]
|
||||||
|
query[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := models.GetAll{{controllerName}}(query, fields, sortby, order, offset, limit)
|
||||||
|
if err != nil {
|
||||||
|
c.Data["json"] = err.Error()
|
||||||
|
} else {
|
||||||
|
c.Data["json"] = l
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put ...
|
||||||
|
// @Title Put
|
||||||
|
// @Description update the {{controllerName}}
|
||||||
|
// @Param id path string true "The id you want to update"
|
||||||
|
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
|
||||||
|
// @Success 200 {object} models.{{controllerName}}
|
||||||
|
// @Failure 403 :id is not int
|
||||||
|
// @router /:id [put]
|
||||||
|
func (c *{{controllerName}}Controller) Put() {
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseInt(idStr, 0, 64)
|
||||||
|
v := models.{{controllerName}}{Id: id}
|
||||||
|
json.Unmarshal(c.Ctx.Input.RequestBody, &v)
|
||||||
|
if err := models.Update{{controllerName}}ById(&v); err == nil {
|
||||||
|
c.Data["json"] = "OK"
|
||||||
|
} else {
|
||||||
|
c.Data["json"] = err.Error()
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete ...
|
||||||
|
// @Title Delete
|
||||||
|
// @Description delete the {{controllerName}}
|
||||||
|
// @Param id path string true "The id you want to delete"
|
||||||
|
// @Success 200 {string} delete success!
|
||||||
|
// @Failure 403 id is empty
|
||||||
|
// @router /:id [delete]
|
||||||
|
func (c *{{controllerName}}Controller) Delete() {
|
||||||
|
idStr := c.Ctx.Input.Param(":id")
|
||||||
|
id, _ := strconv.ParseInt(idStr, 0, 64)
|
||||||
|
if err := models.Delete{{controllerName}}(id); err == nil {
|
||||||
|
c.Data["json"] = "OK"
|
||||||
|
} else {
|
||||||
|
c.Data["json"] = err.Error()
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
`
|
||||||
|
643
g_docs.go
643
g_docs.go
@ -31,63 +31,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/astaxie/beego/swagger"
|
"github.com/astaxie/beego/swagger"
|
||||||
"github.com/astaxie/beego/utils"
|
"github.com/astaxie/beego/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalDocsTemplate = `package docs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
"github.com/astaxie/beego/swagger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rootinfo string = {{.rootinfo}}
|
|
||||||
var subapi string = {{.subapi}}
|
|
||||||
var rootapi swagger.ResourceListing
|
|
||||||
|
|
||||||
var apilist map[string]*swagger.ApiDeclaration
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
basepath := "{{.version}}"
|
|
||||||
err := json.Unmarshal([]byte(rootinfo), &rootapi)
|
|
||||||
if err != nil {
|
|
||||||
beego.Error(err)
|
|
||||||
}
|
|
||||||
err = json.Unmarshal([]byte(subapi), &apilist)
|
|
||||||
if err != nil {
|
|
||||||
beego.Error(err)
|
|
||||||
}
|
|
||||||
beego.GlobalDocApi["Root"] = rootapi
|
|
||||||
for k, v := range apilist {
|
|
||||||
for i, a := range v.Apis {
|
|
||||||
a.Path = urlReplace(k + a.Path)
|
|
||||||
v.Apis[i] = a
|
|
||||||
}
|
|
||||||
v.BasePath = basepath
|
|
||||||
beego.GlobalDocApi[strings.Trim(k, "/")] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func urlReplace(src string) string {
|
|
||||||
pt := strings.Split(src, "/")
|
|
||||||
for i, p := range pt {
|
|
||||||
if len(p) > 0 {
|
|
||||||
if p[0] == ':' {
|
|
||||||
pt[i] = "{" + p[1:] + "}"
|
|
||||||
} else if p[0] == '?' && p[1] == ':' {
|
|
||||||
pt[i] = "{" + p[2:] + "}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(pt, "/")
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ajson = "application/json"
|
ajson = "application/json"
|
||||||
axml = "application/xml"
|
axml = "application/xml"
|
||||||
@ -95,21 +44,19 @@ const (
|
|||||||
ahtml = "text/html"
|
ahtml = "text/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pkgCache map[string]bool //pkg:controller:function:comments comments: key:value
|
var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value
|
||||||
var controllerComments map[string]string
|
var controllerComments map[string]string
|
||||||
var importlist map[string]string
|
var importlist map[string]string
|
||||||
var apilist map[string]*swagger.ApiDeclaration
|
var controllerList map[string]map[string]*swagger.Item //controllername Paths items
|
||||||
var controllerList map[string][]swagger.Api
|
var modelsList map[string]map[string]swagger.Schema
|
||||||
var modelsList map[string]map[string]swagger.Model
|
var rootapi swagger.Swagger
|
||||||
var rootapi swagger.ResourceListing
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
pkgCache = make(map[string]bool)
|
pkgCache = make(map[string]struct{})
|
||||||
controllerComments = make(map[string]string)
|
controllerComments = make(map[string]string)
|
||||||
importlist = make(map[string]string)
|
importlist = make(map[string]string)
|
||||||
apilist = make(map[string]*swagger.ApiDeclaration)
|
controllerList = make(map[string]map[string]*swagger.Item)
|
||||||
controllerList = make(map[string][]swagger.Api)
|
modelsList = make(map[string]map[string]swagger.Schema)
|
||||||
modelsList = make(map[string]map[string]swagger.Model)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateDocs(curpath string) {
|
func generateDocs(curpath string) {
|
||||||
@ -122,64 +69,98 @@ func generateDocs(curpath string) {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
rootapi.Infos = swagger.Infomation{}
|
rootapi.Infos = swagger.Information{}
|
||||||
rootapi.SwaggerVersion = swagger.SwaggerVersion
|
rootapi.SwaggerVersion = "2.0"
|
||||||
//analysis API comments
|
//analysis API comments
|
||||||
if f.Comments != nil {
|
if f.Comments != nil {
|
||||||
for _, c := range f.Comments {
|
for _, c := range f.Comments {
|
||||||
for _, s := range strings.Split(c.Text(), "\n") {
|
for _, s := range strings.Split(c.Text(), "\n") {
|
||||||
if strings.HasPrefix(s, "@APIVersion") {
|
if strings.HasPrefix(s, "@APIVersion") {
|
||||||
rootapi.ApiVersion = strings.TrimSpace(s[len("@APIVersion"):])
|
rootapi.Infos.Version = strings.TrimSpace(s[len("@APIVersion"):])
|
||||||
} else if strings.HasPrefix(s, "@Title") {
|
} else if strings.HasPrefix(s, "@Title") {
|
||||||
rootapi.Infos.Title = strings.TrimSpace(s[len("@Title"):])
|
rootapi.Infos.Title = strings.TrimSpace(s[len("@Title"):])
|
||||||
} else if strings.HasPrefix(s, "@Description") {
|
} else if strings.HasPrefix(s, "@Description") {
|
||||||
rootapi.Infos.Description = strings.TrimSpace(s[len("@Description"):])
|
rootapi.Infos.Description = strings.TrimSpace(s[len("@Description"):])
|
||||||
} else if strings.HasPrefix(s, "@TermsOfServiceUrl") {
|
} else if strings.HasPrefix(s, "@TermsOfServiceUrl") {
|
||||||
rootapi.Infos.TermsOfServiceUrl = strings.TrimSpace(s[len("@TermsOfServiceUrl"):])
|
rootapi.Infos.TermsOfService = strings.TrimSpace(s[len("@TermsOfServiceUrl"):])
|
||||||
} else if strings.HasPrefix(s, "@Contact") {
|
} else if strings.HasPrefix(s, "@Contact") {
|
||||||
rootapi.Infos.Contact = strings.TrimSpace(s[len("@Contact"):])
|
rootapi.Infos.Contact.EMail = strings.TrimSpace(s[len("@Contact"):])
|
||||||
|
} else if strings.HasPrefix(s, "@Name") {
|
||||||
|
rootapi.Infos.Contact.Name = strings.TrimSpace(s[len("@Name"):])
|
||||||
|
} else if strings.HasPrefix(s, "@URL") {
|
||||||
|
rootapi.Infos.Contact.URL = strings.TrimSpace(s[len("@URL"):])
|
||||||
} else if strings.HasPrefix(s, "@License") {
|
} else if strings.HasPrefix(s, "@License") {
|
||||||
rootapi.Infos.License = strings.TrimSpace(s[len("@License"):])
|
if rootapi.Infos.License == nil {
|
||||||
|
rootapi.Infos.License = &swagger.License{Name: strings.TrimSpace(s[len("@License"):])}
|
||||||
|
} else {
|
||||||
|
rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):])
|
||||||
|
}
|
||||||
} else if strings.HasPrefix(s, "@LicenseUrl") {
|
} else if strings.HasPrefix(s, "@LicenseUrl") {
|
||||||
rootapi.Infos.LicenseUrl = strings.TrimSpace(s[len("@LicenseUrl"):])
|
if rootapi.Infos.License == nil {
|
||||||
|
rootapi.Infos.License = &swagger.License{URL: strings.TrimSpace(s[len("@LicenseUrl"):])}
|
||||||
|
} else {
|
||||||
|
rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):])
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(s, "@Schemes") {
|
||||||
|
rootapi.Schemes = strings.Split(strings.TrimSpace(s[len("@Schemes"):]), ",")
|
||||||
|
} else if strings.HasPrefix(s, "@Host") {
|
||||||
|
rootapi.Host = strings.TrimSpace(s[len("@Host"):])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// analisys controller package
|
||||||
for _, im := range f.Imports {
|
for _, im := range f.Imports {
|
||||||
analisyscontrollerPkg(im.Path.Value)
|
localName := ""
|
||||||
|
if im.Name != nil {
|
||||||
|
localName = im.Name.Name
|
||||||
|
}
|
||||||
|
analisyscontrollerPkg(localName, im.Path.Value)
|
||||||
}
|
}
|
||||||
for _, d := range f.Decls {
|
for _, d := range f.Decls {
|
||||||
switch specDecl := d.(type) {
|
switch specDecl := d.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
for _, l := range specDecl.Body.List {
|
for _, l := range specDecl.Body.List {
|
||||||
switch smtp := l.(type) {
|
switch stmt := l.(type) {
|
||||||
case *ast.AssignStmt:
|
case *ast.AssignStmt:
|
||||||
for _, l := range smtp.Rhs {
|
for _, l := range stmt.Rhs {
|
||||||
if v, ok := l.(*ast.CallExpr); ok {
|
if v, ok := l.(*ast.CallExpr); ok {
|
||||||
f, params := analisysNewNamespace(v)
|
// analisys NewNamespace, it will return version and the subfunction
|
||||||
globalDocsTemplate = strings.Replace(globalDocsTemplate, "{{.version}}", f, -1)
|
if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
version, params := analisysNewNamespace(v)
|
||||||
|
if rootapi.BasePath == "" && version != "" {
|
||||||
|
rootapi.BasePath = version
|
||||||
|
}
|
||||||
for _, p := range params {
|
for _, p := range params {
|
||||||
switch pp := p.(type) {
|
switch pp := p.(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
|
controllerName := ""
|
||||||
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
|
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
|
||||||
s, params := analisysNewNamespace(pp)
|
s, params := analisysNewNamespace(pp)
|
||||||
subapi := swagger.ApiRef{Path: s}
|
|
||||||
controllerName := ""
|
|
||||||
for _, sp := range params {
|
for _, sp := range params {
|
||||||
switch pp := sp.(type) {
|
switch pp := sp.(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" {
|
if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" {
|
||||||
controllerName = analisysNSInclude(s, pp)
|
controllerName = analisysNSInclude(s, pp)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v, ok := controllerComments[controllerName]; ok {
|
if v, ok := controllerComments[controllerName]; ok {
|
||||||
subapi.Description = v
|
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
||||||
|
Name: strings.Trim(s, "/"),
|
||||||
|
Description: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rootapi.Apis = append(rootapi.Apis, subapi)
|
|
||||||
} else if selname == "NSInclude" {
|
} else if selname == "NSInclude" {
|
||||||
analisysNSInclude(f, pp)
|
controllerName = analisysNSInclude("", pp)
|
||||||
|
if v, ok := controllerComments[controllerName]; ok {
|
||||||
|
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
||||||
|
Name: controllerName, // if the NSInclude has no prefix, we use the controllername as the tag
|
||||||
|
Description: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,25 +171,27 @@ func generateDocs(curpath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apiinfo, err := json.Marshal(rootapi)
|
os.Mkdir(path.Join(curpath, "swagger"), 0755)
|
||||||
if err != nil {
|
fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json"))
|
||||||
panic(err)
|
fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml"))
|
||||||
}
|
|
||||||
subapi, err := json.Marshal(apilist)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
os.Mkdir(path.Join(curpath, "docs"), 0755)
|
|
||||||
fd, err := os.Create(path.Join(curpath, "docs", "docs.go"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
defer fdyml.Close()
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
a := strings.Replace(globalDocsTemplate, "{{.rootinfo}}", "`"+string(apiinfo)+"`", -1)
|
dt, err := json.MarshalIndent(rootapi, "", " ")
|
||||||
a = strings.Replace(a, "{{.subapi}}", "`"+string(subapi)+"`", -1)
|
dtyml, erryml := yaml.Marshal(rootapi)
|
||||||
fd.WriteString(a)
|
if err != nil || erryml != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
_, err = fd.Write(dt)
|
||||||
|
_, erryml = fdyml.Write(dtyml)
|
||||||
|
if err != nil || erryml != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return version and the others params
|
||||||
func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
|
func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
|
||||||
for i, p := range ce.Args {
|
for i, p := range ce.Args {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
@ -225,45 +208,66 @@ func analisysNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
|
|||||||
|
|
||||||
func analisysNSInclude(baseurl string, ce *ast.CallExpr) string {
|
func analisysNSInclude(baseurl string, ce *ast.CallExpr) string {
|
||||||
cname := ""
|
cname := ""
|
||||||
a := &swagger.ApiDeclaration{}
|
|
||||||
a.ApiVersion = rootapi.ApiVersion
|
|
||||||
a.SwaggerVersion = swagger.SwaggerVersion
|
|
||||||
a.ResourcePath = baseurl
|
|
||||||
a.Produces = []string{"application/json", "application/xml", "text/plain", "text/html"}
|
|
||||||
a.Apis = make([]swagger.Api, 0)
|
|
||||||
a.Models = make(map[string]swagger.Model)
|
|
||||||
for _, p := range ce.Args {
|
for _, p := range ce.Args {
|
||||||
x := p.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr)
|
x := p.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr)
|
||||||
if v, ok := importlist[fmt.Sprint(x.X)]; ok {
|
if v, ok := importlist[fmt.Sprint(x.X)]; ok {
|
||||||
cname = v + x.Sel.Name
|
cname = v + x.Sel.Name
|
||||||
}
|
}
|
||||||
if apis, ok := controllerList[cname]; ok {
|
if apis, ok := controllerList[cname]; ok {
|
||||||
if len(a.Apis) > 0 {
|
for rt, item := range apis {
|
||||||
a.Apis = append(a.Apis, apis...)
|
tag := ""
|
||||||
|
if baseurl != "" {
|
||||||
|
rt = baseurl + rt
|
||||||
|
tag = strings.Trim(baseurl, "/")
|
||||||
} else {
|
} else {
|
||||||
a.Apis = apis
|
tag = cname
|
||||||
}
|
}
|
||||||
|
if item.Get != nil {
|
||||||
|
item.Get.Tags = []string{tag}
|
||||||
}
|
}
|
||||||
if models, ok := modelsList[cname]; ok {
|
if item.Post != nil {
|
||||||
for _, m := range models {
|
item.Post.Tags = []string{tag}
|
||||||
a.Models[m.Id] = m
|
}
|
||||||
|
if item.Put != nil {
|
||||||
|
item.Put.Tags = []string{tag}
|
||||||
|
}
|
||||||
|
if item.Patch != nil {
|
||||||
|
item.Patch.Tags = []string{tag}
|
||||||
|
}
|
||||||
|
if item.Head != nil {
|
||||||
|
item.Head.Tags = []string{tag}
|
||||||
|
}
|
||||||
|
if item.Delete != nil {
|
||||||
|
item.Delete.Tags = []string{tag}
|
||||||
|
}
|
||||||
|
if item.Options != nil {
|
||||||
|
item.Options.Tags = []string{tag}
|
||||||
|
}
|
||||||
|
if len(rootapi.Paths) == 0 {
|
||||||
|
rootapi.Paths = make(map[string]*swagger.Item)
|
||||||
|
}
|
||||||
|
rt = urlReplace(rt)
|
||||||
|
rootapi.Paths[rt] = item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apilist[baseurl] = a
|
|
||||||
return cname
|
return cname
|
||||||
}
|
}
|
||||||
|
|
||||||
func analisyscontrollerPkg(pkgpath string) {
|
func analisyscontrollerPkg(localName, pkgpath string) {
|
||||||
pkgpath = strings.Trim(pkgpath, "\"")
|
pkgpath = strings.Trim(pkgpath, "\"")
|
||||||
if isSystemPackage(pkgpath) {
|
if isSystemPackage(pkgpath) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pps := strings.Split(pkgpath, "/")
|
|
||||||
importlist[pps[len(pps)-1]] = pkgpath
|
|
||||||
if pkgpath == "github.com/astaxie/beego" {
|
if pkgpath == "github.com/astaxie/beego" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if localName != "" {
|
||||||
|
importlist[localName] = pkgpath
|
||||||
|
} else {
|
||||||
|
pps := strings.Split(pkgpath, "/")
|
||||||
|
importlist[pps[len(pps)-1]] = pkgpath
|
||||||
|
}
|
||||||
gopath := os.Getenv("GOPATH")
|
gopath := os.Getenv("GOPATH")
|
||||||
if gopath == "" {
|
if gopath == "" {
|
||||||
panic("please set gopath")
|
panic("please set gopath")
|
||||||
@ -282,6 +286,7 @@ func analisyscontrollerPkg(pkgpath string) {
|
|||||||
if _, ok := pkgCache[pkgpath]; ok {
|
if _, ok := pkgCache[pkgpath]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
pkgCache[pkgpath] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] the %s pkg not exist in gopath\n", pkgpath)
|
ColorLog("[ERRO] the %s pkg not exist in gopath\n", pkgpath)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -303,15 +308,18 @@ func analisyscontrollerPkg(pkgpath string) {
|
|||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 {
|
if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 {
|
||||||
if t, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok {
|
if t, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok {
|
||||||
|
// parse controller method
|
||||||
parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(t.X), pkgpath)
|
parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(t.X), pkgpath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ast.GenDecl:
|
case *ast.GenDecl:
|
||||||
if specDecl.Tok.String() == "type" {
|
if specDecl.Tok == token.TYPE {
|
||||||
for _, s := range specDecl.Specs {
|
for _, s := range specDecl.Specs {
|
||||||
switch tp := s.(*ast.TypeSpec).Type.(type) {
|
switch tp := s.(*ast.TypeSpec).Type.(type) {
|
||||||
case *ast.StructType:
|
case *ast.StructType:
|
||||||
_ = tp.Struct
|
_ = tp.Struct
|
||||||
|
//parse controller definition comments
|
||||||
|
if strings.TrimSpace(specDecl.Doc.Text()) != "" {
|
||||||
controllerComments[pkgpath+s.(*ast.TypeSpec).Name.String()] = specDecl.Doc.Text()
|
controllerComments[pkgpath+s.(*ast.TypeSpec).Name.String()] = specDecl.Doc.Text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,6 +329,7 @@ func analisyscontrollerPkg(pkgpath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isSystemPackage(pkgpath string) bool {
|
func isSystemPackage(pkgpath string) bool {
|
||||||
goroot := runtime.GOROOT()
|
goroot := runtime.GOROOT()
|
||||||
@ -331,13 +340,34 @@ func isSystemPackage(pkgpath string) bool {
|
|||||||
if utils.FileExists(wg) {
|
if utils.FileExists(wg) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO(zh):support go1.4
|
||||||
|
wg, _ = filepath.EvalSymlinks(filepath.Join(goroot, "src", pkgpath))
|
||||||
|
if utils.FileExists(wg) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func peekNextSplitString(ss string) (s string, spacePos int) {
|
||||||
|
spacePos = strings.IndexFunc(ss, unicode.IsSpace)
|
||||||
|
if spacePos < 0 {
|
||||||
|
s = ss
|
||||||
|
spacePos = len(ss)
|
||||||
|
} else {
|
||||||
|
s = strings.TrimSpace(ss[:spacePos])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// parse the func comments
|
// parse the func comments
|
||||||
func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error {
|
func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error {
|
||||||
innerapi := swagger.Api{}
|
var routerPath string
|
||||||
opts := swagger.Operation{}
|
var HTTPMethod string
|
||||||
|
opts := swagger.Operation{
|
||||||
|
Responses: make(map[string]swagger.Response),
|
||||||
|
}
|
||||||
if comments != nil && comments.List != nil {
|
if comments != nil && comments.List != nil {
|
||||||
for _, c := range comments.List {
|
for _, c := range comments.List {
|
||||||
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||||
@ -347,66 +377,64 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
|||||||
if len(e1) < 1 {
|
if len(e1) < 1 {
|
||||||
return errors.New("you should has router infomation")
|
return errors.New("you should has router infomation")
|
||||||
}
|
}
|
||||||
innerapi.Path = e1[0]
|
routerPath = e1[0]
|
||||||
if len(e1) == 2 && e1[1] != "" {
|
if len(e1) == 2 && e1[1] != "" {
|
||||||
e1 = strings.SplitN(e1[1], " ", 2)
|
e1 = strings.SplitN(e1[1], " ", 2)
|
||||||
opts.HttpMethod = strings.ToUpper(strings.Trim(e1[0], "[]"))
|
HTTPMethod = strings.ToUpper(strings.Trim(e1[0], "[]"))
|
||||||
} else {
|
} else {
|
||||||
opts.HttpMethod = "GET"
|
HTTPMethod = "GET"
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(t, "@Title") {
|
} else if strings.HasPrefix(t, "@Title") {
|
||||||
opts.Nickname = strings.TrimSpace(t[len("@Title"):])
|
opts.OperationID = controllerName + "." + strings.TrimSpace(t[len("@Title"):])
|
||||||
} else if strings.HasPrefix(t, "@Description") {
|
} else if strings.HasPrefix(t, "@Description") {
|
||||||
opts.Summary = strings.TrimSpace(t[len("@Description"):])
|
opts.Description = strings.TrimSpace(t[len("@Description"):])
|
||||||
|
} else if strings.HasPrefix(t, "@Summary") {
|
||||||
|
opts.Summary = strings.TrimSpace(t[len("@Summary"):])
|
||||||
} else if strings.HasPrefix(t, "@Success") {
|
} else if strings.HasPrefix(t, "@Success") {
|
||||||
ss := strings.TrimSpace(t[len("@Success"):])
|
ss := strings.TrimSpace(t[len("@Success"):])
|
||||||
rs := swagger.ResponseMessage{}
|
rs := swagger.Response{}
|
||||||
st := make([]string, 3)
|
respCode, pos := peekNextSplitString(ss)
|
||||||
j := 0
|
ss = strings.TrimSpace(ss[pos:])
|
||||||
var tmp []rune
|
respType, pos := peekNextSplitString(ss)
|
||||||
start := false
|
if respType == "{object}" || respType == "{array}" {
|
||||||
|
isArray := respType == "{array}"
|
||||||
for i, c := range ss {
|
ss = strings.TrimSpace(ss[pos:])
|
||||||
if unicode.IsSpace(c) {
|
schemaName, pos := peekNextSplitString(ss)
|
||||||
if !start && j < 2 {
|
if schemaName == "" {
|
||||||
continue
|
ColorLog("[ERRO][%s.%s] Schema must follow {object} or {array}\n", controllerName, funcName)
|
||||||
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
if j == 0 || j == 1 {
|
if strings.HasPrefix(schemaName, "[]") {
|
||||||
st[j] = string(tmp)
|
schemaName = schemaName[2:]
|
||||||
tmp = make([]rune, 0)
|
isArray = true
|
||||||
j += 1
|
}
|
||||||
start = false
|
schema := swagger.Schema{}
|
||||||
continue
|
if sType, ok := basicTypes[schemaName]; ok {
|
||||||
|
typeFormat := strings.Split(sType, ":")
|
||||||
|
schema.Type = typeFormat[0]
|
||||||
|
schema.Format = typeFormat[1]
|
||||||
} else {
|
} else {
|
||||||
st[j] = strings.TrimSpace(ss[i+1:])
|
cmpath, m, mod, realTypes := getModel(schemaName)
|
||||||
break
|
schema.Ref = "#/definitions/" + m
|
||||||
}
|
|
||||||
} else {
|
|
||||||
start = true
|
|
||||||
tmp = append(tmp, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(tmp) > 0 && st[2] == "" {
|
|
||||||
st[2] = strings.TrimSpace(string(tmp))
|
|
||||||
}
|
|
||||||
rs.Message = st[2]
|
|
||||||
if st[1] == "{object}" {
|
|
||||||
if st[2] == "" {
|
|
||||||
panic(controllerName + " " + funcName + " has no object")
|
|
||||||
}
|
|
||||||
cmpath, m, mod, realTypes := getModel(st[2])
|
|
||||||
//ll := strings.Split(st[2], ".")
|
|
||||||
//opts.Type = ll[len(ll)-1]
|
|
||||||
rs.ResponseModel = m
|
|
||||||
if _, ok := modelsList[pkgpath+controllerName]; !ok {
|
if _, ok := modelsList[pkgpath+controllerName]; !ok {
|
||||||
modelsList[pkgpath+controllerName] = make(map[string]swagger.Model, 0)
|
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0)
|
||||||
}
|
}
|
||||||
modelsList[pkgpath+controllerName][st[2]] = mod
|
modelsList[pkgpath+controllerName][schemaName] = mod
|
||||||
appendModels(cmpath, pkgpath, controllerName, realTypes)
|
appendModels(cmpath, pkgpath, controllerName, realTypes)
|
||||||
}
|
}
|
||||||
|
if isArray {
|
||||||
rs.Code, _ = strconv.Atoi(st[0])
|
rs.Schema = &swagger.Schema{
|
||||||
opts.ResponseMessages = append(opts.ResponseMessages, rs)
|
Type: "array",
|
||||||
|
Items: &schema,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rs.Schema = &schema
|
||||||
|
}
|
||||||
|
rs.Description = strings.TrimSpace(ss[pos:])
|
||||||
|
} else {
|
||||||
|
rs.Description = strings.TrimSpace(ss)
|
||||||
|
}
|
||||||
|
opts.Responses[respCode] = rs
|
||||||
} else if strings.HasPrefix(t, "@Param") {
|
} else if strings.HasPrefix(t, "@Param") {
|
||||||
para := swagger.Parameter{}
|
para := swagger.Parameter{}
|
||||||
p := getparams(strings.TrimSpace(t[len("@Param "):]))
|
p := getparams(strings.TrimSpace(t[len("@Param "):]))
|
||||||
@ -414,25 +442,78 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
|||||||
panic(controllerName + "_" + funcName + "'s comments @Param at least should has 4 params")
|
panic(controllerName + "_" + funcName + "'s comments @Param at least should has 4 params")
|
||||||
}
|
}
|
||||||
para.Name = p[0]
|
para.Name = p[0]
|
||||||
para.ParamType = p[1]
|
switch p[1] {
|
||||||
|
case "query":
|
||||||
|
fallthrough
|
||||||
|
case "header":
|
||||||
|
fallthrough
|
||||||
|
case "path":
|
||||||
|
fallthrough
|
||||||
|
case "formData":
|
||||||
|
fallthrough
|
||||||
|
case "body":
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
ColorLog("[WARN][%s.%s] Unknow 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], ".")
|
pp := strings.Split(p[2], ".")
|
||||||
para.DataType = pp[len(pp)-1]
|
typ := pp[len(pp)-1]
|
||||||
|
if len(pp) >= 2 {
|
||||||
|
cmpath, m, mod, realTypes := getModel(p[2])
|
||||||
|
para.Schema = &swagger.Schema{
|
||||||
|
Ref: "#/definitions/" + m,
|
||||||
|
}
|
||||||
|
if _, ok := modelsList[pkgpath+controllerName]; !ok {
|
||||||
|
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema, 0)
|
||||||
|
}
|
||||||
|
modelsList[pkgpath+controllerName][typ] = mod
|
||||||
|
appendModels(cmpath, pkgpath, controllerName, realTypes)
|
||||||
|
} else {
|
||||||
|
isArray := false
|
||||||
|
paraType := ""
|
||||||
|
paraFormat := ""
|
||||||
|
if strings.HasPrefix(typ, "[]") {
|
||||||
|
typ = typ[2:]
|
||||||
|
isArray = true
|
||||||
|
}
|
||||||
|
if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" ||
|
||||||
|
typ == "array" || typ == "file" {
|
||||||
|
paraType = typ
|
||||||
|
} else if sType, ok := basicTypes[typ]; ok {
|
||||||
|
typeFormat := strings.Split(sType, ":")
|
||||||
|
paraType = typeFormat[0]
|
||||||
|
paraFormat = typeFormat[1]
|
||||||
|
} else {
|
||||||
|
ColorLog("[WARN][%s.%s] Unknow param type: %s\n", controllerName, funcName, typ)
|
||||||
|
}
|
||||||
|
if isArray {
|
||||||
|
para.Type = "array"
|
||||||
|
para.Items = &swagger.ParameterItems{
|
||||||
|
Type: paraType,
|
||||||
|
Format: paraFormat,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
para.Type = paraType
|
||||||
|
para.Format = paraFormat
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(p) > 4 {
|
if len(p) > 4 {
|
||||||
para.Required, _ = strconv.ParseBool(p[3])
|
para.Required, _ = strconv.ParseBool(p[3])
|
||||||
para.Description = p[4]
|
para.Description = strings.Trim(p[4], `" `)
|
||||||
} else {
|
} else {
|
||||||
para.Description = p[3]
|
para.Description = strings.Trim(p[3], `" `)
|
||||||
}
|
}
|
||||||
opts.Parameters = append(opts.Parameters, para)
|
opts.Parameters = append(opts.Parameters, para)
|
||||||
} else if strings.HasPrefix(t, "@Failure") {
|
} else if strings.HasPrefix(t, "@Failure") {
|
||||||
rs := swagger.ResponseMessage{}
|
rs := swagger.Response{}
|
||||||
st := strings.TrimSpace(t[len("@Failure"):])
|
st := strings.TrimSpace(t[len("@Failure"):])
|
||||||
var cd []rune
|
var cd []rune
|
||||||
var start bool
|
var start bool
|
||||||
for i, s := range st {
|
for i, s := range st {
|
||||||
if unicode.IsSpace(s) {
|
if unicode.IsSpace(s) {
|
||||||
if start {
|
if start {
|
||||||
rs.Message = strings.TrimSpace(st[i+1:])
|
rs.Description = strings.TrimSpace(st[i+1:])
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
@ -441,10 +522,9 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
|||||||
start = true
|
start = true
|
||||||
cd = append(cd, s)
|
cd = append(cd, s)
|
||||||
}
|
}
|
||||||
rs.Code, _ = strconv.Atoi(string(cd))
|
opts.Responses[string(cd)] = rs
|
||||||
opts.ResponseMessages = append(opts.ResponseMessages, rs)
|
} else if strings.HasPrefix(t, "@Deprecated") {
|
||||||
} else if strings.HasPrefix(t, "@Type") {
|
opts.Deprecated, _ = strconv.ParseBool(strings.TrimSpace(t[len("@Deprecated"):]))
|
||||||
opts.Type = strings.TrimSpace(t[len("@Type"):])
|
|
||||||
} else if strings.HasPrefix(t, "@Accept") {
|
} else if strings.HasPrefix(t, "@Accept") {
|
||||||
accepts := strings.Split(strings.TrimSpace(strings.TrimSpace(t[len("@Accept"):])), ",")
|
accepts := strings.Split(strings.TrimSpace(strings.TrimSpace(t[len("@Accept"):])), ",")
|
||||||
for _, a := range accepts {
|
for _, a := range accepts {
|
||||||
@ -466,14 +546,35 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
innerapi.Operations = append(innerapi.Operations, opts)
|
if routerPath != "" {
|
||||||
if innerapi.Path != "" {
|
var item *swagger.Item
|
||||||
if _, ok := controllerList[pkgpath+controllerName]; ok {
|
if itemList, ok := controllerList[pkgpath+controllerName]; ok {
|
||||||
controllerList[pkgpath+controllerName] = append(controllerList[pkgpath+controllerName], innerapi)
|
if it, ok := itemList[routerPath]; !ok {
|
||||||
|
item = &swagger.Item{}
|
||||||
} else {
|
} else {
|
||||||
controllerList[pkgpath+controllerName] = make([]swagger.Api, 1)
|
item = it
|
||||||
controllerList[pkgpath+controllerName][0] = innerapi
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
controllerList[pkgpath+controllerName] = make(map[string]*swagger.Item)
|
||||||
|
item = &swagger.Item{}
|
||||||
|
}
|
||||||
|
switch HTTPMethod {
|
||||||
|
case "GET":
|
||||||
|
item.Get = &opts
|
||||||
|
case "POST":
|
||||||
|
item.Post = &opts
|
||||||
|
case "PUT":
|
||||||
|
item.Put = &opts
|
||||||
|
case "PATCH":
|
||||||
|
item.Patch = &opts
|
||||||
|
case "DELETE":
|
||||||
|
item.Delete = &opts
|
||||||
|
case "HEAD":
|
||||||
|
item.Head = &opts
|
||||||
|
case "OPTIONS":
|
||||||
|
item.Options = &opts
|
||||||
|
}
|
||||||
|
controllerList[pkgpath+controllerName][routerPath] = item
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -509,7 +610,7 @@ func getparams(str string) []string {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func getModel(str string) (pkgpath, objectname string, m swagger.Model, realTypes []string) {
|
func getModel(str string) (pkgpath, objectname string, m swagger.Schema, realTypes []string) {
|
||||||
strs := strings.Split(str, ".")
|
strs := strings.Split(str, ".")
|
||||||
objectname = strs[len(strs)-1]
|
objectname = strs[len(strs)-1]
|
||||||
pkgpath = strings.Join(strs[:len(strs)-1], "/")
|
pkgpath = strings.Join(strs[:len(strs)-1], "/")
|
||||||
@ -525,7 +626,7 @@ func getModel(str string) (pkgpath, objectname string, m swagger.Model, realType
|
|||||||
ColorLog("[ERRO] the model %s parser.ParseDir error\n", str)
|
ColorLog("[ERRO] the model %s parser.ParseDir error\n", str)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
m.Type = "object"
|
||||||
for _, pkg := range astPkgs {
|
for _, pkg := range astPkgs {
|
||||||
for _, fl := range pkg.Files {
|
for _, fl := range pkg.Files {
|
||||||
for k, d := range fl.Scope.Objects {
|
for k, d := range fl.Scope.Objects {
|
||||||
@ -533,41 +634,96 @@ func getModel(str string) (pkgpath, objectname string, m swagger.Model, realType
|
|||||||
if k != objectname {
|
if k != objectname {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
parseObject(d, k, &m, &realTypes, astPkgs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.Title == "" {
|
||||||
|
ColorLog("[WARN]can't find the object: %s\n", str)
|
||||||
|
// TODO remove when all type have been supported
|
||||||
|
//os.Exit(1)
|
||||||
|
}
|
||||||
|
if len(rootapi.Definitions) == 0 {
|
||||||
|
rootapi.Definitions = make(map[string]swagger.Schema)
|
||||||
|
}
|
||||||
|
rootapi.Definitions[objectname] = m
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package) {
|
||||||
ts, ok := d.Decl.(*ast.TypeSpec)
|
ts, ok := d.Decl.(*ast.TypeSpec)
|
||||||
if !ok {
|
if !ok {
|
||||||
ColorLog("Unknown type without TypeSec: %v", d)
|
ColorLog("Unknown type without TypeSec: %v\n", d)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
|
||||||
st, ok := ts.Type.(*ast.StructType)
|
st, ok := ts.Type.(*ast.StructType)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.Title = k
|
||||||
|
if st.Fields.List != nil {
|
||||||
|
m.Properties = make(map[string]swagger.Propertie)
|
||||||
|
for _, field := range st.Fields.List {
|
||||||
|
isSlice, realType, sType := typeAnalyser(field)
|
||||||
|
*realTypes = append(*realTypes, realType)
|
||||||
|
mp := swagger.Propertie{}
|
||||||
|
if isSlice {
|
||||||
|
mp.Type = "array"
|
||||||
|
if isBasicType(realType) {
|
||||||
|
typeFormat := strings.Split(sType, ":")
|
||||||
|
mp.Items = &swagger.Propertie{
|
||||||
|
Type: typeFormat[0],
|
||||||
|
Format: typeFormat[1],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mp.Items = &swagger.Propertie{
|
||||||
|
Ref: "#/definitions/" + realType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sType == "object" {
|
||||||
|
mp.Ref = "#/definitions/" + realType
|
||||||
|
} else if isBasicType(realType) {
|
||||||
|
typeFormat := strings.Split(sType, ":")
|
||||||
|
mp.Type = typeFormat[0]
|
||||||
|
mp.Format = typeFormat[1]
|
||||||
|
} else if realType == "map" {
|
||||||
|
typeFormat := strings.Split(sType, ":")
|
||||||
|
mp.AdditionalProperties = &swagger.Propertie{
|
||||||
|
Type: typeFormat[0],
|
||||||
|
Format: typeFormat[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if field.Names != nil {
|
||||||
|
|
||||||
|
// set property name as field name
|
||||||
|
var name = field.Names[0].Name
|
||||||
|
|
||||||
|
// if no tag skip tag processing
|
||||||
|
if field.Tag == nil {
|
||||||
|
m.Properties[name] = mp
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.Id = k
|
|
||||||
if st.Fields.List != nil {
|
var tagValues []string
|
||||||
m.Properties = make(map[string]swagger.ModelProperty)
|
|
||||||
for _, field := range st.Fields.List {
|
|
||||||
isSlice, realType := typeAnalyser(field)
|
|
||||||
realTypes = append(realTypes, realType)
|
|
||||||
mp := swagger.ModelProperty{}
|
|
||||||
// add type slice
|
|
||||||
if isSlice {
|
|
||||||
if isBasicType(realType) {
|
|
||||||
mp.Type = "[]" + realType
|
|
||||||
} else {
|
|
||||||
mp.Type = "array"
|
|
||||||
mp.Items = make(map[string]string)
|
|
||||||
mp.Items["$ref"] = realType
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mp.Type = realType
|
|
||||||
}
|
|
||||||
// if the tag contains json tag, set the name to the left most json tag
|
|
||||||
var name = field.Names[0].Name
|
|
||||||
if field.Tag != nil {
|
|
||||||
stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
|
stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
|
||||||
if tag := stag.Get("json"); tag != "" {
|
tag := stag.Get("json")
|
||||||
name = tag
|
|
||||||
|
if tag != "" {
|
||||||
|
tagValues = strings.Split(tag, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dont add property if json tag first value is "-"
|
||||||
|
if len(tagValues) == 0 || tagValues[0] != "-" {
|
||||||
|
|
||||||
|
// set property name to the left most json tag value only if is not omitempty
|
||||||
|
if len(tagValues) > 0 && tagValues[0] != "omitempty" {
|
||||||
|
name = tagValues[0]
|
||||||
|
}
|
||||||
|
|
||||||
if thrifttag := stag.Get("thrift"); thrifttag != "" {
|
if thrifttag := stag.Get("thrift"); thrifttag != "" {
|
||||||
ts := strings.Split(thrifttag, ",")
|
ts := strings.Split(thrifttag, ",")
|
||||||
if ts[0] != "" {
|
if ts[0] != "" {
|
||||||
@ -580,66 +736,77 @@ func getModel(str string) (pkgpath, objectname string, m swagger.Model, realType
|
|||||||
if desc := stag.Get("description"); desc != "" {
|
if desc := stag.Get("description"); desc != "" {
|
||||||
mp.Description = desc
|
mp.Description = desc
|
||||||
}
|
}
|
||||||
}
|
|
||||||
m.Properties[name] = mp
|
m.Properties[name] = mp
|
||||||
}
|
}
|
||||||
|
if ignore := stag.Get("ignore"); ignore != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, pkg := range astPkgs {
|
||||||
|
for _, fl := range pkg.Files {
|
||||||
|
for nameOfObj, obj := range fl.Scope.Objects {
|
||||||
|
if obj.Name == fmt.Sprint(field.Type) {
|
||||||
|
parseObject(obj, nameOfObj, m, realTypes, astPkgs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.Id == "" {
|
|
||||||
ColorLog("can't find the object: %v", str)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func typeAnalyser(f *ast.Field) (isSlice bool, realType string) {
|
func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
|
||||||
if arr, ok := f.Type.(*ast.ArrayType); ok {
|
if arr, ok := f.Type.(*ast.ArrayType); ok {
|
||||||
if isBasicType(fmt.Sprint(arr.Elt)) {
|
if isBasicType(fmt.Sprint(arr.Elt)) {
|
||||||
return false, fmt.Sprintf("[]%v", arr.Elt)
|
return false, fmt.Sprintf("[]%v", arr.Elt), basicTypes[fmt.Sprint(arr.Elt)]
|
||||||
}
|
}
|
||||||
if mp, ok := arr.Elt.(*ast.MapType); ok {
|
if mp, ok := arr.Elt.(*ast.MapType); ok {
|
||||||
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value)
|
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), "object"
|
||||||
}
|
}
|
||||||
if star, ok := arr.Elt.(*ast.StarExpr); ok {
|
if star, ok := arr.Elt.(*ast.StarExpr); ok {
|
||||||
return true, fmt.Sprint(star.X)
|
return true, fmt.Sprint(star.X), "object"
|
||||||
} else {
|
}
|
||||||
return true, fmt.Sprint(arr.Elt)
|
return true, fmt.Sprint(arr.Elt), "object"
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch t := f.Type.(type) {
|
switch t := f.Type.(type) {
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
return false, fmt.Sprint(t.X)
|
return false, fmt.Sprint(t.X), "object"
|
||||||
|
case *ast.MapType:
|
||||||
|
val := fmt.Sprintf("%v", t.Value)
|
||||||
|
if isBasicType(val) {
|
||||||
|
return false, "map", basicTypes[val]
|
||||||
}
|
}
|
||||||
return false, fmt.Sprint(f.Type)
|
return false, val, "object"
|
||||||
}
|
}
|
||||||
|
if k, ok := basicTypes[fmt.Sprint(f.Type)]; ok {
|
||||||
|
return false, fmt.Sprint(f.Type), k
|
||||||
|
}
|
||||||
|
return false, fmt.Sprint(f.Type), "object"
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBasicType(Type string) bool {
|
func isBasicType(Type string) bool {
|
||||||
for _, v := range basicTypes {
|
if _, ok := basicTypes[Type]; ok {
|
||||||
if v == Type {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// refer to builtin.go
|
// refer to builtin.go
|
||||||
var basicTypes = []string{
|
var basicTypes = map[string]string{
|
||||||
"bool",
|
"bool": "boolean:",
|
||||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
"uint": "integer:int32", "uint8": "integer:int32", "uint16": "integer:int32", "uint32": "integer:int32", "uint64": "integer:int64",
|
||||||
"int", "int8", "int16", "int32", "int64",
|
"int": "integer:int64", "int8": "integer:int32", "int16:int32": "integer:int32", "int32": "integer:int32", "int64": "integer:int64",
|
||||||
"float32", "float64",
|
"uintptr": "integer:int64",
|
||||||
"string",
|
"float32": "number:float", "float64": "number:double",
|
||||||
"complex64", "complex128",
|
"string": "string:",
|
||||||
"byte", "rune", "uintptr",
|
"complex64": "number:float", "complex128": "number:double",
|
||||||
|
"byte": "string:byte", "rune": "string:byte",
|
||||||
}
|
}
|
||||||
|
|
||||||
// regexp get json tag
|
// regexp get json tag
|
||||||
func grepJsonTag(tag string) string {
|
func grepJSONTag(tag string) string {
|
||||||
r, _ := regexp.Compile(`json:"([^"]*)"`)
|
r, _ := regexp.Compile(`json:"([^"]*)"`)
|
||||||
matches := r.FindAllStringSubmatch(tag, -1)
|
matches := r.FindAllStringSubmatch(tag, -1)
|
||||||
if len(matches) > 0 {
|
if len(matches) > 0 {
|
||||||
@ -669,3 +836,17 @@ func appendModels(cmpath, pkgpath, controllerName string, realTypes []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func urlReplace(src string) string {
|
||||||
|
pt := strings.Split(src, "/")
|
||||||
|
for i, p := range pt {
|
||||||
|
if len(p) > 0 {
|
||||||
|
if p[0] == ':' {
|
||||||
|
pt[i] = "{" + p[1:] + "}"
|
||||||
|
} else if p[0] == '?' && p[1] == ':' {
|
||||||
|
pt[i] = "{" + p[2:] + "}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(pt, "/")
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
@ -31,11 +32,11 @@ func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
|
|||||||
var mode byte
|
var mode byte
|
||||||
switch level {
|
switch level {
|
||||||
case "1":
|
case "1":
|
||||||
mode = O_MODEL
|
mode = OModel
|
||||||
case "2":
|
case "2":
|
||||||
mode = O_MODEL | O_CONTROLLER
|
mode = OModel | OController
|
||||||
case "3":
|
case "3":
|
||||||
mode = O_MODEL | O_CONTROLLER | O_ROUTER
|
mode = OModel | OController | ORouter
|
||||||
default:
|
default:
|
||||||
ColorLog("[ERRO] Invalid 'level' option: %s\n", level)
|
ColorLog("[ERRO] Invalid 'level' option: %s\n", level)
|
||||||
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
|
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
|
||||||
@ -90,7 +91,7 @@ func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bo
|
|||||||
// It will wipe the following directories and recreate them:./models, ./controllers, ./routers
|
// It will wipe the following directories and recreate them:./models, ./controllers, ./routers
|
||||||
// Newly geneated files will be inside these folders.
|
// Newly geneated files will be inside these folders.
|
||||||
func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
|
func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
|
||||||
if (O_MODEL & mode) == O_MODEL {
|
if (OModel & mode) == OModel {
|
||||||
ColorLog("[INFO] Creating model files...\n")
|
ColorLog("[INFO] Creating model files...\n")
|
||||||
writeHproseModelFiles(tables, paths.ModelPath, selectedTables)
|
writeHproseModelFiles(tables, paths.ModelPath, selectedTables)
|
||||||
}
|
}
|
||||||
@ -98,6 +99,8 @@ func writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *M
|
|||||||
|
|
||||||
// writeHproseModelFiles generates model files
|
// writeHproseModelFiles generates model files
|
||||||
func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
|
func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
for _, tb := range tables {
|
for _, tb := range tables {
|
||||||
// if selectedTables map is not nil and this table is not selected, ignore it
|
// if selectedTables map is not nil and this table is not selected, ignore it
|
||||||
if selectedTables != nil {
|
if selectedTables != nil {
|
||||||
@ -110,7 +113,7 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
|
|||||||
var f *os.File
|
var f *os.File
|
||||||
var err error
|
var err error
|
||||||
if isExist(fpath) {
|
if isExist(fpath) {
|
||||||
ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath)
|
ColorLog("[WARN] '%v' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -118,7 +121,7 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[WARN] skip create file\n")
|
ColorLog("[WARN] Skipped create file '%s'\n", fpath)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -130,10 +133,10 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
|
|||||||
}
|
}
|
||||||
template := ""
|
template := ""
|
||||||
if tb.Pk == "" {
|
if tb.Pk == "" {
|
||||||
template = HPROSE_STRUCT_MODEL_TPL
|
template = HproseStructModelTPL
|
||||||
} else {
|
} else {
|
||||||
template = HPROSE_MODEL_TPL
|
template = HproseModelTPL
|
||||||
hproseAddFunctions = append(hproseAddFunctions, strings.Replace(HPROSE_ADDFUNCTION, "{{modelName}}", camelCase(tb.Name), -1))
|
hproseAddFunctions = append(hproseAddFunctions, strings.Replace(HproseAddFunction, "{{modelName}}", camelCase(tb.Name), -1))
|
||||||
}
|
}
|
||||||
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
|
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
|
||||||
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1)
|
fileStr = strings.Replace(fileStr, "{{modelName}}", camelCase(tb.Name), -1)
|
||||||
@ -147,17 +150,17 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
|
|||||||
fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1)
|
fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1)
|
||||||
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
|
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
|
||||||
if _, err := f.WriteString(fileStr); err != nil {
|
if _, err := f.WriteString(fileStr); err != nil {
|
||||||
ColorLog("[ERRO] Could not write model file to %s\n", fpath)
|
ColorLog("[ERRO] Could not write model file to '%s'\n", fpath)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
f.Close()
|
CloseFile(f)
|
||||||
ColorLog("[INFO] model => %s\n", fpath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||||
formatSourceCode(fpath)
|
formatSourceCode(fpath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HPROSE_ADDFUNCTION = `
|
HproseAddFunction = `
|
||||||
// publish about {{modelName}} function
|
// publish about {{modelName}} function
|
||||||
service.AddFunction("Add{{modelName}}", models.Add{{modelName}})
|
service.AddFunction("Add{{modelName}}", models.Add{{modelName}})
|
||||||
service.AddFunction("Get{{modelName}}ById", models.Get{{modelName}}ById)
|
service.AddFunction("Get{{modelName}}ById", models.Get{{modelName}}ById)
|
||||||
@ -166,12 +169,12 @@ const (
|
|||||||
service.AddFunction("Delete{{modelName}}", models.Delete{{modelName}})
|
service.AddFunction("Delete{{modelName}}", models.Delete{{modelName}})
|
||||||
|
|
||||||
`
|
`
|
||||||
HPROSE_STRUCT_MODEL_TPL = `package models
|
HproseStructModelTPL = `package models
|
||||||
{{importTimePkg}}
|
{{importTimePkg}}
|
||||||
{{modelStruct}}
|
{{modelStruct}}
|
||||||
`
|
`
|
||||||
|
|
||||||
HPROSE_MODEL_TPL = `package models
|
HproseModelTPL = `package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -260,7 +263,7 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri
|
|||||||
|
|
||||||
var l []{{modelName}}
|
var l []{{modelName}}
|
||||||
qs = qs.OrderBy(sortFields...)
|
qs = qs.OrderBy(sortFields...)
|
||||||
if _, err := qs.Limit(limit, offset).All(&l, fields...); err == nil {
|
if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
for _, v := range l {
|
for _, v := range l {
|
||||||
ml = append(ml, v)
|
ml = append(ml, v)
|
||||||
|
178
g_migration.go
178
g_migration.go
@ -23,15 +23,170 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
M_PATH = "migrations"
|
MPath = "migrations"
|
||||||
M_DATE_FORMAT = "20060102_150405"
|
MDateFormat = "20060102_150405"
|
||||||
|
DBPath = "database"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DBDriver interface {
|
||||||
|
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()) + `)");`
|
||||||
|
return upsql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mysqlDriver) generateCreateDown(tableName string) string {
|
||||||
|
downsql := `m.SQL("DROP TABLE ` + "`" + tableName + "`" + `")`
|
||||||
|
return downsql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mysqlDriver) generateSQLFromFields(fields string) string {
|
||||||
|
sql, tags := "", ""
|
||||||
|
fds := strings.Split(fields, ",")
|
||||||
|
for i, v := range fds {
|
||||||
|
kv := strings.SplitN(v, ":", 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
typ, tag := m.getSQLType(kv[1])
|
||||||
|
if typ == "" {
|
||||||
|
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||||
|
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 + ","
|
||||||
|
if tag != "" {
|
||||||
|
tags = tags + fmt.Sprintf(tag, "`"+snakeString(kv[0])+"`") + ","
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sql = strings.TrimRight(sql+tags, ",")
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mysqlDriver) getSQLType(ktype string) (tp, tag string) {
|
||||||
|
kv := strings.SplitN(ktype, ":", 2)
|
||||||
|
switch kv[0] {
|
||||||
|
case "string":
|
||||||
|
if len(kv) == 2 {
|
||||||
|
return "varchar(" + kv[1] + ") NOT NULL", ""
|
||||||
|
}
|
||||||
|
return "varchar(128) NOT NULL", ""
|
||||||
|
case "text":
|
||||||
|
return "longtext NOT NULL", ""
|
||||||
|
case "auto":
|
||||||
|
return "int(11) NOT NULL AUTO_INCREMENT", ""
|
||||||
|
case "pk":
|
||||||
|
return "int(11) NOT NULL", "PRIMARY KEY (%s)"
|
||||||
|
case "datetime":
|
||||||
|
return "datetime NOT NULL", ""
|
||||||
|
case "int", "int8", "int16", "int32", "int64":
|
||||||
|
fallthrough
|
||||||
|
case "uint", "uint8", "uint16", "uint32", "uint64":
|
||||||
|
return "int(11) DEFAULT NULL", ""
|
||||||
|
case "bool":
|
||||||
|
return "tinyint(1) NOT NULL", ""
|
||||||
|
case "float32", "float64":
|
||||||
|
return "float NOT NULL", ""
|
||||||
|
case "float":
|
||||||
|
return "float NOT NULL", ""
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type postgresqlDriver struct{}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
downsql := `m.SQL("DROP TABLE ` + tableName + `")`
|
||||||
|
return downsql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m postgresqlDriver) generateSQLFromFields(fields string) string {
|
||||||
|
sql, tags := "", ""
|
||||||
|
fds := strings.Split(fields, ",")
|
||||||
|
for i, v := range fds {
|
||||||
|
kv := strings.SplitN(v, ":", 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
typ, tag := m.getSQLType(kv[1])
|
||||||
|
if typ == "" {
|
||||||
|
ColorLog("[ERRO] Fields format is wrong. Should be: key:type,key:type " + v + "\n")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if i == 0 && strings.ToLower(kv[0]) != "id" {
|
||||||
|
sql += "id serial primary key,"
|
||||||
|
}
|
||||||
|
sql += snakeString(kv[0]) + " " + typ + ","
|
||||||
|
if tag != "" {
|
||||||
|
tags = tags + fmt.Sprintf(tag, snakeString(kv[0])) + ","
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tags != "" {
|
||||||
|
sql = strings.TrimRight(sql+" "+tags, ",")
|
||||||
|
} else {
|
||||||
|
sql = strings.TrimRight(sql, ",")
|
||||||
|
}
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m postgresqlDriver) getSQLType(ktype string) (tp, tag string) {
|
||||||
|
kv := strings.SplitN(ktype, ":", 2)
|
||||||
|
switch kv[0] {
|
||||||
|
case "string":
|
||||||
|
if len(kv) == 2 {
|
||||||
|
return "char(" + kv[1] + ") NOT NULL", ""
|
||||||
|
}
|
||||||
|
return "TEXT NOT NULL", ""
|
||||||
|
case "text":
|
||||||
|
return "TEXT NOT NULL", ""
|
||||||
|
case "auto", "pk":
|
||||||
|
return "serial primary key", ""
|
||||||
|
case "datetime":
|
||||||
|
return "TIMESTAMP WITHOUT TIME ZONE NOT NULL", ""
|
||||||
|
case "int", "int8", "int16", "int32", "int64":
|
||||||
|
fallthrough
|
||||||
|
case "uint", "uint8", "uint16", "uint32", "uint64":
|
||||||
|
return "integer DEFAULT NULL", ""
|
||||||
|
case "bool":
|
||||||
|
return "boolean NOT NULL", ""
|
||||||
|
case "float32", "float64", "float":
|
||||||
|
return "numeric NOT NULL", ""
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDBDriver() DBDriver {
|
||||||
|
switch driver {
|
||||||
|
case "mysql":
|
||||||
|
return mysqlDriver{}
|
||||||
|
case "postgres":
|
||||||
|
return postgresqlDriver{}
|
||||||
|
default:
|
||||||
|
panic("driver not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// generateMigration generates migration file template for database schema update.
|
// generateMigration generates migration file template for database schema update.
|
||||||
// The generated file template consists of an up() method for updating schema and
|
// The generated file template consists of an up() method for updating schema and
|
||||||
// a down() method for reverting the update.
|
// a down() method for reverting the update.
|
||||||
func generateMigration(mname, upsql, downsql, curpath string) {
|
func generateMigration(mname, upsql, downsql, curpath string) {
|
||||||
migrationFilePath := path.Join(curpath, "database", M_PATH)
|
w := NewColorWriter(os.Stdout)
|
||||||
|
migrationFilePath := path.Join(curpath, DBPath, MPath)
|
||||||
if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) {
|
if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) {
|
||||||
// create migrations directory
|
// create migrations directory
|
||||||
if err := os.MkdirAll(migrationFilePath, 0777); err != nil {
|
if err := os.MkdirAll(migrationFilePath, 0777); err != nil {
|
||||||
@ -40,26 +195,25 @@ func generateMigration(mname, upsql, downsql, curpath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// create file
|
// create file
|
||||||
today := time.Now().Format(M_DATE_FORMAT)
|
today := time.Now().Format(MDateFormat)
|
||||||
fpath := path.Join(migrationFilePath, fmt.Sprintf("%s_%s.go", today, mname))
|
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 {
|
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
||||||
defer f.Close()
|
defer CloseFile(f)
|
||||||
content := strings.Replace(MIGRATION_TPL, "{{StructName}}", camelCase(mname)+"_"+today, -1)
|
content := strings.Replace(MigrationTPL, "{{StructName}}", camelCase(mname)+"_"+today, -1)
|
||||||
content = strings.Replace(content, "{{CurrTime}}", today, -1)
|
content = strings.Replace(content, "{{CurrTime}}", today, -1)
|
||||||
content = strings.Replace(content, "{{UpSQL}}", upsql, -1)
|
content = strings.Replace(content, "{{UpSQL}}", upsql, -1)
|
||||||
content = strings.Replace(content, "{{DownSQL}}", downsql, -1)
|
content = strings.Replace(content, "{{DownSQL}}", downsql, -1)
|
||||||
f.WriteString(content)
|
f.WriteString(content)
|
||||||
// gofmt generated source code
|
// Run 'gofmt' on the generated source code
|
||||||
formatSourceCode(fpath)
|
formatSourceCode(fpath)
|
||||||
ColorLog("[INFO] Migration file generated: %s\n", fpath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||||
} else {
|
} else {
|
||||||
// error creating file
|
|
||||||
ColorLog("[ERRO] Could not create migration file: %s\n", err)
|
ColorLog("[ERRO] Could not create migration file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MIGRATION_TPL = `package main
|
const MigrationTPL = `package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/astaxie/beego/migration"
|
"github.com/astaxie/beego/migration"
|
||||||
@ -79,13 +233,13 @@ func init() {
|
|||||||
|
|
||||||
// Run the migrations
|
// Run the migrations
|
||||||
func (m *{{StructName}}) Up() {
|
func (m *{{StructName}}) Up() {
|
||||||
// use m.Sql("CREATE TABLE ...") to make schema update
|
// use m.SQL("CREATE TABLE ...") to make schema update
|
||||||
{{UpSQL}}
|
{{UpSQL}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse the migrations
|
// Reverse the migrations
|
||||||
func (m *{{StructName}}) Down() {
|
func (m *{{StructName}}) Down() {
|
||||||
// use m.Sql("DROP TABLE ...") to reverse schema update
|
// use m.SQL("DROP TABLE ...") to reverse schema update
|
||||||
{{DownSQL}}
|
{{DownSQL}}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
63
g_model.go
63
g_model.go
@ -1,13 +1,30 @@
|
|||||||
|
// 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 main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateModel(mname, fields, crupath string) {
|
func generateModel(mname, fields, currpath string) {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
p, f := path.Split(mname)
|
p, f := path.Split(mname)
|
||||||
modelName := strings.Title(f)
|
modelName := strings.Title(f)
|
||||||
packageName := "models"
|
packageName := "models"
|
||||||
@ -15,24 +32,28 @@ func generateModel(mname, fields, crupath string) {
|
|||||||
i := strings.LastIndex(p[:len(p)-1], "/")
|
i := strings.LastIndex(p[:len(p)-1], "/")
|
||||||
packageName = p[i+1 : len(p)-1]
|
packageName = p[i+1 : len(p)-1]
|
||||||
}
|
}
|
||||||
modelStruct, err, hastime := getStruct(modelName, fields)
|
|
||||||
|
modelStruct, hastime, err := getStruct(modelName, fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ColorLog("[ERRO] Could not genrate models struct: %s\n", err)
|
ColorLog("[ERRO] Could not generate the model struct: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorLog("[INFO] Using '%s' as model name\n", modelName)
|
ColorLog("[INFO] Using '%s' as model name\n", modelName)
|
||||||
ColorLog("[INFO] Using '%s' as package name\n", packageName)
|
ColorLog("[INFO] Using '%s' as package name\n", packageName)
|
||||||
fp := path.Join(crupath, "models", p)
|
|
||||||
|
fp := path.Join(currpath, "models", p)
|
||||||
if _, err := os.Stat(fp); os.IsNotExist(err) {
|
if _, err := os.Stat(fp); os.IsNotExist(err) {
|
||||||
// create controller directory
|
// Create the model's directory
|
||||||
if err := os.MkdirAll(fp, 0777); err != nil {
|
if err := os.MkdirAll(fp, 0777); err != nil {
|
||||||
ColorLog("[ERRO] Could not create models directory: %s\n", err)
|
ColorLog("[ERRO] Could not create the model directory: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fpath := path.Join(fp, strings.ToLower(modelName)+".go")
|
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 {
|
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
||||||
defer f.Close()
|
defer CloseFile(f)
|
||||||
content := strings.Replace(modelTpl, "{{packageName}}", packageName, -1)
|
content := strings.Replace(modelTpl, "{{packageName}}", packageName, -1)
|
||||||
content = strings.Replace(content, "{{modelName}}", modelName, -1)
|
content = strings.Replace(content, "{{modelName}}", modelName, -1)
|
||||||
content = strings.Replace(content, "{{modelStruct}}", modelStruct, -1)
|
content = strings.Replace(content, "{{modelStruct}}", modelStruct, -1)
|
||||||
@ -42,42 +63,45 @@ func generateModel(mname, fields, crupath string) {
|
|||||||
content = strings.Replace(content, "{{timePkg}}", "", -1)
|
content = strings.Replace(content, "{{timePkg}}", "", -1)
|
||||||
}
|
}
|
||||||
f.WriteString(content)
|
f.WriteString(content)
|
||||||
// gofmt generated source code
|
// Run 'gofmt' on the generated source code
|
||||||
formatSourceCode(fpath)
|
formatSourceCode(fpath)
|
||||||
ColorLog("[INFO] model file generated: %s\n", fpath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
|
||||||
} else {
|
} else {
|
||||||
// error creating file
|
|
||||||
ColorLog("[ERRO] Could not create model file: %s\n", err)
|
ColorLog("[ERRO] Could not create model file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStruct(structname, fields string) (string, error, bool) {
|
func getStruct(structname, fields string) (string, bool, error) {
|
||||||
if fields == "" {
|
if fields == "" {
|
||||||
return "", errors.New("fields can't empty"), false
|
return "", false, errors.New("fields cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
hastime := false
|
hastime := false
|
||||||
structStr := "type " + structname + " struct{\n"
|
structStr := "type " + structname + " struct{\n"
|
||||||
fds := strings.Split(fields, ",")
|
fds := strings.Split(fields, ",")
|
||||||
for i, v := range fds {
|
for i, v := range fds {
|
||||||
kv := strings.SplitN(v, ":", 2)
|
kv := strings.SplitN(v, ":", 2)
|
||||||
if len(kv) != 2 {
|
if len(kv) != 2 {
|
||||||
return "", errors.New("the filds format is wrong. should key:type,key:type " + v), false
|
return "", false, errors.New("the fields format is wrong. Should be key:type,key:type " + v)
|
||||||
}
|
}
|
||||||
|
|
||||||
typ, tag, hastimeinner := getType(kv[1])
|
typ, tag, hastimeinner := getType(kv[1])
|
||||||
if typ == "" {
|
if typ == "" {
|
||||||
return "", errors.New("the filds format is wrong. should key:type,key:type " + v), false
|
return "", false, errors.New("the fields format is wrong. Should be key:type,key:type " + v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == 0 && strings.ToLower(kv[0]) != "id" {
|
if i == 0 && strings.ToLower(kv[0]) != "id" {
|
||||||
structStr = structStr + "Id int64 `orm:\"auto\"`\n"
|
structStr = structStr + "Id int64 `orm:\"auto\"`\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if hastimeinner {
|
if hastimeinner {
|
||||||
hastime = true
|
hastime = true
|
||||||
}
|
}
|
||||||
structStr = structStr + camelString(kv[0]) + " " + typ + " " + tag + "\n"
|
structStr = structStr + camelString(kv[0]) + " " + typ + " " + tag + "\n"
|
||||||
}
|
}
|
||||||
structStr += "}\n"
|
structStr += "}\n"
|
||||||
return structStr, nil, hastime
|
return structStr, hastime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields support type
|
// fields support type
|
||||||
@ -88,9 +112,8 @@ func getType(ktype string) (kt, tag string, hasTime bool) {
|
|||||||
case "string":
|
case "string":
|
||||||
if len(kv) == 2 {
|
if len(kv) == 2 {
|
||||||
return "string", "`orm:\"size(" + kv[1] + ")\"`", false
|
return "string", "`orm:\"size(" + kv[1] + ")\"`", false
|
||||||
} else {
|
|
||||||
return "string", "`orm:\"size(128)\"`", false
|
|
||||||
}
|
}
|
||||||
|
return "string", "`orm:\"size(128)\"`", false
|
||||||
case "text":
|
case "text":
|
||||||
return "string", "`orm:\"type(longtext)\"`", false
|
return "string", "`orm:\"type(longtext)\"`", false
|
||||||
case "auto":
|
case "auto":
|
||||||
@ -140,7 +163,7 @@ func Add{{modelName}}(m *{{modelName}}) (id int64, err error) {
|
|||||||
|
|
||||||
// Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if
|
// Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if
|
||||||
// Id doesn't exist
|
// Id doesn't exist
|
||||||
func Get{{modelName}}ById(id int) (v *{{modelName}}, err error) {
|
func Get{{modelName}}ById(id int64) (v *{{modelName}}, err error) {
|
||||||
o := orm.NewOrm()
|
o := orm.NewOrm()
|
||||||
v = &{{modelName}}{Id: id}
|
v = &{{modelName}}{Id: id}
|
||||||
if err = o.Read(v); err == nil {
|
if err = o.Read(v); err == nil {
|
||||||
@ -202,7 +225,7 @@ func GetAll{{modelName}}(query map[string]string, fields []string, sortby []stri
|
|||||||
|
|
||||||
var l []{{modelName}}
|
var l []{{modelName}}
|
||||||
qs = qs.OrderBy(sortFields...)
|
qs = qs.OrderBy(sortFields...)
|
||||||
if _, err := qs.Limit(limit, offset).All(&l, fields...); err == nil {
|
if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
for _, v := range l {
|
for _, v := range l {
|
||||||
ml = append(ml, v)
|
ml = append(ml, v)
|
||||||
@ -240,7 +263,7 @@ func Update{{modelName}}ById(m *{{modelName}}) (err error) {
|
|||||||
|
|
||||||
// Delete{{modelName}} deletes {{modelName}} by Id and returns error if
|
// Delete{{modelName}} deletes {{modelName}} by Id and returns error if
|
||||||
// the record to be deleted doesn't exist
|
// the record to be deleted doesn't exist
|
||||||
func Delete{{modelName}}(id int) (err error) {
|
func Delete{{modelName}}(id int64) (err error) {
|
||||||
o := orm.NewOrm()
|
o := orm.NewOrm()
|
||||||
v := {{modelName}}{Id: id}
|
v := {{modelName}}{Id: id}
|
||||||
// ascertain id exists in the database
|
// ascertain id exists in the database
|
||||||
|
109
g_scaffold.go
109
g_scaffold.go
@ -1,101 +1,48 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "strings"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func generateScaffold(sname, fields, crupath, driver, conn string) {
|
func generateScaffold(sname, fields, currpath, driver, conn string) {
|
||||||
// generate model
|
ColorLog("[INFO] Do you want to create a '%v' model? [Yes|No] ", sname)
|
||||||
ColorLog("[INFO] Do you want me to create a %v model? [yes|no]] ", sname)
|
|
||||||
|
// Generate the model
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
generateModel(sname, fields, crupath)
|
generateModel(sname, fields, currpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate controller
|
// Generate the controller
|
||||||
ColorLog("[INFO] Do you want me to create a %v controller? [yes|no]] ", sname)
|
ColorLog("[INFO] Do you want to create a '%v' controller? [Yes|No] ", sname)
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
generateController(sname, crupath)
|
generateController(sname, currpath)
|
||||||
}
|
}
|
||||||
// generate view
|
|
||||||
ColorLog("[INFO] Do you want me to create views for this %v resource? [yes|no]] ", sname)
|
// Generate the views
|
||||||
|
ColorLog("[INFO] Do you want to create views for this '%v' resource? [Yes|No] ", sname)
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
generateView(sname, crupath)
|
generateView(sname, currpath)
|
||||||
}
|
}
|
||||||
// generate migration
|
|
||||||
ColorLog("[INFO] Do you want me to create a %v migration and schema for this resource? [yes|no]] ", sname)
|
// Generate a migration
|
||||||
|
ColorLog("[INFO] Do you want to create a '%v' migration and schema for this resource? [Yes|No] ", sname)
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
upsql := ""
|
upsql := ""
|
||||||
downsql := ""
|
downsql := ""
|
||||||
if fields != "" {
|
if fields != "" {
|
||||||
upsql = `m.Sql("CREATE TABLE ` + sname + "(" + generateSQLFromFields(fields) + `)");`
|
dbMigrator := newDBDriver()
|
||||||
downsql = `m.Sql("DROP TABLE ` + "`" + sname + "`" + `")`
|
upsql = dbMigrator.generateCreateUp(sname)
|
||||||
|
downsql = dbMigrator.generateCreateDown(sname)
|
||||||
|
//todo remove
|
||||||
|
//if driver == "" {
|
||||||
|
// downsql = strings.Replace(downsql, "`", "", -1)
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
generateMigration(sname, upsql, downsql, crupath)
|
generateMigration(sname, upsql, downsql, currpath)
|
||||||
}
|
}
|
||||||
// run migration
|
|
||||||
ColorLog("[INFO] Do you want to go ahead and migrate the database? [yes|no]] ")
|
// Run the migration
|
||||||
|
ColorLog("[INFO] Do you want to migrate the database? [Yes|No] ")
|
||||||
if askForConfirmation() {
|
if askForConfirmation() {
|
||||||
migrateUpdate(crupath, driver, conn)
|
migrateUpdate(currpath, driver, conn)
|
||||||
}
|
}
|
||||||
ColorLog("[INFO] All done! Don't forget to add beego.Router(\"/%v\" ,&controllers.%vController{}) to routers/route.go\n", sname, strings.Title(sname))
|
ColorLog("[INFO] All done! Don't forget to add beego.Router(\"/%v\" ,&controllers.%vController{}) to routers/route.go\n", sname, strings.Title(sname))
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateSQLFromFields(fields string) string {
|
|
||||||
sql := ""
|
|
||||||
tags := ""
|
|
||||||
fds := strings.Split(fields, ",")
|
|
||||||
for i, v := range fds {
|
|
||||||
kv := strings.SplitN(v, ":", 2)
|
|
||||||
if len(kv) != 2 {
|
|
||||||
ColorLog("[ERRO] the filds format is wrong. should key:type,key:type " + v)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
typ, tag := getSqlType(kv[1])
|
|
||||||
if typ == "" {
|
|
||||||
ColorLog("[ERRO] the filds format is wrong. should key:type,key:type " + v)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if i == 0 && strings.ToLower(kv[0]) != "id" {
|
|
||||||
sql = sql + "`id` int(11) NOT NULL AUTO_INCREMENT,"
|
|
||||||
tags = tags + "PRIMARY KEY (`id`),"
|
|
||||||
}
|
|
||||||
sql = sql + "`" + snakeString(kv[0]) + "` " + typ + ","
|
|
||||||
if tag != "" {
|
|
||||||
tags = tags + fmt.Sprintf(tag, "`"+snakeString(kv[0])+"`") + ","
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sql = strings.TrimRight(sql+tags, ",")
|
|
||||||
return sql
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSqlType(ktype string) (tp, tag string) {
|
|
||||||
kv := strings.SplitN(ktype, ":", 2)
|
|
||||||
switch kv[0] {
|
|
||||||
case "string":
|
|
||||||
if len(kv) == 2 {
|
|
||||||
return "varchar(" + kv[1] + ") NOT NULL", ""
|
|
||||||
} else {
|
|
||||||
return "varchar(128) NOT NULL", ""
|
|
||||||
}
|
|
||||||
case "text":
|
|
||||||
return "longtext NOT NULL", ""
|
|
||||||
case "auto":
|
|
||||||
return "int(11) NOT NULL AUTO_INCREMENT", ""
|
|
||||||
case "pk":
|
|
||||||
return "int(11) NOT NULL", "PRIMARY KEY (%s)"
|
|
||||||
case "datetime":
|
|
||||||
return "datetime NOT NULL", ""
|
|
||||||
case "int", "int8", "int16", "int32", "int64":
|
|
||||||
fallthrough
|
|
||||||
case "uint", "uint8", "uint16", "uint32", "uint64":
|
|
||||||
return "int(11) DEFAULT NULL", ""
|
|
||||||
case "bool":
|
|
||||||
return "tinyint(1) NOT NULL", ""
|
|
||||||
case "float32", "float64":
|
|
||||||
return "float NOT NULL", ""
|
|
||||||
case "float":
|
|
||||||
return "float NOT NULL", ""
|
|
||||||
}
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
|
57
g_views.go
57
g_views.go
@ -1,47 +1,74 @@
|
|||||||
|
// 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 main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
// recipe
|
// recipe
|
||||||
// admin/recipe
|
// admin/recipe
|
||||||
func generateView(vpath, crupath string) {
|
func generateView(viewpath, currpath string) {
|
||||||
absvpath := path.Join(crupath, "views", vpath)
|
w := NewColorWriter(os.Stdout)
|
||||||
os.MkdirAll(absvpath, os.ModePerm)
|
|
||||||
cfile := path.Join(absvpath, "index.tpl")
|
ColorLog("[INFO] Generating view...\n")
|
||||||
|
|
||||||
|
absViewPath := path.Join(currpath, "views", viewpath)
|
||||||
|
err := os.MkdirAll(absViewPath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
ColorLog("[ERRO] Could not create '%s' view: %s\n", viewpath, err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfile := path.Join(absViewPath, "index.tpl")
|
||||||
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
||||||
defer f.Close()
|
defer CloseFile(f)
|
||||||
f.WriteString(cfile)
|
f.WriteString(cfile)
|
||||||
ColorLog("[INFO] Created: %v\n", cfile)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
cfile = path.Join(absvpath, "show.tpl")
|
|
||||||
|
cfile = path.Join(absViewPath, "show.tpl")
|
||||||
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
||||||
defer f.Close()
|
defer CloseFile(f)
|
||||||
f.WriteString(cfile)
|
f.WriteString(cfile)
|
||||||
ColorLog("[INFO] Created: %v\n", cfile)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
cfile = path.Join(absvpath, "create.tpl")
|
|
||||||
|
cfile = path.Join(absViewPath, "create.tpl")
|
||||||
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
||||||
defer f.Close()
|
defer CloseFile(f)
|
||||||
f.WriteString(cfile)
|
f.WriteString(cfile)
|
||||||
ColorLog("[INFO] Created: %v\n", cfile)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
cfile = path.Join(absvpath, "edit.tpl")
|
|
||||||
|
cfile = path.Join(absViewPath, "edit.tpl")
|
||||||
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
|
||||||
defer f.Close()
|
defer CloseFile(f)
|
||||||
f.WriteString(cfile)
|
f.WriteString(cfile)
|
||||||
ColorLog("[INFO] Created: %v\n", cfile)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
|
||||||
} else {
|
} else {
|
||||||
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
ColorLog("[ERRO] Could not create view file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
34
hproseapp.go
34
hproseapp.go
@ -255,6 +255,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createhprose(cmd *Command, args []string) int {
|
func createhprose(cmd *Command, args []string) int {
|
||||||
|
ShowShortVersionBanner()
|
||||||
|
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
|
||||||
curpath, _ := os.Getwd()
|
curpath, _ := os.Getwd()
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
cmd.Flag.Parse(args[1:])
|
cmd.Flag.Parse(args[1:])
|
||||||
@ -269,12 +273,15 @@ func createhprose(cmd *Command, args []string) int {
|
|||||||
}
|
}
|
||||||
if conn == "" {
|
if conn == "" {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColorLog("[INFO] Creating Hprose application...\n")
|
||||||
|
|
||||||
os.MkdirAll(apppath, 0755)
|
os.MkdirAll(apppath, 0755)
|
||||||
fmt.Println("create app folder:", apppath)
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m")
|
||||||
os.Mkdir(path.Join(apppath, "conf"), 0755)
|
os.Mkdir(path.Join(apppath, "conf"), 0755)
|
||||||
fmt.Println("create conf:", path.Join(apppath, "conf"))
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m")
|
||||||
fmt.Println("create conf app.conf:", path.Join(apppath, "conf", "app.conf"))
|
fmt.Fprintf(w, "\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"),
|
WriteToFile(path.Join(apppath, "conf", "app.conf"),
|
||||||
strings.Replace(hproseconf, "{{.Appname}}", args[0], -1))
|
strings.Replace(hproseconf, "{{.Appname}}", args[0], -1))
|
||||||
|
|
||||||
if conn != "" {
|
if conn != "" {
|
||||||
@ -282,7 +289,7 @@ func createhprose(cmd *Command, args []string) int {
|
|||||||
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
|
||||||
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
|
||||||
generateHproseAppcode(string(driver), string(conn), "1", string(tables), path.Join(curpath, args[0]))
|
generateHproseAppcode(string(driver), string(conn), "1", string(tables), path.Join(curpath, args[0]))
|
||||||
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
|
fmt.Fprintf(w, "\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(hproseMainconngo, "{{.Appname}}", packpath, -1)
|
||||||
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1)
|
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1)
|
||||||
maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(hproseAddFunctions, ""), -1)
|
maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(hproseAddFunctions, ""), -1)
|
||||||
@ -291,7 +298,7 @@ func createhprose(cmd *Command, args []string) int {
|
|||||||
} else if driver == "postgres" {
|
} else if driver == "postgres" {
|
||||||
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
|
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
|
||||||
}
|
}
|
||||||
writetofile(path.Join(apppath, "main.go"),
|
WriteToFile(path.Join(apppath, "main.go"),
|
||||||
strings.Replace(
|
strings.Replace(
|
||||||
maingoContent,
|
maingoContent,
|
||||||
"{{.conn}}",
|
"{{.conn}}",
|
||||||
@ -301,17 +308,18 @@ func createhprose(cmd *Command, args []string) int {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
os.Mkdir(path.Join(apppath, "models"), 0755)
|
os.Mkdir(path.Join(apppath, "models"), 0755)
|
||||||
fmt.Println("create models:", path.Join(apppath, "models"))
|
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m")
|
||||||
|
|
||||||
fmt.Println("create models object.go:", path.Join(apppath, "models", "object.go"))
|
fmt.Fprintf(w, "\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)
|
WriteToFile(path.Join(apppath, "models", "object.go"), apiModels)
|
||||||
|
|
||||||
fmt.Println("create models user.go:", path.Join(apppath, "models", "user.go"))
|
fmt.Fprintf(w, "\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)
|
WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2)
|
||||||
|
|
||||||
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
|
fmt.Fprintf(w, "\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"),
|
WriteToFile(path.Join(apppath, "main.go"),
|
||||||
strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1))
|
strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1))
|
||||||
}
|
}
|
||||||
|
ColorLog("[SUCC] New Hprose application successfully created!\n")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
129
migrate.go
129
migrate.go
@ -23,6 +23,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdMigrate = &Command{
|
var cmdMigrate = &Command{
|
||||||
@ -31,22 +32,22 @@ var cmdMigrate = &Command{
|
|||||||
Long: `
|
Long: `
|
||||||
bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||||
run all outstanding migrations
|
run all outstanding migrations
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
-driver: [mysql | postgres | sqlite] (default: mysql)
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
||||||
|
|
||||||
bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||||
rollback the last migration operation
|
rollback the last migration operation
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
-driver: [mysql | postgres | sqlite] (default: mysql)
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
||||||
|
|
||||||
bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||||
rollback all migrations
|
rollback all migrations
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
-driver: [mysql | postgres | sqlite] (default: mysql)
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
||||||
|
|
||||||
bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
|
||||||
rollback all migrations and run them all again
|
rollback all migrations and run them all again
|
||||||
-driver: [mysql | postgresql | sqlite], the default is mysql
|
-driver: [mysql | postgres | sqlite] (default: mysql)
|
||||||
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
-conn: the connection string used by the driver, the default is root:@tcp(127.0.0.1:3306)/test
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
@ -56,21 +57,24 @@ var mConn docValue
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmdMigrate.Run = runMigration
|
cmdMigrate.Run = runMigration
|
||||||
cmdMigrate.Flag.Var(&mDriver, "driver", "database driver: mysql, postgresql, etc.")
|
cmdMigrate.Flag.Var(&mDriver, "driver", "database driver: mysql, postgres, sqlite, etc.")
|
||||||
cmdMigrate.Flag.Var(&mConn, "conn", "connection string used by the driver to connect to a database instance")
|
cmdMigrate.Flag.Var(&mConn, "conn", "connection string used by the driver to connect to a database instance")
|
||||||
}
|
}
|
||||||
|
|
||||||
// runMigration is the entry point for starting a migration
|
// runMigration is the entry point for starting a migration
|
||||||
func runMigration(cmd *Command, args []string) int {
|
func runMigration(cmd *Command, args []string) int {
|
||||||
crupath, _ := os.Getwd()
|
ShowShortVersionBanner()
|
||||||
|
|
||||||
gopath := os.Getenv("GOPATH")
|
currpath, _ := os.Getwd()
|
||||||
Debugf("gopath:%s", gopath)
|
|
||||||
if gopath == "" {
|
gps := GetGOPATHs()
|
||||||
ColorLog("[ERRO] $GOPATH not found\n")
|
if len(gps) == 0 {
|
||||||
ColorLog("[HINT] Set $GOPATH in your environment vairables\n")
|
ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
gopath := gps[0]
|
||||||
|
Debugf("GOPATH: %s", gopath)
|
||||||
|
|
||||||
// load config
|
// load config
|
||||||
err := loadConfig()
|
err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,19 +102,19 @@ func runMigration(cmd *Command, args []string) int {
|
|||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
// run all outstanding migrations
|
// run all outstanding migrations
|
||||||
ColorLog("[INFO] Running all outstanding migrations\n")
|
ColorLog("[INFO] Running all outstanding migrations\n")
|
||||||
migrateUpdate(crupath, driverStr, connStr)
|
migrateUpdate(currpath, driverStr, connStr)
|
||||||
} else {
|
} else {
|
||||||
mcmd := args[0]
|
mcmd := args[0]
|
||||||
switch mcmd {
|
switch mcmd {
|
||||||
case "rollback":
|
case "rollback":
|
||||||
ColorLog("[INFO] Rolling back the last migration operation\n")
|
ColorLog("[INFO] Rolling back the last migration operation\n")
|
||||||
migrateRollback(crupath, driverStr, connStr)
|
migrateRollback(currpath, driverStr, connStr)
|
||||||
case "reset":
|
case "reset":
|
||||||
ColorLog("[INFO] Reseting all migrations\n")
|
ColorLog("[INFO] Reseting all migrations\n")
|
||||||
migrateReset(crupath, driverStr, connStr)
|
migrateReset(currpath, driverStr, connStr)
|
||||||
case "refresh":
|
case "refresh":
|
||||||
ColorLog("[INFO] Refreshing all migrations\n")
|
ColorLog("[INFO] Refreshing all migrations\n")
|
||||||
migrateRefresh(crupath, driverStr, connStr)
|
migrateRefresh(currpath, driverStr, connStr)
|
||||||
default:
|
default:
|
||||||
ColorLog("[ERRO] Command is missing\n")
|
ColorLog("[ERRO] Command is missing\n")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
@ -121,38 +125,43 @@ func runMigration(cmd *Command, args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// migrateUpdate does the schema update
|
// migrateUpdate does the schema update
|
||||||
func migrateUpdate(crupath, driver, connStr string) {
|
func migrateUpdate(currpath, driver, connStr string) {
|
||||||
migrate("upgrade", crupath, driver, connStr)
|
migrate("upgrade", currpath, driver, connStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrateRollback rolls back the latest migration
|
// migrateRollback rolls back the latest migration
|
||||||
func migrateRollback(crupath, driver, connStr string) {
|
func migrateRollback(currpath, driver, connStr string) {
|
||||||
migrate("rollback", crupath, driver, connStr)
|
migrate("rollback", currpath, driver, connStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrateReset rolls back all migrations
|
// migrateReset rolls back all migrations
|
||||||
func migrateReset(crupath, driver, connStr string) {
|
func migrateReset(currpath, driver, connStr string) {
|
||||||
migrate("reset", crupath, driver, connStr)
|
migrate("reset", currpath, driver, connStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrationRefresh rolls back all migrations and start over again
|
// migrationRefresh rolls back all migrations and start over again
|
||||||
func migrateRefresh(crupath, driver, connStr string) {
|
func migrateRefresh(currpath, driver, connStr string) {
|
||||||
migrate("refresh", crupath, driver, connStr)
|
migrate("refresh", currpath, driver, connStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrate generates source code, build it, and invoke the binary who does the actual migration
|
// migrate generates source code, build it, and invoke the binary who does the actual migration
|
||||||
func migrate(goal, crupath, driver, connStr string) {
|
func migrate(goal, currpath, driver, connStr string) {
|
||||||
dir := path.Join(crupath, "database", "migrations")
|
dir := path.Join(currpath, "database", "migrations")
|
||||||
binary := "m"
|
postfix := ""
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
postfix = ".exe"
|
||||||
|
}
|
||||||
|
binary := "m" + postfix
|
||||||
source := binary + ".go"
|
source := binary + ".go"
|
||||||
// connect to database
|
// connect to database
|
||||||
db, err := sql.Open(driver, connStr)
|
db, err := sql.Open(driver, connStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ColorLog("[ERRO] Could not connect to %s: %s\n", driver, connStr)
|
ColorLog("[ERRO] Could not connect to %s: %s\n", driver, connStr)
|
||||||
|
ColorLog("[ERRO] Error: %v", err.Error())
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
checkForSchemaUpdateTable(db)
|
checkForSchemaUpdateTable(db, driver)
|
||||||
latestName, latestTime := getLatestMigration(db, goal)
|
latestName, latestTime := getLatestMigration(db, goal)
|
||||||
writeMigrationSourceFile(dir, source, driver, connStr, latestTime, latestName, goal)
|
writeMigrationSourceFile(dir, source, driver, connStr, latestTime, latestName, goal)
|
||||||
buildMigrationBinary(dir, binary)
|
buildMigrationBinary(dir, binary)
|
||||||
@ -163,20 +172,24 @@ func migrate(goal, crupath, driver, connStr string) {
|
|||||||
|
|
||||||
// checkForSchemaUpdateTable checks the existence of migrations table.
|
// checkForSchemaUpdateTable checks the existence of migrations table.
|
||||||
// It checks for the proper table structures and creates the table using MYSQL_MIGRATION_DDL if it does not exist.
|
// It checks for the proper table structures and creates the table using MYSQL_MIGRATION_DDL if it does not exist.
|
||||||
func checkForSchemaUpdateTable(db *sql.DB) {
|
func checkForSchemaUpdateTable(db *sql.DB, driver string) {
|
||||||
if rows, err := db.Query("SHOW TABLES LIKE 'migrations'"); err != nil {
|
showTableSQL := showMigrationsTableSQL(driver)
|
||||||
|
if rows, err := db.Query(showTableSQL); err != nil {
|
||||||
ColorLog("[ERRO] Could not show migrations table: %s\n", err)
|
ColorLog("[ERRO] Could not show migrations table: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
} else if !rows.Next() {
|
} else if !rows.Next() {
|
||||||
// no migrations table, create anew
|
// no migrations table, create anew
|
||||||
|
createTableSQL := createMigrationsTableSQL(driver)
|
||||||
ColorLog("[INFO] Creating 'migrations' table...\n")
|
ColorLog("[INFO] Creating 'migrations' table...\n")
|
||||||
if _, err := db.Query(MYSQL_MIGRATION_DDL); err != nil {
|
if _, err := db.Query(createTableSQL); err != nil {
|
||||||
ColorLog("[ERRO] Could not create migrations table: %s\n", err)
|
ColorLog("[ERRO] Could not create migrations table: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checking that migrations table schema are expected
|
// checking that migrations table schema are expected
|
||||||
if rows, err := db.Query("DESC migrations"); err != nil {
|
selectTableSQL := selectMigrationsTableSQL(driver)
|
||||||
|
if rows, err := db.Query(selectTableSQL); err != nil {
|
||||||
ColorLog("[ERRO] Could not show columns of migrations table: %s\n", err)
|
ColorLog("[ERRO] Could not show columns of migrations table: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
} else {
|
} else {
|
||||||
@ -212,6 +225,39 @@ func checkForSchemaUpdateTable(db *sql.DB) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showMigrationsTableSQL(driver string) string {
|
||||||
|
switch driver {
|
||||||
|
case "mysql":
|
||||||
|
return "SHOW TABLES LIKE 'migrations'"
|
||||||
|
case "postgres":
|
||||||
|
return "SELECT * FROM pg_catalog.pg_tables WHERE tablename = 'migrations';"
|
||||||
|
default:
|
||||||
|
return "SHOW TABLES LIKE 'migrations'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMigrationsTableSQL(driver string) string {
|
||||||
|
switch driver {
|
||||||
|
case "mysql":
|
||||||
|
return MYSQLMigrationDDL
|
||||||
|
case "postgres":
|
||||||
|
return POSTGRESMigrationDDL
|
||||||
|
default:
|
||||||
|
return MYSQLMigrationDDL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectMigrationsTableSQL(driver string) string {
|
||||||
|
switch driver {
|
||||||
|
case "mysql":
|
||||||
|
return "DESC migrations"
|
||||||
|
case "postgres":
|
||||||
|
return "SELECT * FROM migrations WHERE false ORDER BY id_migration;"
|
||||||
|
default:
|
||||||
|
return "DESC migrations"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getLatestMigration retrives latest migration with status 'update'
|
// getLatestMigration retrives latest migration with status 'update'
|
||||||
func getLatestMigration(db *sql.DB, goal string) (file string, createdAt int64) {
|
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"
|
sql := "SELECT name FROM migrations where status = 'update' ORDER BY id_migration DESC LIMIT 1"
|
||||||
@ -250,7 +296,7 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in
|
|||||||
ColorLog("[ERRO] Could not create file: %s\n", err)
|
ColorLog("[ERRO] Could not create file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
} else {
|
} else {
|
||||||
content := strings.Replace(MIGRATION_MAIN_TPL, "{{DBDriver}}", driver, -1)
|
content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1)
|
||||||
content = strings.Replace(content, "{{ConnStr}}", connStr, -1)
|
content = strings.Replace(content, "{{ConnStr}}", connStr, -1)
|
||||||
content = strings.Replace(content, "{{LatestTime}}", strconv.FormatInt(latestTime, 10), -1)
|
content = strings.Replace(content, "{{LatestTime}}", strconv.FormatInt(latestTime, 10), -1)
|
||||||
content = strings.Replace(content, "{{LatestName}}", latestName, -1)
|
content = strings.Replace(content, "{{LatestName}}", latestName, -1)
|
||||||
@ -259,7 +305,7 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in
|
|||||||
ColorLog("[ERRO] Could not write to file: %s\n", err)
|
ColorLog("[ERRO] Could not write to file: %s\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
f.Close()
|
CloseFile(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +375,7 @@ func formatShellOutput(o string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MIGRATION_MAIN_TPL = `package main
|
MigrationMainTPL = `package main
|
||||||
|
|
||||||
import(
|
import(
|
||||||
"os"
|
"os"
|
||||||
@ -338,6 +384,7 @@ import(
|
|||||||
"github.com/astaxie/beego/migration"
|
"github.com/astaxie/beego/migration"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init(){
|
func init(){
|
||||||
@ -367,7 +414,7 @@ func main(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
`
|
`
|
||||||
MYSQL_MIGRATION_DDL = `
|
MYSQLMigrationDDL = `
|
||||||
CREATE TABLE migrations (
|
CREATE TABLE migrations (
|
||||||
id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
|
id_migration int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'surrogate key',
|
||||||
name varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
|
name varchar(255) DEFAULT NULL COMMENT 'migration name, unique',
|
||||||
@ -378,4 +425,16 @@ CREATE TABLE migrations (
|
|||||||
PRIMARY KEY (id_migration)
|
PRIMARY KEY (id_migration)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||||
`
|
`
|
||||||
|
|
||||||
|
POSTGRESMigrationDDL = `
|
||||||
|
CREATE TYPE migrations_status AS ENUM('update', 'rollback');
|
||||||
|
|
||||||
|
CREATE TABLE migrations (
|
||||||
|
id_migration SERIAL PRIMARY KEY,
|
||||||
|
name varchar(255) DEFAULT NULL,
|
||||||
|
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
statements text,
|
||||||
|
rollback_statements text,
|
||||||
|
status migrations_status
|
||||||
|
)`
|
||||||
)
|
)
|
||||||
|
65
pack.go
65
pack.go
@ -37,22 +37,23 @@ import (
|
|||||||
var cmdPack = &Command{
|
var cmdPack = &Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "pack",
|
UsageLine: "pack",
|
||||||
Short: "compress an beego project",
|
Short: "Compress a beego project into a single file",
|
||||||
Long: `
|
Long: `
|
||||||
compress an beego project
|
Pack is used to compress a beego project into a single file.
|
||||||
|
This eases the deployment by extracting the zip file to a server.
|
||||||
|
|
||||||
-p app path. default is current path
|
-p app path (default is the current path).
|
||||||
-b build specify platform app. default true
|
-b build specify platform app (default: true).
|
||||||
-ba additional args of go build
|
-ba additional args of go build
|
||||||
-be=[] additional ENV Variables of go build. eg: GOARCH=arm
|
-be=[] additional ENV Variables of go build. eg: GOARCH=arm
|
||||||
-o compressed file output dir. default use current path
|
-o compressed file output dir. default use current path
|
||||||
-f="" format. [ tar.gz / zip ]. default tar.gz
|
-f="" format: tar.gz, zip (default: tar.gz)
|
||||||
-exp="" relpath exclude prefix. default: .
|
-exp="" relpath exclude prefix (default: .). use : as separator
|
||||||
-exs="" relpath exclude suffix. default: .go:.DS_Store:.tmp
|
-exs="" relpath exclude suffix (default: .go:.DS_Store:.tmp). use : as separator
|
||||||
all path use : as separator
|
all path use : as separator
|
||||||
-exr=[] file/directory name exclude by Regexp. default: ^.
|
-exr=[] file/directory name exclude by Regexp (default: ^).
|
||||||
-fs=false follow symlink. default false
|
-fs=false follow symlink (default: false).
|
||||||
-ss=false skip symlink. default false
|
-ss=false skip symlink (default: false)
|
||||||
default embed symlink into compressed file
|
default embed symlink into compressed file
|
||||||
-v=false verbose
|
-v=false verbose
|
||||||
`,
|
`,
|
||||||
@ -71,6 +72,7 @@ var (
|
|||||||
buildEnvs ListOpts
|
buildEnvs ListOpts
|
||||||
verbose bool
|
verbose bool
|
||||||
format string
|
format string
|
||||||
|
w io.Writer
|
||||||
)
|
)
|
||||||
|
|
||||||
type ListOpts []string
|
type ListOpts []string
|
||||||
@ -100,6 +102,7 @@ func init() {
|
|||||||
fs.BoolVar(&verbose, "v", false, "verbose")
|
fs.BoolVar(&verbose, "v", false, "verbose")
|
||||||
cmdPack.Flag = *fs
|
cmdPack.Flag = *fs
|
||||||
cmdPack.Run = packApp
|
cmdPack.Run = packApp
|
||||||
|
w = NewColorWriter(os.Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exitPrint(con string) {
|
func exitPrint(con string) {
|
||||||
@ -198,6 +201,7 @@ func (wft *walkFileTree) virPath(fpath string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
name = name[1:]
|
name = name[1:]
|
||||||
|
name = path.ToSlash(name)
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,13 +244,12 @@ func (wft *walkFileTree) walkLeaf(fpath string, fi os.FileInfo, err error) error
|
|||||||
|
|
||||||
if added, err := wft.wak.compress(name, fpath, fi); added {
|
if added, err := wft.wak.compress(name, fpath, fi); added {
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Printf("Compressed: %s\n", name)
|
fmt.Fprintf(w, "\t%s%scompressed%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", name, "\x1b[0m")
|
||||||
}
|
}
|
||||||
wft.allfiles[name] = true
|
wft.allfiles[name] = true
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wft *walkFileTree) iterDirectory(fpath string, fi os.FileInfo) error {
|
func (wft *walkFileTree) iterDirectory(fpath string, fi os.FileInfo) error {
|
||||||
@ -337,7 +340,7 @@ func (wft *tarWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
defer fr.Close()
|
defer CloseFile(fr)
|
||||||
_, err = io.Copy(tw, fr)
|
_, err = io.Copy(tw, fr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -373,7 +376,7 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
defer fr.Close()
|
defer CloseFile(fr)
|
||||||
_, err = io.Copy(w, fr)
|
_, err = io.Copy(w, fr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -395,10 +398,10 @@ func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
|
|||||||
func packDirectory(excludePrefix []string, excludeSuffix []string,
|
func packDirectory(excludePrefix []string, excludeSuffix []string,
|
||||||
excludeRegexp []*regexp.Regexp, includePath ...string) (err error) {
|
excludeRegexp []*regexp.Regexp, includePath ...string) (err error) {
|
||||||
|
|
||||||
fmt.Printf("exclude relpath prefix: %s\n", strings.Join(excludePrefix, ":"))
|
ColorLog("Excluding relpath prefix: %s\n", strings.Join(excludePrefix, ":"))
|
||||||
fmt.Printf("exclude relpath suffix: %s\n", strings.Join(excludeSuffix, ":"))
|
ColorLog("Excluding relpath suffix: %s\n", strings.Join(excludeSuffix, ":"))
|
||||||
if len(excludeRegexp) > 0 {
|
if len(excludeRegexp) > 0 {
|
||||||
fmt.Printf("exclude filename regex: `%s`\n", strings.Join(excludeR, "`, `"))
|
ColorLog("Excluding filename regex: `%s`\n", strings.Join(excludeR, "`, `"))
|
||||||
}
|
}
|
||||||
|
|
||||||
w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||||
@ -454,7 +457,7 @@ func packDirectory(excludePrefix []string, excludeSuffix []string,
|
|||||||
func isBeegoProject(thePath string) bool {
|
func isBeegoProject(thePath string) bool {
|
||||||
fh, _ := os.Open(thePath)
|
fh, _ := os.Open(thePath)
|
||||||
fis, _ := fh.Readdir(-1)
|
fis, _ := fh.Readdir(-1)
|
||||||
regex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?"github.com/astaxie/beego".*?\).*func main()`)
|
regex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`)
|
||||||
for _, fi := range fis {
|
for _, fi := range fis {
|
||||||
if fi.IsDir() == false && strings.HasSuffix(fi.Name(), ".go") {
|
if fi.IsDir() == false && strings.HasSuffix(fi.Name(), ".go") {
|
||||||
data, err := ioutil.ReadFile(path.Join(thePath, fi.Name()))
|
data, err := ioutil.ReadFile(path.Join(thePath, fi.Name()))
|
||||||
@ -470,6 +473,8 @@ func isBeegoProject(thePath string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func packApp(cmd *Command, args []string) int {
|
func packApp(cmd *Command, args []string) int {
|
||||||
|
ShowShortVersionBanner()
|
||||||
|
|
||||||
curPath, _ := os.Getwd()
|
curPath, _ := os.Getwd()
|
||||||
thePath := ""
|
thePath := ""
|
||||||
|
|
||||||
@ -491,17 +496,17 @@ func packApp(cmd *Command, args []string) int {
|
|||||||
|
|
||||||
thePath, err := path.Abs(appPath)
|
thePath, err := path.Abs(appPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exitPrint(fmt.Sprintf("wrong app path: %s", thePath))
|
exitPrint(fmt.Sprintf("Wrong app path: %s", thePath))
|
||||||
}
|
}
|
||||||
if stat, err := os.Stat(thePath); os.IsNotExist(err) || stat.IsDir() == false {
|
if stat, err := os.Stat(thePath); os.IsNotExist(err) || stat.IsDir() == false {
|
||||||
exitPrint(fmt.Sprintf("not exist app path: %s", thePath))
|
exitPrint(fmt.Sprintf("App path does not exist: %s", thePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
if isBeegoProject(thePath) == false {
|
if isBeegoProject(thePath) == false {
|
||||||
exitPrint(fmt.Sprintf("not support non beego project"))
|
exitPrint(fmt.Sprintf("Bee does not support non Beego project"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("app path: %s\n", thePath)
|
ColorLog("Packaging application: %s\n", thePath)
|
||||||
|
|
||||||
appName := path.Base(thePath)
|
appName := path.Base(thePath)
|
||||||
|
|
||||||
@ -516,14 +521,12 @@ func packApp(cmd *Command, args []string) int {
|
|||||||
|
|
||||||
str := strconv.FormatInt(time.Now().UnixNano(), 10)[9:]
|
str := strconv.FormatInt(time.Now().UnixNano(), 10)[9:]
|
||||||
|
|
||||||
gobin := path.Join(runtime.GOROOT(), "bin", "go")
|
|
||||||
tmpdir := path.Join(os.TempDir(), "beePack-"+str)
|
tmpdir := path.Join(os.TempDir(), "beePack-"+str)
|
||||||
|
|
||||||
os.Mkdir(tmpdir, 0700)
|
os.Mkdir(tmpdir, 0700)
|
||||||
|
|
||||||
if build {
|
if build {
|
||||||
fmt.Println("build", appName)
|
ColorLog("Building application...\n")
|
||||||
|
|
||||||
var envs []string
|
var envs []string
|
||||||
for _, env := range buildEnvs {
|
for _, env := range buildEnvs {
|
||||||
parts := strings.SplitN(env, "=", 2)
|
parts := strings.SplitN(env, "=", 2)
|
||||||
@ -545,7 +548,7 @@ func packApp(cmd *Command, args []string) int {
|
|||||||
os.Setenv("GOOS", goos)
|
os.Setenv("GOOS", goos)
|
||||||
os.Setenv("GOARCH", goarch)
|
os.Setenv("GOARCH", goarch)
|
||||||
|
|
||||||
fmt.Println("GOOS", goos, "GOARCH", goarch)
|
ColorLog("Env: GOOS=%s GOARCH=%s\n", goos, goarch)
|
||||||
|
|
||||||
binPath := path.Join(tmpdir, appName)
|
binPath := path.Join(tmpdir, appName)
|
||||||
if goos == "windows" {
|
if goos == "windows" {
|
||||||
@ -558,10 +561,10 @@ func packApp(cmd *Command, args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Println(gobin, " ", strings.Join(args, " "))
|
fmt.Fprintf(w, "\t%s%s+ go %s%s%s\n", "\x1b[32m", "\x1b[1m", strings.Join(args, " "), "\x1b[21m", "\x1b[0m")
|
||||||
}
|
}
|
||||||
|
|
||||||
execmd := exec.Command(gobin, args...)
|
execmd := exec.Command("go", args...)
|
||||||
execmd.Env = append(os.Environ(), envs...)
|
execmd.Env = append(os.Environ(), envs...)
|
||||||
execmd.Stdout = os.Stdout
|
execmd.Stdout = os.Stdout
|
||||||
execmd.Stderr = os.Stderr
|
execmd.Stderr = os.Stderr
|
||||||
@ -571,7 +574,7 @@ func packApp(cmd *Command, args []string) int {
|
|||||||
exitPrint(err.Error())
|
exitPrint(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("build success")
|
ColorLog("Build successful\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch format {
|
switch format {
|
||||||
@ -623,6 +626,6 @@ func packApp(cmd *Command, args []string) int {
|
|||||||
exitPrint(err.Error())
|
exitPrint(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("file write to `%s`\n", outputP)
|
ColorLog("Writing to output: `%s`\n", outputP)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
169
run.go
169
run.go
@ -15,6 +15,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
path "path/filepath"
|
path "path/filepath"
|
||||||
@ -23,52 +24,94 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var cmdRun = &Command{
|
var cmdRun = &Command{
|
||||||
UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true]",
|
UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]",
|
||||||
Short: "run the app which can hot compile",
|
Short: "run the app and start a Web server for development",
|
||||||
Long: `
|
Long: `
|
||||||
start the appname throw exec.Command
|
Run command will supervise the file system of the beego project using inotify,
|
||||||
|
it will recompile and restart the app after any modifications.
|
||||||
|
|
||||||
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 mainFiles ListOpts
|
var (
|
||||||
|
mainFiles ListOpts
|
||||||
var downdoc docValue
|
downdoc docValue
|
||||||
var gendoc docValue
|
gendoc docValue
|
||||||
|
// The flags list of the paths excluded from watching
|
||||||
|
excludedPaths strFlags
|
||||||
|
// Pass through to -tags arg of "go build"
|
||||||
|
buildTags string
|
||||||
|
// Application path
|
||||||
|
currpath string
|
||||||
|
// Application name
|
||||||
|
appname string
|
||||||
|
// Channel to signal an Exit
|
||||||
|
exit chan bool
|
||||||
|
// Flag to watch the vendor folder
|
||||||
|
vendorWatch bool
|
||||||
|
// Current user workspace
|
||||||
|
currentGoPath string
|
||||||
|
// Current runmode
|
||||||
|
runmode string
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmdRun.Run = runApp
|
cmdRun.Run = runApp
|
||||||
cmdRun.Flag.Var(&mainFiles, "main", "specify main go files")
|
cmdRun.Flag.Var(&mainFiles, "main", "specify main go files")
|
||||||
cmdRun.Flag.Var(&gendoc, "gendoc", "auto generate the docs")
|
cmdRun.Flag.Var(&gendoc, "gendoc", "auto generate the docs")
|
||||||
cmdRun.Flag.Var(&downdoc, "downdoc", "auto download swagger file when not exist")
|
cmdRun.Flag.Var(&downdoc, "downdoc", "auto download swagger file when not exist")
|
||||||
|
cmdRun.Flag.Var(&excludedPaths, "e", "Excluded paths[].")
|
||||||
|
cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Watch vendor folder")
|
||||||
|
cmdRun.Flag.StringVar(&buildTags, "tags", "", "Build tags (https://golang.org/pkg/go/build/)")
|
||||||
|
cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set BEEGO_RUNMODE env variable.")
|
||||||
|
exit = make(chan bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
var appname string
|
|
||||||
|
|
||||||
func runApp(cmd *Command, args []string) int {
|
func runApp(cmd *Command, args []string) int {
|
||||||
exit := make(chan bool)
|
ShowShortVersionBanner()
|
||||||
crupath, _ := os.Getwd()
|
|
||||||
|
|
||||||
if len(args) == 0 || args[0] == "watchall" {
|
if len(args) == 0 || args[0] == "watchall" {
|
||||||
appname = path.Base(crupath)
|
currpath, _ = os.Getwd()
|
||||||
ColorLog("[INFO] Uses '%s' as 'appname'\n", appname)
|
|
||||||
|
if found, _gopath, _ := SearchGOPATHs(currpath); found {
|
||||||
|
appname = path.Base(currpath)
|
||||||
|
currentGoPath = _gopath
|
||||||
} else {
|
} else {
|
||||||
appname = args[0]
|
exitPrint(fmt.Sprintf("Bee does not support non Beego project: %s", currpath))
|
||||||
|
}
|
||||||
|
ColorLog("[INFO] Using '%s' as 'appname'\n", appname)
|
||||||
|
} else {
|
||||||
|
// Check if passed Bee application path/name exists in the GOPATH(s)
|
||||||
|
if found, _gopath, _path := SearchGOPATHs(args[0]); found {
|
||||||
|
currpath = _path
|
||||||
|
currentGoPath = _gopath
|
||||||
|
appname = path.Base(currpath)
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("No Beego application '%s' found in your GOPATH", args[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorLog("[INFO] Using '%s' as 'appname'\n", appname)
|
||||||
|
|
||||||
|
if strings.HasSuffix(appname, ".go") && isExist(currpath) {
|
||||||
|
ColorLog("[WARN] The appname is in conflict with currpath's file, do you want to build appname as %s\n", appname)
|
||||||
|
ColorLog("[INFO] Do you want to overwrite it? [yes|no]] ")
|
||||||
|
if !askForConfirmation() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debugf("current path:%s\n", currpath)
|
||||||
|
|
||||||
|
if runmode == "prod" || runmode == "dev"{
|
||||||
|
os.Setenv("BEEGO_RUNMODE", runmode)
|
||||||
|
ColorLog("[INFO] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE"))
|
||||||
|
}else if runmode != ""{
|
||||||
|
os.Setenv("BEEGO_RUNMODE", runmode)
|
||||||
|
ColorLog("[WARN] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE"))
|
||||||
|
}else if os.Getenv("BEEGO_RUNMODE") != ""{
|
||||||
|
ColorLog("[WARN] Using '%s' as 'runmode'\n", os.Getenv("BEEGO_RUNMODE"))
|
||||||
}
|
}
|
||||||
Debugf("current path:%s\n", crupath)
|
|
||||||
|
|
||||||
err := loadConfig()
|
err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -76,19 +119,12 @@ func runApp(cmd *Command, args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var paths []string
|
var paths []string
|
||||||
|
readAppDirectories(currpath, &paths)
|
||||||
readAppDirectories(crupath, &paths)
|
|
||||||
|
|
||||||
// Because monitor files has some issues, we watch current directory
|
// Because monitor files has some issues, we watch current directory
|
||||||
// and ignore non-go files.
|
// and ignore non-go files.
|
||||||
gps := GetGOPATHs()
|
|
||||||
if len(gps) == 0 {
|
|
||||||
ColorLog("[ERRO] Fail to start[ %s ]\n", "$GOPATH is not set or empty")
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
gopath := gps[0]
|
|
||||||
for _, p := range conf.DirStruct.Others {
|
for _, p := range conf.DirStruct.Others {
|
||||||
paths = append(paths, strings.Replace(p, "$GOPATH", gopath, -1))
|
paths = append(paths, strings.Replace(p, "$GOPATH", currentGoPath, -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
files := []string{}
|
files := []string{}
|
||||||
@ -97,7 +133,14 @@ func runApp(cmd *Command, args []string) int {
|
|||||||
files = append(files, arg)
|
files = append(files, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if downdoc == "true" {
|
||||||
|
if _, err := os.Stat(path.Join(currpath, "swagger", "index.html")); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
downloadFromURL(swaggerlink, "swagger.zip")
|
||||||
|
unzipAndDelete("swagger.zip")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if gendoc == "true" {
|
if gendoc == "true" {
|
||||||
NewWatcher(paths, files, true)
|
NewWatcher(paths, files, true)
|
||||||
Autobuild(files, true)
|
Autobuild(files, true)
|
||||||
@ -105,21 +148,13 @@ func runApp(cmd *Command, args []string) int {
|
|||||||
NewWatcher(paths, files, false)
|
NewWatcher(paths, files, false)
|
||||||
Autobuild(files, false)
|
Autobuild(files, false)
|
||||||
}
|
}
|
||||||
if downdoc == "true" {
|
|
||||||
if _, err := os.Stat(path.Join(crupath, "swagger")); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
downloadFromUrl(swaggerlink, "swagger.zip")
|
|
||||||
unzipAndDelete("swagger.zip", "swagger")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-exit:
|
case <-exit:
|
||||||
runtime.Goexit()
|
runtime.Goexit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAppDirectories(directory string, paths *[]string) {
|
func readAppDirectories(directory string, paths *[]string) {
|
||||||
@ -128,25 +163,57 @@ func readAppDirectories(directory string, paths *[]string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
useDiectory := false
|
useDirectory := false
|
||||||
for _, fileInfo := range fileInfos {
|
for _, fileInfo := range fileInfos {
|
||||||
if strings.HasSuffix(fileInfo.Name(), "docs") {
|
if strings.HasSuffix(fileInfo.Name(), "docs") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if strings.HasSuffix(fileInfo.Name(), "swagger") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !vendorWatch && strings.HasSuffix(fileInfo.Name(), "vendor") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isExcluded(path.Join(directory, fileInfo.Name())) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if fileInfo.IsDir() == true && fileInfo.Name()[0] != '.' {
|
if fileInfo.IsDir() == true && fileInfo.Name()[0] != '.' {
|
||||||
readAppDirectories(directory+"/"+fileInfo.Name(), paths)
|
readAppDirectories(directory+"/"+fileInfo.Name(), paths)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if useDiectory == true {
|
if useDirectory == true {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if path.Ext(fileInfo.Name()) == ".go" {
|
if path.Ext(fileInfo.Name()) == ".go" {
|
||||||
*paths = append(*paths, directory)
|
*paths = append(*paths, directory)
|
||||||
useDiectory = true
|
useDirectory = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a file is excluded
|
||||||
|
func isExcluded(filePath string) bool {
|
||||||
|
for _, p := range excludedPaths {
|
||||||
|
absP, err := path.Abs(p)
|
||||||
|
if err != nil {
|
||||||
|
ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", p)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
absFilePath, err := path.Abs(filePath)
|
||||||
|
if err != nil {
|
||||||
|
ColorLog("[ERROR] Can not get absolute path of [ %s ]\n", filePath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(absFilePath, absP) {
|
||||||
|
ColorLog("[INFO] Excluding from watching [ %s ]\n", filePath)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
56
rundocs.go
56
rundocs.go
@ -20,7 +20,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdRundocs = &Command{
|
var cmdRundocs = &Command{
|
||||||
@ -33,8 +33,9 @@ var cmdRundocs = &Command{
|
|||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
var (
|
||||||
swaggerlink = "https://github.com/beego/swagger/archive/v1.zip"
|
swaggerVersion = "2"
|
||||||
|
swaggerlink = "https://github.com/beego/swagger/archive/v" + swaggerVersion + ".zip"
|
||||||
)
|
)
|
||||||
|
|
||||||
type docValue string
|
type docValue string
|
||||||
@ -59,8 +60,8 @@ func init() {
|
|||||||
|
|
||||||
func runDocs(cmd *Command, args []string) int {
|
func runDocs(cmd *Command, args []string) int {
|
||||||
if isDownload == "true" {
|
if isDownload == "true" {
|
||||||
downloadFromUrl(swaggerlink, "swagger.zip")
|
downloadFromURL(swaggerlink, "swagger.zip")
|
||||||
err := unzipAndDelete("swagger.zip", "swagger")
|
err := unzipAndDelete("swagger.zip")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("has err exet unzipAndDelete", err)
|
fmt.Println("has err exet unzipAndDelete", err)
|
||||||
}
|
}
|
||||||
@ -77,40 +78,50 @@ func runDocs(cmd *Command, args []string) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadFromUrl(url, fileName string) {
|
func downloadFromURL(url, fileName string) {
|
||||||
fmt.Println("Downloading", url, "to", fileName)
|
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 {
|
||||||
|
ColorLog("[%s] Filename %s already exist\n", INFO, fileName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if down {
|
||||||
|
ColorLog("[%s]Downloading %s to %s\n", SUCC, url, fileName)
|
||||||
output, err := os.Create(fileName)
|
output, err := os.Create(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error while creating", fileName, "-", err)
|
ColorLog("[%s]Error while creating %s: %s\n", ERRO, fileName, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer output.Close()
|
defer output.Close()
|
||||||
|
|
||||||
response, err := http.Get(url)
|
response, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error while downloading", url, "-", err)
|
ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
|
|
||||||
n, err := io.Copy(output, response.Body)
|
n, err := io.Copy(output, response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error while downloading", url, "-", err)
|
ColorLog("[%s]Error while downloading %s:%s\n", ERRO, url, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ColorLog("[%s] %d bytes downloaded.\n", SUCC, n)
|
||||||
fmt.Println(n, "bytes downloaded.")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func unzipAndDelete(src, dest string) error {
|
func unzipAndDelete(src string) error {
|
||||||
fmt.Println("start to unzip file from " + src + " to " + dest)
|
ColorLog("[%s]start to unzip file from %s\n", INFO, src)
|
||||||
r, err := zip.OpenReader(src)
|
r, err := zip.OpenReader(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
|
rp := strings.NewReplacer("swagger-"+swaggerVersion, "swagger")
|
||||||
for _, f := range r.File {
|
for _, f := range r.File {
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -118,12 +129,12 @@ func unzipAndDelete(src, dest string) error {
|
|||||||
}
|
}
|
||||||
defer rc.Close()
|
defer rc.Close()
|
||||||
|
|
||||||
path := filepath.Join(dest, f.Name)
|
fname := rp.Replace(f.Name)
|
||||||
if f.FileInfo().IsDir() {
|
if f.FileInfo().IsDir() {
|
||||||
os.MkdirAll(path, f.Mode())
|
os.MkdirAll(fname, f.Mode())
|
||||||
} else {
|
} else {
|
||||||
f, err := os.OpenFile(
|
f, err := os.OpenFile(
|
||||||
path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -135,11 +146,6 @@ func unzipAndDelete(src, dest string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ColorLog("[%s]Start delete src file %s\n", INFO, src)
|
||||||
fmt.Println("Start delete src file " + src)
|
return os.RemoveAll(src)
|
||||||
err = os.RemoveAll(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
1
test.go
1
test.go
@ -73,7 +73,6 @@ func testApp(cmd *Command, args []string) int {
|
|||||||
runTest()
|
runTest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTest() {
|
func runTest() {
|
||||||
|
8
testdata/router/router.go
vendored
8
testdata/router/router.go
vendored
@ -8,21 +8,21 @@ type Router struct {
|
|||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Router) Get() {
|
func (r *Router) Get() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Router) Post() {
|
func (r *Router) Post() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Controller) Put() {
|
func (c *Controller) Put() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Controller) Delete() {
|
func (c *Controller) Delete() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
52
util.go
52
util.go
@ -15,13 +15,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"path"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
||||||
@ -152,7 +153,6 @@ func getColorLevel(level string) string {
|
|||||||
default:
|
default:
|
||||||
return level
|
return level
|
||||||
}
|
}
|
||||||
return level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsExist returns whether a file or directory exists.
|
// IsExist returns whether a file or directory exists.
|
||||||
@ -174,6 +174,34 @@ func GetGOPATHs() []string {
|
|||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SearchGOPATHs(app string) (bool, string, string) {
|
||||||
|
gps := GetGOPATHs()
|
||||||
|
if len(gps) == 0 {
|
||||||
|
ColorLog("[ERRO] Fail to start [ %s ]\n", "GOPATH environment variable is not set or empty")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup the application inside the user workspace(s)
|
||||||
|
for _, gopath := range gps {
|
||||||
|
var currentPath string
|
||||||
|
|
||||||
|
if !strings.Contains(app, "src") {
|
||||||
|
gopathsrc := path.Join(gopath, "src")
|
||||||
|
currentPath = path.Join(gopathsrc, app)
|
||||||
|
} else {
|
||||||
|
currentPath = app
|
||||||
|
}
|
||||||
|
|
||||||
|
if isExist(currentPath) {
|
||||||
|
if !isBeegoProject(currentPath) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return true, gopath, currentPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, "", ""
|
||||||
|
}
|
||||||
|
|
||||||
// askForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and
|
// askForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and
|
||||||
// then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as
|
// then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as
|
||||||
// confirmations. If the input is not recognized, it will ask again. The function does not return
|
// confirmations. If the input is not recognized, it will ask again. The function does not return
|
||||||
@ -247,3 +275,23 @@ func camelString(s string) string {
|
|||||||
}
|
}
|
||||||
return string(data[:len(data)])
|
return string(data[:len(data)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
72
version.go
72
version.go
@ -2,51 +2,81 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
path "path/filepath"
|
path "path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdVersion = &Command{
|
var cmdVersion = &Command{
|
||||||
UsageLine: "version",
|
UsageLine: "version",
|
||||||
Short: "show the bee & beego version",
|
Short: "prints the current Bee version",
|
||||||
Long: `
|
Long: `
|
||||||
show the bee & beego version
|
Prints the current Bee, Beego and Go version alongside the platform information
|
||||||
|
|
||||||
bee version
|
|
||||||
bee: 1.1.1
|
|
||||||
beego: 1.2
|
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const verboseVersionBanner string = `%s%s______
|
||||||
|
| ___ \
|
||||||
|
| |_/ / ___ ___
|
||||||
|
| ___ \ / _ \ / _ \
|
||||||
|
| |_/ /| __/| __/
|
||||||
|
\____/ \___| \___| v{{ .BeeVersion }}%s
|
||||||
|
%s%s
|
||||||
|
├── Beego : {{ .BeegoVersion }}
|
||||||
|
├── GoVersion : {{ .GoVersion }}
|
||||||
|
├── GOOS : {{ .GOOS }}
|
||||||
|
├── GOARCH : {{ .GOARCH }}
|
||||||
|
├── NumCPU : {{ .NumCPU }}
|
||||||
|
├── GOPATH : {{ .GOPATH }}
|
||||||
|
├── GOROOT : {{ .GOROOT }}
|
||||||
|
├── Compiler : {{ .Compiler }}
|
||||||
|
└── Date : {{ Now "Monday, 2 Jan 2006" }}%s
|
||||||
|
`
|
||||||
|
|
||||||
|
const shortVersionBanner = `%s%s______
|
||||||
|
| ___ \
|
||||||
|
| |_/ / ___ ___
|
||||||
|
| ___ \ / _ \ / _ \
|
||||||
|
| |_/ /| __/| __/
|
||||||
|
\____/ \___| \___| v{{ .BeeVersion }}%s
|
||||||
|
`
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmdVersion.Run = versionCmd
|
cmdVersion.Run = versionCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func versionCmd(cmd *Command, args []string) int {
|
func versionCmd(cmd *Command, args []string) int {
|
||||||
fmt.Println("bee :" + version)
|
ShowVerboseVersionBanner()
|
||||||
fmt.Println("beego :" + getbeegoVersion())
|
|
||||||
//fmt.Println("Go :" + runtime.Version())
|
|
||||||
goversion, err := exec.Command("go", "version").Output()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println("Go :" + string(goversion))
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func getbeegoVersion() string {
|
// ShowVerboseVersionBanner prints the verbose version banner
|
||||||
|
func ShowVerboseVersionBanner() {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
coloredBanner := fmt.Sprintf(verboseVersionBanner, "\x1b[35m", "\x1b[1m", "\x1b[0m",
|
||||||
|
"\x1b[32m", "\x1b[1m", "\x1b[0m")
|
||||||
|
InitBanner(w, bytes.NewBufferString(coloredBanner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowShortVersionBanner prints the short version banner
|
||||||
|
func ShowShortVersionBanner() {
|
||||||
|
w := NewColorWriter(os.Stdout)
|
||||||
|
coloredBanner := fmt.Sprintf(shortVersionBanner, "\x1b[35m", "\x1b[1m", "\x1b[0m")
|
||||||
|
InitBanner(w, bytes.NewBufferString(coloredBanner))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBeegoVersion() string {
|
||||||
gopath := os.Getenv("GOPATH")
|
gopath := os.Getenv("GOPATH")
|
||||||
re, err := regexp.Compile(`const VERSION = "([0-9.]+)"`)
|
re, err := regexp.Compile(`VERSION = "([0-9.]+)"`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if gopath == "" {
|
if gopath == "" {
|
||||||
err = fmt.Errorf("you should set GOPATH in the env")
|
err = fmt.Errorf("You should set GOPATH env variable")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
wgopath := path.SplitList(gopath)
|
wgopath := path.SplitList(gopath)
|
||||||
@ -58,11 +88,11 @@ func getbeegoVersion() string {
|
|||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ColorLog("[ERRO] get beego.go has error\n")
|
ColorLog("[ERRO] Get `beego.go` has error\n")
|
||||||
}
|
}
|
||||||
fd, err := os.Open(filename)
|
fd, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ColorLog("[ERRO] open beego.go has error\n")
|
ColorLog("[ERRO] Open `beego.go` has error\n")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
reader := bufio.NewReader(fd)
|
reader := bufio.NewReader(fd)
|
||||||
@ -82,5 +112,5 @@ func getbeegoVersion() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return "you don't install beego,install first: github.com/astaxie/beego"
|
return "Beego not installed. Please install it first: https://github.com/astaxie/beego"
|
||||||
}
|
}
|
||||||
|
68
watch.go
68
watch.go
@ -17,21 +17,21 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/howeyc/fsnotify"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/howeyc/fsnotify"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
state sync.Mutex
|
state sync.Mutex
|
||||||
eventTime = make(map[string]int64)
|
eventTime = make(map[string]int64)
|
||||||
buildPeriod time.Time
|
scheduleTime time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewWatcher(paths []string, files []string, isgenerate bool) {
|
func NewWatcher(paths []string, files []string, isgenerate bool) {
|
||||||
@ -47,20 +47,14 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
|
|||||||
case e := <-watcher.Event:
|
case e := <-watcher.Event:
|
||||||
isbuild := true
|
isbuild := true
|
||||||
|
|
||||||
// Skip TMP files for Sublime Text.
|
// Skip ignored files
|
||||||
if checkTMPFile(e.Name) {
|
if shouldIgnoreFile(e.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !chekcIfWatchExt(e.Name) {
|
if !checkIfWatchExt(e.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent duplicated builds.
|
|
||||||
if buildPeriod.Add(1 * time.Second).After(time.Now()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buildPeriod = time.Now()
|
|
||||||
|
|
||||||
mt := getFileModTime(e.Name)
|
mt := getFileModTime(e.Name)
|
||||||
if t := eventTime[e.Name]; mt == t {
|
if t := eventTime[e.Name]; mt == t {
|
||||||
ColorLog("[SKIP] # %s #\n", e.String())
|
ColorLog("[SKIP] # %s #\n", e.String())
|
||||||
@ -71,7 +65,19 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
|
|||||||
|
|
||||||
if isbuild {
|
if isbuild {
|
||||||
ColorLog("[EVEN] %s\n", e)
|
ColorLog("[EVEN] %s\n", e)
|
||||||
go Autobuild(files, isgenerate)
|
go func() {
|
||||||
|
// Wait 1s before autobuild util there is no file change.
|
||||||
|
scheduleTime = time.Now().Add(1 * time.Second)
|
||||||
|
for {
|
||||||
|
time.Sleep(scheduleTime.Sub(time.Now()))
|
||||||
|
if time.Now().After(scheduleTime) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Autobuild(files, isgenerate)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
case err := <-watcher.Error:
|
case err := <-watcher.Error:
|
||||||
ColorLog("[WARN] %s\n", err.Error()) // No need to exit here
|
ColorLog("[WARN] %s\n", err.Error()) // No need to exit here
|
||||||
@ -115,8 +121,8 @@ func Autobuild(files []string, isgenerate bool) {
|
|||||||
defer state.Unlock()
|
defer state.Unlock()
|
||||||
|
|
||||||
ColorLog("[INFO] Start building...\n")
|
ColorLog("[INFO] Start building...\n")
|
||||||
path, _ := os.Getwd()
|
|
||||||
os.Chdir(path)
|
os.Chdir(currpath)
|
||||||
|
|
||||||
cmdName := "go"
|
cmdName := "go"
|
||||||
if conf.Gopm.Enable {
|
if conf.Gopm.Enable {
|
||||||
@ -130,6 +136,7 @@ func Autobuild(files []string, isgenerate bool) {
|
|||||||
icmd := exec.Command("go", "list", "./...")
|
icmd := exec.Command("go", "list", "./...")
|
||||||
buf := bytes.NewBuffer([]byte(""))
|
buf := bytes.NewBuffer([]byte(""))
|
||||||
icmd.Stdout = buf
|
icmd.Stdout = buf
|
||||||
|
icmd.Env = append(os.Environ(), "GOGC=off")
|
||||||
err = icmd.Run()
|
err = icmd.Run()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
list := strings.Split(buf.String(), "\n")[1:]
|
list := strings.Split(buf.String(), "\n")[1:]
|
||||||
@ -140,6 +147,7 @@ func Autobuild(files []string, isgenerate bool) {
|
|||||||
icmd = exec.Command(cmdName, "install", pkg)
|
icmd = exec.Command(cmdName, "install", pkg)
|
||||||
icmd.Stdout = os.Stdout
|
icmd.Stdout = os.Stdout
|
||||||
icmd.Stderr = os.Stderr
|
icmd.Stderr = os.Stderr
|
||||||
|
icmd.Env = append(os.Environ(), "GOGC=off")
|
||||||
err = icmd.Run()
|
err = icmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
@ -150,6 +158,7 @@ func Autobuild(files []string, isgenerate bool) {
|
|||||||
|
|
||||||
if isgenerate {
|
if isgenerate {
|
||||||
icmd := exec.Command("bee", "generate", "docs")
|
icmd := exec.Command("bee", "generate", "docs")
|
||||||
|
icmd.Env = append(os.Environ(), "GOGC=off")
|
||||||
icmd.Stdout = os.Stdout
|
icmd.Stdout = os.Stdout
|
||||||
icmd.Stderr = os.Stderr
|
icmd.Stderr = os.Stderr
|
||||||
icmd.Run()
|
icmd.Run()
|
||||||
@ -164,9 +173,13 @@ func Autobuild(files []string, isgenerate bool) {
|
|||||||
|
|
||||||
args := []string{"build"}
|
args := []string{"build"}
|
||||||
args = append(args, "-o", appName)
|
args = append(args, "-o", appName)
|
||||||
|
if buildTags != "" {
|
||||||
|
args = append(args, "-tags", buildTags)
|
||||||
|
}
|
||||||
args = append(args, files...)
|
args = append(args, files...)
|
||||||
|
|
||||||
bcmd := exec.Command(cmdName, args...)
|
bcmd := exec.Command(cmdName, args...)
|
||||||
|
bcmd.Env = append(os.Environ(), "GOGC=off")
|
||||||
bcmd.Stdout = os.Stdout
|
bcmd.Stdout = os.Stdout
|
||||||
bcmd.Stderr = os.Stderr
|
bcmd.Stderr = os.Stderr
|
||||||
err = bcmd.Run()
|
err = bcmd.Run()
|
||||||
@ -217,18 +230,33 @@ func Start(appname string) {
|
|||||||
started <- true
|
started <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkTMPFile returns true if the event was for TMP files.
|
// Should ignore filenames generated by
|
||||||
func checkTMPFile(name string) bool {
|
// Emacs, Vim or SublimeText
|
||||||
if strings.HasSuffix(strings.ToLower(name), ".tmp") {
|
func shouldIgnoreFile(filename string) bool {
|
||||||
|
for _, regex := range ignoredFilesRegExps {
|
||||||
|
r, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
panic("Could not compile the regex: " + regex)
|
||||||
|
}
|
||||||
|
if r.MatchString(filename) {
|
||||||
return true
|
return true
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var watchExts = []string{".go"}
|
var watchExts = []string{".go"}
|
||||||
|
var ignoredFilesRegExps = []string{
|
||||||
|
`.#(\w+).go`,
|
||||||
|
`.(\w+).go.swp`,
|
||||||
|
`(\w+).go~`,
|
||||||
|
`(\w+).tmp`,
|
||||||
|
}
|
||||||
|
|
||||||
// chekcIfWatchExt returns true if the name HasSuffix <watch_ext>.
|
// checkIfWatchExt returns true if the name HasSuffix <watch_ext>.
|
||||||
func chekcIfWatchExt(name string) bool {
|
func checkIfWatchExt(name string) bool {
|
||||||
for _, s := range watchExts {
|
for _, s := range watchExts {
|
||||||
if strings.HasSuffix(name, s) {
|
if strings.HasSuffix(name, s) {
|
||||||
return true
|
return true
|
||||||
|
Loading…
Reference in New Issue
Block a user