RevelのTutorialに挑戦してみる

http://revel.github.io/tutorial/index.html
[前回](http://qiita.com/kazusa-qooq/items/d8dd14120198c0915c64)、GolangのWebFramework、Revelの概要を理解したので、今度はチュートリアルに挑戦していきます。なお環境はcloud9です。プロジェクトタイプ：カスタムでGoが使えますが、始めからあるdemo-projectでも問題ありません。

#Introduction
Getting Startedを通してとてもシンプルなRevelについて学びましょう。
・Getting started：Revelの入手と起動
・Creating a new Revel app：スケルトンプロジェクトの作成
・The request flow：別手法でのリクエストハンドリング
・Implementing the Hello World app：Hallo World アプリケーションの作成

#Getting started
http://revel.github.io/tutorial/gettingstarted.html
##Install Go
Revelを使用するにはGoのインストールが必要です。
今回はCloud9を使用するので割愛します。

###Set up your GOPATH
インストールでGOPATHを設定しなかった場合、作成が必要です。GOPATHはコードのトップディレクトリになります。なおCloud9ではデフォルトが'~/workspace'になっています。
１．ディレクトリを作成します。
`~/workspace (master) $ mkdir gocode`
２．GOPATHを設定します。
`export GOPATH=~/workspace/gocode`
３．設定を保存します。
`echo export GOPATH=$GOPATH >> ~/.bashrc`

##Install git and hg
ライブラリを入手する'go get'を利用するにはGitとMercurialが必要です。Cloud9には既に入っているため割愛します。

##Get the Revel framework
Revelを入手するには以下のコマンドを実行します。
`go get github.com/revel/revel`
このコマンドは`$GOPATH/src/github.com/revel/revel/`にリポジトリをコピーし、さらにその依存関係を解決します。なお成功した場合、コマンドライン上には何も出力されません。

##Get and Build the Revel command line tool
Revelアプリケーションをビルド・実行するにはコマンドラインツールが必要です。以下のコマンドで入手します。こちらも同様にコマンドライン上には何も出力されません。
`go get github.com/revel/cmd/revel`
`$GOPATH/bin`にrevelコマンドがインストールされましたのでパスを通します。

```
~/workspace/gocode (master) $ export PATH="$PATH:$GOPATH/bin"
~/workspace/gocode (master) $ revel
~
~ revel! http://revel.github.io
~
usage: revel command [arguments]

The commands are:

    new         create a skeleton Revel application
    run         run a Revel application
    build       build a Revel application (e.g. for deployment)
    package     package a Revel application (e.g. for deployment)
    clean       clean a Revel application's temp files
    test        run all tests from the command-line

Use "revel help [command]" for more information.
~/workspace/gocode (master) $ 
```

#Creating a new Revel application
http://revel.github.io/tutorial/createapp.html
revelコマンド`revel new myapp`を実行しアプリケーションを作成します。

```
~/workspace/gocode (master) $ revel new myapp
~
~ revel! http://revel.github.io
~
Your application is ready:
   /home/ubuntu/workspace/gocode/src/myapp

You can run it with:
   revel run myapp
~/workspace/gocode (master) $ 
```

Cloud９の場合、アドレスとポートを変更する必要があります。

``` src/myapp/conf/app.conf
# The IP address on which to listen.
http.addr = 0.0.0.0

# The port on which to listen.
http.port = 8080
```

`revel run myapp`で作成したアプリケーションを起動します。

```
~/workspace/gocode (master) $ revel run myapp
~
~ revel! http://revel.github.io
~
INFO  2015/04/23 01:23:12 revel.go:329: Loaded module static
INFO  2015/04/23 01:23:12 revel.go:329: Loaded module testrunner
INFO  2015/04/23 01:23:12 revel.go:206: Initialized Revel v0.12.0 (2015-03-25) for >= go1.3
INFO  2015/04/23 01:23:12 run.go:57: Running myapp (myapp) in dev mode
INFO  2015/04/23 01:23:12 harness.go:165: Listening on 0.0.0.0:8080

```

ブラウザでアクセスします。
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/a777ccc3-799a-8446-b9f7-8dba4e2fb6fa.png)

できましたね。

#The Request Flow
http://revel.github.io/tutorial/requestflow.html
この項ではRevelがどのようにリクエストを扱いウェルカムページを表示したかを見ていきます。
##Routes
まず最初に`conf/routes`をチェックします。

```
GET     /     App.Index
```
これはルートパスにアクセスがあった場合、AppコントローラのIndexメソッドを実行することを意味しています。

##Actions
ではAppコントローラを見てみましょう。

