読者です 読者をやめる 読者になる 読者になる

DRYな備忘録

Don't Repeat Yourself.

(訳しながら)つくって覚えるRevelフレームワーク - その3

go

第3回

きっと何者にもなれない僕は、Go言語のウェブフレームワークであるRevelのドキュメントを和訳しつつ、理解を深めたいでござる。

前回 : (訳しながら)つくって覚えるRevelフレームワーク - その2 - DRYな備忘録

今回は

Introduction のチュートリアルを見ながら、簡単なアプリを一つ作る。

(´-`).。oO( 原文載せるの疲れた...

Introduction

このチュートリアルではクソシンプルなアプリをひとつ作っていきます。

  • Getting started - Revelのインストールと起動までします
  • Creating a new Revel app - Revelアプリのスケルトンを作ります
  • The request flow - リクエストをハンドリングする段階を順に見ていきます
  • Implementing the Hello World app - ちっちゃいアプリを作ってみましょう

コマンドラインインターフェースはOSXまたはLinuxを想定していますが、Windowsでも問題なく動作するはずです。

Getting started

Getting Started

ここでは、GoとRevelのインストール手順を紹介します。

/* 割愛しまーす!ここが参考になるかも
VPSをDebian7.1wheezyにしたのでサーバセットアップのログを取っておこうと思ったわけ 2
*/

最後に、動作を確認してみましょう

$ revel help
~
~ revel! http://robfig.github.com/revel
~
usage: revel command [arguments]

The commands are:

    run         run a Revel application
    new         create a skeleton Revel application
    clean       clean a Revel application\'s temp files
    package     package a Revel application (e.g. for deployment)

Use "revel help [command]" for more information.

これで全て整いました。

Creating a new Revel app

Creating a new Revel application

Revelのコマンドラインツールを使って$GOPATHにアプリケーションを作って起動してみます

$ cd $GOPATH

$ revel new myapp
~
~ revel! http://robfig.github.com/revel
~
Your application is ready:
    /Users/robfig/code/gocode/src/myapp

You can run it with:
    revel run myapp

$ revel run myapp
~
~ revel! http://robfig.github.com/revel
~
2012/09/27 17:01:54 run.go:41: Running myapp (myapp) in dev mode
2012/09/27 17:01:54 harness.go:112: Listening on :9000

ブラウザで http://localhost:9000/ にアクセスすると、アプリが動いているはすです pic 作られたアプリケーションの詳細はここで説明されています。

The Request Flow

前述のセクションではmyappという名前でRevelアプリを作りました。このセクションではRevelがhttp://localhost:9000へのHTTPリクエストを受け取り、結果を表示するまでのフローを追っていきたいと思います。

Routes

まず真っ先に、Revelはconf/routesを参照します。revel newで自動的に生成されたroutesファイルに、以下のrouteが定義されています

GET     /                                       App.Index

この表記は

  • /
  • GETでリクエストが来た場合
  • Appコントローラの
  • Indexメソッドを呼ぶ

ということを意味しています。

Actions

app/controllers/app.goの該当コードを見ていきましょう。

package controllers

import "github.com/robfig/revel"

type App struct {
    *revel.Controller
}

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

すべてのコントローラーは(直接的/間接的に)一番上に*revel.Controllerを持つ構造である必要があります。コントローラーのメソッドで、外部にpublicであり、返り値の型がrevel.Resultであるものは全てActionとして扱われます。

RevelのコントローラーにはResultを生成する便利なメソッドが多数備わっています。上記の例の中ではRender()メソッドを用いています。これは、自動的にテンプレートを割り当て200OKのレスポンスを返すメソッドです。

Templates

全てのテンプレートファイルはapp/viewsディレクトリ以下に配置してください。明示的にテンプレート名が与えられない場合、アクションの名前から動的にテンプレートを割り当てようとします。今回の例でも、明示的にテンプレート名が与えられていないので、App.Indexというアクション名からapp/views/App/Index.htmlというファイルをGoTemplateに割り当てています。

{{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" .}}

GoTemplateが提供する関数以外にも、いくつかのRevel独自のヘルパーがあります。

上記のテンプレートは非常にシンプルな例です

  • レンダリング変数に新しくtitleを追加している
  • header.htmlテンプレートを(titleをバインドして)インクルードしている
  • ウェルカムメッセージを表示している
  • flash.htmlをインクルードし、メッセージがあれば表示している
  • footer.htmlをインクルードしている

また、header.htmlでは、他の表記方法も見ることができます。

<!DOCTYPE html>
<html>
  <head>
    <title>{{.title}}</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link rel="stylesheet" type="text/css" media="screen" href="/public/stylesheets/main.css">
    <link rel="shortcut icon" type="image/png" href="/public/images/favicon.png">
    <script src="/public/javascripts/jquery-1.5.2.min.js" type="text/javascript" charset="utf-8"></script>
    {{range .moreStyles}}
      <link rel="stylesheet" type="text/css" href="/public/{{.}}">
    {{end}}
    {{range .moreScripts}}
      <script src="/public/{{.}}" type="text/javascript" charset="utf-8"></script>
    {{end}}
  </head>
  <body>

titleという変数がセットされていることがわかります。また、呼び元のテンプレートでmoreStylesmoreScripts変数をバインドしていれば、ここでjsファイルやcssファイルが読み込まれるます。

{{append . "moreScripts" "src/myapp.js"}}

Hot-reload

ここでおもむろん、ウェルカムメッセージを変えてみましょう!Index.htmlの、以下の箇所を編集します。

<h1>It works!</h1>

<h1>Hello World</h1>

このように。

ブラウザのページをリロードしてみてください。コンパイルしていないのにウェルカムメッセージが変わっていることが確認できるはずです!サーバのコンソールでは「ソースが編集されたのでリロードされた」という旨のメッセージが表示されていると思います。

Revelは以下のロケーションのファイルを監視しています。

  • app/以下の全ての.goファイル
  • app/views/以下の全てのtemplateファイル
  • conf/routesファイル

これらのファイルが編集されると、最新のコードをコンパイルしてアプリケーションが更新されます。

今度はリアルタイムにエラーが出る様子を試してみましょう。app/controllers/app.goを開いて以下の編集をします。

return c.Render()

return c.Renderx()

このように存在しないメソッドを呼んでみます。

すぐさまブラウザのページをリロードすると、良い感じのエラーメッセージが表示されているはずです。

pic

では最後に、アクションからテンプレートにデータを渡してみましょう。

app/controllers/app.goで、

return c.Renderx()

greeting := "Aloha World"
return c.Render(greeting)

このように編集します。

またapp/views/App/Index.htmlにおいて

<h1>Hello World</h1>

の部分を

<h1>{{.greeting}}</h1>

このように編集します。

これにより、アクションでgreetingという名前の変数をテンプレートにおいて.greetingで参照できるようになったハズです。

それでは、ブラウザのページをリロードしてハワイアンな気分を味わいましょう!

pic

The "Hello World" app

このセクションでは"Play Framework"と同じような"HelloWorld"アプリケーションをつくっていきたいと思います。先ほど作ったmyappプロジェクトのコードを使います。それでは始めましょう。

app/views/App/Index.htmlを編集して、flash.htmlをインクルードしているすぐ下に以下のフォームを追加します。

<form action="/App/Hello" method="GET">
    <input type="text" name="myName" /><br/>
    <input type="submit" value="Say hello!" />
</form>

ページをリロードすると pic

とりあえずこのフォームを送信してみましょう。すると...

pic

/App/Helloに対応するアクションを書いてないので当然ですね。新しいアクション定義をapp/controllers/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" .}}

改めてブラウザをリロードすればこのアクションとテンプレートがレンダリングされているのを確認できるはずです。 pic

ではこれで最後です。フォーム値のバリデーションを追加し、エラーメッセージを表示してみます。

「名前」は空ではいけないし、最低でも3文字は欲しいところです。これをコントローラのバリデーションモジュールで実現してみましょう。app/controllers/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)
}

これで、有効な名前文字列を入力していない場合には、このコントローラのIndex()メソッドに戻されるようになりました。この名前とバリデーションのエラーはFlash(1リクエストの間だけ有効なcookie)に保存されます。

既存のflash.htmlは、エラーやflash messageがある場合にそれを表示します。

{{if .flash.success}}
<div class="alert alert-success">
    {{.flash.success}}
</div>
{{end}}

{{if or .errors .flash.error}}
<div class="alert alert-error">
    {{if .flash.error}}
        {{.flash.error}}
    {{end}}
    {{if .errors}}
    <ul style="margin-top:10px;">
        {{range .errors}}
            <li>{{.}}</li>
        {{end}}
    </ul>
    {{end}}
</div>
{{end}}

ためしに1文字だけの名前を送信してみましょう。 pic いいですね!適切なエラーメッセージが表示され、入力値はそのままなのが分かります。

おめでとうございます。これで"HelloWorld"アプリケーションの完成です。

とりあえず

次回は、Introductionあたりで、設計や思想について学んでみたいと思ってます。

しかし、

英語の勉強にもなるし、すごくソフトウェアの勉強になるんだけど、この辺はただのチュートリアルだし、やったことあるし、他のフーレムワークとかと大差無いし、ぶっちゃけつまんなかったっす。