はじめてのアプリケーション -- ‘Hello World’ チュートリアル
よくある ‘Hello World’ の例を使って Play フレームワークの良さを理解する、とてもシンプルなチュートリアルです。
前提条件
何よりも、まず Java がインストールされていることを確認してください。Play は Java 5 以降 を必要とします。
私たちはコマンドラインを多用するので、Unix ライクな OS のほうが良いでしょう。もし Windows 上で動かす場合でも、問題なく動作します。ただコマンドプロンプトでいくつかのコマンドを実行しなければならないだけです。
もちろんテキストエディタも必要です。もし Eclipse や Netbeans のようなフル機能の Java IDE に慣れているなら、もちろんそれを使用できます。しかし、Play であれば、Textmate, Emacs または VI のようにシンプルなテキストエディタでも楽しく作業できます。これは、フレームワーク自身がコンパイルとデプロイを管理するからです。間もなくその様子をご覧に入れます...
Play フレームワークのインストール
インストールは非常に簡単です。 ダウンロードページから最新のバイナリパッケージをダウンロードして、任意のパスに展開してください。
Windows を使用している場合、パスにスペースを含めないのは一般的に良い案です。例えば、 c:\Play は c:\Documents And Settings\user\play\ よりも良い選択です。
効率的に作業するためには、作業パスに Play ディレクトリを加える必要があります。パスを追加することで、コマンドプロンプトでただ play とタイプすれば、Play ユーティリティを使用できるようになります。正常にインストールが完了したことを確認するには、新しいコマンドラインを開いて play とタイプしてください; このコマンドは Play の基本的なヘルプを表示します。
プロジェクトの作成
Play が正しくインストールされたので、いよいよ hello world アプリケーションを作成します。Play アプリケーションの作成は、Play コマンドラインユーティリティによって完全に管理されており、とても簡単です。Play コマンドラインユーティリティは、すべての Play アプリケーション間において標準的なプロジェクトレイアウトを割り当てます。
新しいコマンドラインを開いて、以下をタイプしてください:
~$ play new helloworld
アプリケーションの完全な名前を入力するプロンプトが表示されます。 Hello world とタイプしてください。
play new コマンドは helloworld/ ディレクトリを新規に作成し、一連のファイルやディレクトリをここに追加します。以下は、もっとも重要なディレクトリです:
app/ ディレクトリはアプリケーションの中心部であり、モデル、コントローラ、およびビュー用のディレクトリに分けられています。 他の Java パッケージを含むこともできます。 app/ ディレクトリは .java ソースファイルを保存するディレクトリです。
conf/ ディレクトリは、特に重要な application.conf ファイル、ルーティングを定義する routes ファイル、国際化のための messages ファイルなど、アプリケーションを設定する全てのファイルを保存します。
lib/ は標準的な .jar ファイルとしてパッケージされたオプションの Java ライブラリを保存します。
public/ は JavaScript、スタイルシート、および画像ディレクトリを含む公的に利用可能なリソースを保存します。
test/ には、すべてのアプリケーションテストを保存します。テストは Java の JUnit として書かれるか、または Selenium テストとして書かれます。
Play は UTF-8 を唯一のエンコーディングとして使用するので、これらのディレクトリでホスティングされたすべてのテキストファイルが UTF-8 でエンコードされていることは非常に重要です。テキストエディタの設定がこれに従っていることを確認してください。
もし、あなたが熟練した Java 開発者であれば、すべての .class ファイルがどこに行ってしまったのか不思議に思うことでしょう。実は、どこにもありません: Play はいかなる class ファイルも使用せず、java ソースファイルを直接読み込みます。舞台裏では、実行中の Java ソースをコンパイルするために Eclipse コンパイラを使用しています。
これは、開発工程において 2 つの非常に重要なことを可能にします。最初の 1 つは、Play はあなたが Java ソースファイルに加えたいかなる変更をも検出して、実行時にそれを自動的にリロードするということです。 2 番目は、Java の例外が発生したとき、Play がソースコードそのものを示しながらより良いエラーレポートを作成するということです。
アプリケーションの実行
ここまで来れば、新しく作成したアプリケーションを試してみることができます。コマンドラインに戻り、新たに作成された helloworld/ ディレクトリに移動したら、 play run とタイプしてください。Play がアプリケーションをロードし、9000 番ポートで Web サーバを起動します。
ブラウザから http://localhost:9000 を開くことで、新しいアプリケーションを閲覧することができます。新しいアプリケーションは、それが首尾よく作成されたこと示す標準のウェルカムページを表示します。
新しいアプリケーションがどのようにしてこのページを表示するのかを見てみましょう。
アプリケーションのエントリーポイントは conf/routes ファイルです。このファイルはアプリケーション上のアクセス可能な URL をすべて定義します。 生成された routes ファイルを開くと、最初の ‘route’ が確認できるでしょう:
GET / Application.index
この設定は、web サーバが / パスに対する GET リクエストを受け取った場合には Application.index という Java メソッドをコールしなければならないことを Play に伝えています。この場合、controllers パッケージが暗黙的に指定されるので、 Application.index は controllers.Application.index のショートカットです。
スタンドアロンな Java アプリケーションを作成するとき、一般的に以下のようなメソッドを定義することで単一のエントリーポイントを設けます:
public static void main(String[] args) {
...
}
Play アプリケーションには、各 URL あたり 1 つのエントリーポイントがあります。 私たちは、これらのメソッドを アクション メソッドと呼びます。アクションメソッドは コントローラ と呼ぶ特別なクラスで定義されます。
controllers.Application コントローラがどのようなものか見てみましょう。 helloworld/app/controllers/Application.java ソースファイルを開いてください:
package controllers;
import play.mvc.*;
public class Application extends Controller {
public static void index() {
render();
}
}
コントローラクラスは play.mvc.Controller クラスを継承します。このクラスは index アクションで使用した render() メソッドのような、コントローラ向けの便利なメソッドを提供します。
index アクションは public static void なメソッドとして定義されています。アクションメソッドはこのようにして定義されます。コントローラクラスは決してインスタンス化されないので、アクションメソッドは static です。アクションメソッドは、フレームワークがリクエストされた URL にレスポンスできるよう public として宣言されます。アクションメソッドは常に void を返します。
デフォルトの index アクションはシンプルです: Play にテンプレートをレンダリングするよう指示する render() メソッドをコールします。テンプレートの使用は HTTP レスポンスを生成する最も一般的な (しかし、唯一ではない) 方法です。
テンプレートは /app/views ディレクトリに存在するシンプルなテキストファイルです。今回はテンプレートを指定しなかったので、このアクション用のデフォルトのテンプレートが使用されます: Application/index.html です。
テンプレートがどのようなものかを見るには、 helloworld/app/views/Application/index.html ファイルを開いてください:
#{extends 'main.html' /}
#{set title:'Home' /}
#{welcome /}
テンプレートの内容はとても簡単に見えます。実際、ここにあるものはすべて Play のタグです。Play タグは JSP taglib にとても似ています。 #{welcome /} が、ブラウザに表示されたウェルカムメッセージを生成しています。
#{extends /} タグは、このテンプレートが main.html と呼ばれる別のテンプレートを継承することを Play に伝えます。テンプレートの継承は、共通化された部分を再利用することで複雑なウェブページを作成することができる強力な概念です。
helloworld/app/views/main.html テンプレートを開いていてください:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>#{get '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'}" />
</head>
<body>
#{doLayout /}
</body>
</html>
#{doLayout /} タグに気付きましたか?ここに Application/index.html の内容が挿入されます。
フォームの作成
名前を入力できるフォームを作成するところから ‘Hello World’ アプリケーションを始めましょう。
helloworld/app/views/Application/index.html テンプレートを編集してください:
#{extends 'main.html' /}
#{set title:'Home' /}
<form action="@{Application.sayHello()}" method="GET">
<input type="text" name="myName" />
<input type="submit" value="Say hello!" />
</form>
フォームの投稿はいかなる副作用も持たず、べき等なので、フォームを投稿するメソッドとして GET を使用することに注意してください。
Application.sayHello アクションを起動できる URL を自動的に生成するよう Play に問い合わせるには @{...} 記法を使用します。さあ、ブラウザ内のトップページをリフレッシュしましょう。
おっと、エラーです。存在しない Application.sayHello アクションを参照していることが原因です。 helloworld/app/controllers/Application.java ファイル内にアクションを作成しましょう:
package controllers;
import play.mvc.*;
public class Application extends Controller {
public static void index() {
render();
}
public static void sayHello(String myName) {
render(myName);
}
}
アクションメソッドのシグネチャに myName パラメータを宣言したので、ここにはフォームから投稿される HTTP myName パラメータの値が自動的に設定されます。その後、テンプレートを表示するために render をコールします。 myName 変数を render() の呼び出しに渡しているので、この変数はテンプレートから利用できます。
この段階で名前を入力してフォームをサブミットすると、別のエラーが発生します:
エラーの内容はかなり明確です。Play はこのアクションメソッドのデフォルトのテンプレートをレンダリングしようとしますが、それは存在しません。 helloworld/app/views/Application/sayHello.html を作成しましょう:
#{extends 'main.html' /}
#{set title:'Home' /}
<h1>Hello ${myName ?: 'guest'}!</h1>
<a href="@{Application.index()}">Back to form</a>
ページをリフレッシュします。
Groovy の ?: 演算子の使い方を見てください。 myName 変数に値が設定されていない場合は、デフォルト値を表示します。このため、名前を入力せずにフォームをサブミットすると、‘Hello guest’ を表示します。
より良いURLの提供
さて、投稿した URL を見てみると、このような感じのものになっていることでしょう:
http://localhost:9000/application/sayhello?myName=guillaume
これはあまりきれいではありません。これは Play がデフォルトの ‘catch all’ ルートを使用したからです。
* /{controller}/{action} {controller}.{action}
Application.sayHello アクションにカスタムパスを設定する事でより良い URL を得ることができます。 helloworld/conf/routes を編集して、はじめの一行の次にこのルーティングを追記してください:
GET /hello Application.sayHello
フォームに戻ってもう一度サブミットし、新しい URL パターンが使用されることを確認してください。
レイアウトのカスタマイズ
アプリケーションが使用する両方のテンプレートが、同じ main.html テンプレートを継承する場合、容易にカスタムレイアウトを加えることができます。 helloworld/app/views/main.html を編集してください:
...
<body>
The Hello world app.
<hr/>
#{doLayout /}
</body>
...
これで両方のページに共通のヘッダを持たせることができます。
バリデーションの追加
名前欄を入力必須とするちょっとしたバリデーションをフォームに追加しましょう。Play のバリデーションフレームワークを使用できます。
helloworld/app/controllers/Application.java コントローラの sayHello アクションを編集してください:
...
public static void sayHello(@Required String myName) {
if(validation.hasErrors()) {
flash.error("Oops, please enter your name!");
index();
}
render(myName);
}
...
@Required アノテーションを使用するために play.data.validation. をインポートするのを忘れないでください。Play は自動的に myName フィールドに値が設定されているかを確認し、設定されていない場合は errors スコープにエラーオブジェクトを追加します。何かエラーがある場合は、 flash スコープにメッセージを追加して index アクションにリダイレクトするようにします。
flash スコープは、アクションがリダイレクトされる間もメッセージを保持します。
さて、何かエラーがあれば error メッセージを表示しなければなりません。 helloworld/app/views/Application/index.html を編集してください:
#{extends 'main.html' /}
#{set title:'Home' /}
#{if flash.error}
<p style="color:#c00">
${flash.error}
</p>
#{/if}
<form action="@{Application.sayHello()}" method="GET">
<input type="text" name="myName" />
<input type="submit" value="Say hello!" />
</form>
これが動作する事を確認します:
自動化されたテストスイートの作成
このアプリケーションのためのいくつかのテストを書いて、このチュートリアルを終えましょう。今はテストすべき Java のロジックが無いので、Web アプリケーションそのものをテストする必要があります。そこで、Selenium テストを書いていきましょう。
まず最初に、アプリケーションを test モード で実行する必要があります。アプリケーションを停止し、 test コマンドで再起動してください:
$ play test
play test コマンドは、ブラウザから直接テストスイートを実行することのできるテストランナーモジュールをロードすること以外は play run とほとんど同じです。
URL http://localhost:9000/@tests をブラウザで開き、テストランナーを見てみてください。デフォルトのテストをすべて選択して実行してみてください。すべてのテストはグリーンであるべきですが、実際のところ、これらのデフォルトのテストは何もテストしません。
通常、selenium のテストスイートは HTML ファイルとして記述されます。selenium が必要とする HTML の構文は (HTML のテーブル要素を使うようフォーマットされており) 少々退屈です。Play のテンプレートエンジンと selenium シナリオの構文を簡易にするタグのセットを使うことで、Play がこれを手助けしてくれるのは良いニュースです。
新しく作成された Play アプリケーションのデフォルトのテストスイートには selenium テストが含まれています。 helloworld/test/Application.test.html ファイルを開いてください:
*{ You can use plain selenium command using the selenium tag }*
#{selenium}
// Open the home page, and check that no error occurred
open('/')
waitForPageToLoad(1000)
assertNotTitle('Application error')
#{/selenium}
現時点でこのテストは何も問題なく実行されるはずです。このテストは、ホームページを開いて、このページが ‘Application error’ というテキストを含んでいないことを確認するだけです。
アプリケーションのためのテストを書きましょう。テストの内容を編集してください:
#{selenium}
// Open the home page, and check that no error occurred
open('/')
assertNotTitle('Application error')
// Check that it is the form
assertTextPresent('The Hello world app.')
// Submit the form
clickAndWait('css=input[type=submit]')
// Check the error
assertTextPresent('Oops, please enter your name!')
// Type the name and submit
type('css=input[type=text]', 'bob')
clickAndWait('css=input[type=submit]')
// Check the result
assertTextPresent('Hello bob!')
assertTextPresent('The Hello world app.')
// Check the back link
clickAndWait('link=Back to form')
// Home page?
assertTextNotPresent('Hello bob!')
#{/selenium}
これでこのアプリケーションを完全にテストできました。テストランナーでこのテストを選択して ‘Start’ をクリックしてください。すべてがグリーンになるはずです!
さらに詳しく
これは非常にばかばかしいアプリケーションに関するとても簡単なチュートリアルでした。Play についてもっと学びたいなら、 Play guide をチェックしてください。