```src/myapp/app/controllers/app.go
package controllers

import "github.com/revel/revel"

type App struct {
	*revel.Controller
}

func (c App) Index() revel.Result {
	return c.Render()
}

```
全てのコントローラは`*revel.Controller`を組み込んだstructです。revel.Resultを返すエクスポートされたメソッドはアクションとして使用されます。エクスポートかどうかはメソッド名の頭が大文字かどうかで判定されます。
Revelコントローラは一般的なResultを返すための多くのメソッドを提供しています。この例ではテンプレートを使用した200 OKなResultを返します。なお次項で説明しますが`c.Render()`は対応するテンプレートを自動的に見つけ出します。

##Templates
テンプレートは`app/views`の中にあります。上記の例のようにテンプレートが明示されていない場合、`app/views/<contorller>/<action>.html`が対象となります。この場合だと`app/views/App/Index.html`です。

``` src/myapp/app/views/App/Index.html
{{set . "title" "Home"}}
{{template "header.html" .}}

<header class="hero-unit" style="background-color:#A9F16C">
  <div class="container">
    <div class="row">
      <div class="hero-text">
        <h1>It works!</h1>
        <p></p>
      </div>
    </div>
  </div>
</header>

<div class="container">
  <div class="row">
    <div class="span6">
      {{template "flash.html" .}}
    </div>
  </div>
</div>

{{template "footer.html" .}}
```

GoテンプレートとRevelは有用なヘルパを用意しています。`set`や`template`などがそうです。

##Hot-reload
Revelはファイルの変更を監視し再コンパイルしてくれます。確認するために`It workds!`を`Hello Revel`に変えて保存してみましょう。ブラウザを更新すると次のようになるはずです。
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/af2470f7-30c0-b5c6-68dd-5affe90a7f8d.png)

Revelは次のファイルを監視してます。

* app/以下のgoファイル
* app/views/以下のテンプレート
* conf/routes

ここでコンパイルエラー時の画面表示を確認してみましょう。app.go の　c.Render() を c.Renderx()にして、画面を更新してみましょう。
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/d342e39e-3862-137e-225d-b80ce9cafa8c.png)
このようにわかりやすいメッセージが表示されます。

では次にコントローラからテンプレートに値を渡してみましょう。

```
func (c App) Index() revel.Result {
  greeting := "Aloha World"
  return c.Render(greeting)
}
```

テンプレートでその値を使います。
`<h1>{{.greeting}}</h1>`

画面を更新して確認します。
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/84fe9f7a-9fb4-1c82-90e4-733e5c56efdc.png)

できましたね。

#The 'Hello World' app
http://revel.github.io/tutorial/firstapp.html
ではハローワールドアプリケーションの作成に移りましょう。
flashテンプレートより下に次のフォームを追加します。

```
<form action="/App/Hello" method="GET">
    <input type="text" name="myName" /><br/>
    <input type="submit" value="Say hello!" />
</form>
```
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/60be64fe-2b9e-c972-068b-1b5240785b24.png)

サブミットしてみましょう。エラーになります。
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/f8e605da-e9fb-9968-d274-739e4aa649ff.png)

ではアクションとビューを用意しましょう。

``` app.go
func (c App) Hello(myName string) revel.Result {
    return c.Render(myName)
}
```

``` app/views/App/Hello.html
{{set . "title" "Home"}}
{{template "header.html" .}}

<h1>Hello {{.myName}}</h1>
<a href="/">Back to form</a>

{{template "footer.html" .}}
```
ではもう一度サブミットしてみましょう。入力欄にはRevelと入れています。
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/b8c32b23-10cc-50c8-1107-631813f0b2a7.png)

本文では全く触れてませんが、フォーム送信データとアクションの引数でmyNameがバインディングされています。

ではちょっとしたバリデーションを追加してみましょう。myNameは必須で3文字以上という制限をかけます。

``` app.go
func (c App) Hello(myName string) revel.Result {
    c.Validation.Required(myName).Message("Your name is required!")
    c.Validation.MinSize(myName, 3).Message("Your name is not long enough!")

    if c.Validation.HasErrors() {
        c.Validation.Keep()
        c.FlashParams()
        return c.Redirect(App.Index)
    }

    return c.Render(myName)
}
```

これらのバリデーションエラーはflashテンプレートで表示されます。

現状ですと不正な値を入力した場合、フォームがクリアされていると思います。場合にもよりますが残しておいたほうがいいケースも多々あるでしょう。そのためには次にようにします。

``` index.html
<form action="/App/Hello" method="GET">
    {{with $field := field "myName" .}}
        <input type="text" name="{{$field.Name}}" value="{{$field.Flash}}"/><br/>
    {{end}}
    <input type="submit" value="Say hello!" />
</form>
```
これで誤ったデータを修正可能になりました。
![image](https://qiita-image-store.s3.amazonaws.com/0/71988/6344288a-52e0-28b2-a205-81c07bc00a36.png)


以上でチュートリアルは終わりとなります。
お疲れ様でした。