Revelの主な処理の流れ

Go製フレームワーク[Revel](http://revel.github.io/)の処理の流れです。
ざっくりとした処理の流れの話です。

以下、社内勉強会用資料。

# revelの主な処理の流れ

![RevelDesign.png](https://qiita-image-store.s3.amazonaws.com/0/3163/c203f738-315f-e9f1-ceb3-0afa73c6e02b.png)
http://revel.github.io/manual/concepts.html

Play Frameworkを参考に作っている。
基本的にはMVC。

# revel アプリの作成方法

## revel アプリケーションの作成

```sh
$ revel new sample
~
~ revel! http://revel.github.io
~
Your application is ready:
   /Users/username/.go/src/sample

You can run it with:
   revel run sample
```

`$GOPATH/src/sample` のファイル構成

```bash
.
├── app
│   ├── controllers
│   │   └── app.go
│   ├── init.go
│   └── views
│       ├── App
│       │   └── Index.html
│       ├── debug.html
│       ├── errors
│       │   ├── 404.html
│       │   └── 500.html
│       ├── flash.html
│       ├── footer.html
│       └── header.html
├── conf
│   ├── app.conf
│   └── routes
├── messages
│   └── sample.en
├── public
│   ├── css
│   │   └── bootstrap.css
│   ├── img
│   │   ├── favicon.png
│   │   ├── glyphicons-halflings-white.png
│   │   └── glyphicons-halflings.png
│   └── js
│       └── jquery-1.9.1.min.js
└── tests
    └── apptest.go
```

## revel の実行

```sh
$ revel run sample
```

起動してアクセスをすると、`app/routes/routes.go` と `app/tmp/main.go` にファイルが作られている。

```bash
.
├── app
│   ├── controllers
│   │   └── app.go
│   ├── init.go
│   ├── routes # ここと
│   │   └── routes.go
│   ├── tmp    # ここが新しい
│   │   └── main.go
│   └── views
# 省略
```

# revel の内部構造
 
![revel.png](https://qiita-image-store.s3.amazonaws.com/0/3163/2315c15d-b2aa-367d-d522-0e13e53ed0c1.png)

https://github.com/hirokidaichi/goviz で出力

## 各構成

* `main`: コマンドツール
* `harness`: アプリケーションのビルドやwatchなどを行う
* `revel`: コア
* `config`: コンフィグ設定
* `websocket`: websocket
* `pathtree`: routerのマッチング `/:userId` のマッチング
* `simpleuuid`: uuid生成
* `fsnotify`: fileのwatchで利用
* `gocolorize`: コンソールに色つけ


# Harness

* ユーザープログラムをパースして、起動コントローラとなるmain.goを生成して、ユーザーアプリケーションサーバーを起動する
* ユーザープログラムのbuildとrun。コンパイルエラーの表示。
* rebuildやモニタリングもここ経由で表示

## watch

* app側の `config/app.conf` のwatchの状態によって、harness側の挙動が変わる
  * harness用のport開いたり

## build

github.com/revel/revel/harness/build.go 内でビルドしている.

* app/tmp, app/routes/ のクリーンアップ
* アプリケーションに必要な情報の収集(不必要なものを除外)
https://github.com/revel/revel/blob/master/harness/build.go#L40
* db.import が設定されている場合は、それの実行
* **main.go と routes.go** の生成


```go
// Generate two source files.
templateArgs := map[string]interface{}{
     "Controllers":    sourceInfo.ControllerSpecs(),
     "ValidationKeys": sourceInfo.ValidationKeys,
     "ImportPaths":    calcImportAliases(sourceInfo),
     "TestSuites":     sourceInfo.TestSuites(),
}

genSource("tmp", "main.go", MAIN, templateArgs)
genSource("routes", "routes.go", ROUTES, templateArgs)

```

https://github.com/revel/revel/blob/master/harness/build.go#L288 にmain.go。
https://github.com/revel/revel/blob/master/harness/build.go#L341 にroutes.goのテンプレートがある

## main.go作成後

`github.com/revel/revel/harness/app.go` 側からオプションを渡してmain.goを実行

```go:github.com/revel/revel/harness/app.go
// AppCmd manages the running of a Revel app server.
// It requires revel.Init to have been called previously.
type AppCmd struct {
  *exec.Cmd
}

//
// (省略)
//

// Run the app server inline.  Never returns.
func (cmd AppCmd) Run() {
  revel.TRACE.Println("Exec app:", cmd.Path, cmd.Args)
  if err := cmd.Cmd.Run(); err != nil {
    revel.ERROR.Fatalln("Error running:", err)
  }
}
```

# main.go側の処理

`github.com/revel/revel/server.go` に`func Run()` があり、なんだかんだでそれが走る。

リクエストを受け付けると handlerが実行され
revel.Controllerとrevel.Request, revel.Responseが作成されて
各Filtersを実行していく。

## Filters

`revel new`を実行したときのアプリケーションのScaffoldの
`app/init.go` にfiltersが設定されている。

![RevelDesign.png](https://qiita-image-store.s3.amazonaws.com/0/3163/c203f738-315f-e9f1-ceb3-0afa73c6e02b.png)
http://revel.github.io/manual/concepts.html

```go:app/init.go
func init() {
  // Filters is the default set of global filters.
  revel.Filters = []revel.Filter{
    revel.PanicFilter,             // Recover from panics and display an error page instead.
    revel.RouterFilter,            // Use the routing table to select the right Action
    revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
    revel.ParamsFilter,            // Parse parameters into Controller.Params.
    revel.SessionFilter,           // Restore and write the session cookie.
    revel.FlashFilter,             // Restore and write the flash cookie.
    revel.ValidationFilter,        // Restore kept validation errors and save new ones from cookie.
    revel.I18nFilter,              // Resolve the requested language
    HeaderFilter,                  // Add some security based headers
    revel.InterceptorFilter,       // Run interceptors around the action.
    revel.CompressFilter,          // Compress the result.
    revel.ActionInvoker,           // Invoke the action.
  }

  // register startup functions with OnAppStart
  // ( order dependent )
  // revel.OnAppStart(InitDB)
  // revel.OnAppStart(FillCache)
}
```

`revel.ActionInvoker`がコントローラの特定のアクション実行のトリガー。
最終的に`c.Result`にactionの結果が入る(テンプレじゃないよ)

```go:github.com/revel/revel/server.go
func handleInternal(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
  var (
    req  = NewRequest(r)
    resp = NewResponse(w)
    c    = NewController(req, resp)
  )
  req.Websocket = ws

  Filters[0](c, Filters[1:]) // ここで各フィルターが実行 & 最後にアクション実行
  if c.Result != nil {
    c.Result.Apply(req, resp)
  } else if c.Response.Status != 0 {
    c.Response.Out.WriteHeader(c.Response.Status)
  }
  // Close the Writer if we can
  if w, ok := resp.Out.(io.Closer); ok {
    w.Close()
  }
}
``` 

# 結果の整形、ジェネレート

* 返ってくる Resultの型によって、最終的に返すstringを変えている

## Resultのインターフェース
```go
type Result interface {
  Apply(req *Request, resp *Response)
}
```

## Resultsの構造体
```
type ErrorResult struct {
type PlaintextErrorResult struct {
type RenderTemplateResult struct {
type RenderHtmlResult struct {
type RenderJsonResult struct {
type RenderXmlResult struct {
type RenderTextResult struct {
type ContentDisposition string
type BinaryResult struct {
type RedirectToUrlResult struct {
type RedirectToActionResult struct {
```

## RenderTemplateResult の場合

特に何もしないと`RenderTemplateResult`が利用される。
https://github.com/revel/revel/blob/master/results.go#L165 で
templateを使ってhtmlを生成している。

それをResponseにしこんで1リクエストの処理が完了する。

# 話してないこと

* Routerの挙動
* watch時の挙動
* 各Filterの挙動
* ActionInvoker実行時のBinderについて
* Interceptors
* Validation