Documentation

You are viewing the documentation for the 2.1.5 release in the 2.1.x series of releases. The latest stable release series is 2.4.x.

§初めての Play アプリケーション

Play 2.0 でシンプルな TODO 管理アプリケーションを構築し、クラウドにデプロイしましょう。

§前提条件

はじめに、 Play のインストールが成功している 事を確かめてください。 Java (バージョン 6 以上) のインストールと、Play のバイナリパッケージの unzip さえすれば始められます。

多くのコマンドを使用するため、 Unix 系 OS を利用する方が効率が良いです。 もし Windows システムを動作させているなら、コマンドプロンプトにいくつかのコマンドを入力する事で、動作させる事も可能です。

もちろん、テキストエディタは必要です。 Eclipse もしくは IntelliJ のようなお好みの Scala IDE を利用することもできます。しかし、 Playであれば、編集や開発プロセス自身をフレームワークが管理してくれるため、 Textmate, Emacs もしくは vi のようなシンプルなテキストエディタで楽しみながら作業する事が可能です。

§プロジェクト作成

Play が正しくインストールされているとして、新しくアプリケーションを作成してみましょう。 Play アプリケーションを作成する事はとても簡単であり、 Play コマンドで全て管理されています。全ての Play アプリケーション間で標準のプロジェクト構成になります。

コマンドラインで以下のように入力してください:

$ play new todolist

いくつかの質問が表示されます。 Create a simple Scala application プロジェクトのテンプレートを選択しましょう。

play new コマンドが新しいディレクトリ todolist/ を作成し、一連のファイル、ディレクトリを生成します、重要なファイルは以下の通りです:

Play では UTF-8 をエンコーディング形式として採用しており、上記のディレクトリ内に配置された全テキストファイルは本文字セットを使用してエンコードされます。テキストエディタに応じて設定を確認してください。

§Play コンソールの使用

一度アプリケーションを作成したら、 Play コンソールを実行できます。新しく作成された todolist/ ディレクトリに行き、以下のコマンドを実行して下さい:

$ play

このコマンドで Play コンソールが起動されます。 Play コンソールから実行できることはいくつかありますが、まずアプリケーションを実行させてみましょう。コンソールプロンプトから、 run とタイプしてください:

[todolist] $ run

development モードで本アプリケーションが実行されています。ブラウザを開き、 http://localhost:9000/ へアクセスしてください:

補足: 詳細は Play 2.0 コンソールを使う を確認してください。

§概要

このアプリケーションがどうやってページを表示しているか確認して行きましょう。

本アプリケーションの主なエントリーポイントは conf/routes ファイルです。このファイルはアプリケーションがアクセス可能な URL の全てを定義しています。生成された routes ファイルを開けば、初期の route を確認できます:

GET /       controllers.Application.index

上記の記述は web サーバーが / パスへの GET リクエストを受信した時に、 controllers.Application.index メソッドから実行するための Action を検索するという事を簡単に説明しています。

どうやって controllers.Application.index メソッドが実行されるのかを確認しましょう。 todolist/app/controllers/Application.scala ソースファイルを開いてください:

package controllers

import play.api._
import play.api.mvc._

object Application extends Controller {
  
  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }
  
}

controllers.Application.index はリクエスト処理を扱う Action 型の戻り値を返す事が確認できます。 Action は web ブラウザへの HTTP レスポンスを表現する Result を返す必要があります。

補足: Action についての詳細は、 アクション、コントローラ、レスポンス を確認してください。

ここでは、アクションは HTML コンテントを含む 200 OK のレスポンスを返します。 HTML コンテントはテンプレートによって提供されます。 Play テンプレートは標準 Scala 関数、 views.html.index(message: String) としてコンパイルされます。

本テンプレートは app/views/index.scala.html ソースファイル上で定義されます。

@(message: String)

@main("Welcome to Play 2.0") {
    
    @play20.welcome(message)
    
}

最初の行は関数の仕様を定義します。ここでは、単一の String 型のパラメータを受け取ります。テンプレートの内容には Scala と HTML (もしくはテキストベースの言語) を一緒に記入しています。 Scala の式は特殊な @ 文字から始まります。

§開発フロー

さて、新しいアプリケーションにいくつかの変更を加えていきましょう。 Application.scala 内でレスポンスの内容を書き換えてみます:

def index = Action {
  Ok("Hello world")
}

上記の変更によって index アクションはシンプルな text/plainHello world レスポンスを返すようになります。この変更を確かめるために、ブラウザ上でホームページを更新してみましょう:

変更を確認するのにコードを明示的にコンパイルする必要もサーバーを再起動する必要もありません。変更が検出されると、自動的にリロードされます。もし、コードを誤って変更したらどうなるでしょう?

やってみましょう:

def index = Action {
  Ok("Hello world)
}

上記のように変更したら、ブラウザ上でホームページをリロードしてください:

確認された通り、ブラウザ上に直接エラーが表示されます。

§アプリケーションの準備

本 TODO 管理アプリケーションのために、いくつかのアクションと一致する URL が必要になります。 routes ファイルを定義することから始めましょう。

conf/routes ファイルを編集してください。

# Home page
GET     /                       controllers.Application.index
                                
# Tasks          
GET     /tasks                  controllers.Application.tasks
POST    /tasks                  controllers.Application.newTask
POST    /tasks/:id/delete       controllers.Application.deleteTask(id: Long)

全てのタスクを一覧する route 定義を作成し、タスクの作成と削除を処理する2つの定義を作成します。タスク削除を扱うための route には URL パス内に id 変数を定義しています。 id の値は Action を作成する deleteTask メソッドへ渡されます。

ブラウザをリロードすれば、 Play が routes ファイルをコンパイルできないことが確認できます。

これは新しい routes ファイルが参照しているアクションメソッドが存在していないことが原因です。 Application.scala ファイルに追記していきましょう。

object Application extends Controller {
  
  def index = Action {
    Ok("Hello world")
  }
  
  def tasks = TODO
  
  def newTask = TODO
  
  def deleteTask(id: Long) = TODO
  
}

上記のコードでアクションの暫定的な実装をするために TODO という定義を使用しました。まだアクションの実装を書きたくない場合に、ビルドインの TODO アクションを使用することが可能です。このアクションからは 501 Not Implemented HTTP レスポンスが返るようになっています。

確認するために、 http://localhost:9000/tasks にアクセスできるか試してみましょう。

アクションの実装をはじめる前に修正が必要な最後の項目は index アクションです。タスクの一覧ページに自動リダイレクトするようにします:

def index = Action {
  Redirect(routes.Application.tasks)
}

見ての通り、 303 See Other の HTTP レスポンスを使用するために Ok の代わりに Redirect を使用しています。また、tasks アクションの呼び出しに必要な URL を生成するため、リバースルーターを使っています。

補足: 詳細については HTTPルーティング を参照してください。

§Task モデルの準備

実装を続ける前にアプリケーション内で Task の見え方を定義する必要があります。 case classapp/models/Task.scala ファイルに作成してください:

package models

case class Task(id: Long, label: String)

object Task {
  
  def all(): List[Task] = Nil
  
  def create(label: String) {}
  
  def delete(id: Long) {}
  
}

Task の操作を管理するためのコンパニオンオブジェクトも上記のように作成しました。ここでは各操作のダミー実装を記載しましたが、このチュートリアルの後半、リレーショナルデータベースに関する項目の中でタスクを保存する実装を追加していきます。

§アプリケーションテンプレート

本アプリケーションはタスクリストとタスク作成フォームの両方を見せる単一のウェブページを用います。
index.scala.html テンプレートを変更していきましょう:

@(tasks: List[Task], taskForm: Form[String])

@import helper._

@main("Todo list") {
    
    <h1>@tasks.size task(s)</h1>
    
    <ul>
        @tasks.map { task =>
            <li>
                @task.label
                
                @form(routes.Application.deleteTask(task.id)) {
                    <input type="submit" value="Delete">
                }
            </li>
        }
    </ul>
    
    <h2>Add a new task</h2>
    
    @form(routes.Application.newTask) {
        
        @inputText(taskForm("label")) 
        
        <input type="submit" value="Create">
        
    }
    
}

2つのパラメータを受け取るように以下のテンプレートのシグネチャを変更しました:

フォーム作成用のヘルパー関数を提供する helper._ をインポートしました。これにより、 actionmethod 属性がついた <form> タグ付きの HTML を生成する form 関数や入力用のフォーム HTML を作成する inputText 関数が使用できます。

補足: 詳細については テンプレートエンジンフォームテンプレートヘルパーの利用 を確認してください。

§タスクフォーム

Form オブジェクトはバリデーションを含んだ HTML フォームの定義をカプセル化します。本アプリケーションでは単一の label フィールドを持つフォームが必要なため、フォームを Application コントローラ内に作成してみましょう。フォームは以下のようにする事で、ユーザーによって入力された label が空ではないことをチェックします。

import play.api.data._
import play.api.data.Forms._

val taskForm = Form(
  "label" -> nonEmptyText
)

taskForm の型はシンプルな String を生成するフォームである事を表す Form[String] 型になります。 play.api.data 内にあるいくつかのクラスをインポートする必要があります。

補足: 詳細は フォームの定義 を参照してください。

§最初のページをレンダリングする

現在、アプリケーションページを表示するために要求された全ての要素の用意ができました。 tasks アクションを実装しましょう:

import models.Task

def tasks = Action {
  Ok(views.html.index(Task.all(), taskForm))
}

本メソッドがタスクリストとタスクフォームを呼出し、 index.scala.html テンプレートによってレンダリングされた HTML を含む 200 OK の結果を返します。

ブラウザ内で http://localhost:9000/tasks にアクセスしてみましょう:

§フォーム投稿処理

現在では、タスク作成フォームを投稿した場合でも、まだ TODO ページが取得されます。 newTask アクションの実装をしていきましょう。

def newTask = Action { implicit request =>
  taskForm.bindFromRequest.fold(
    errors => BadRequest(views.html.index(Task.all(), errors)),
    label => {
      Task.create(label)
      Redirect(routes.Application.tasks)
    }
  )
}

フォームに入力するため、スコープ内で request を持つことが必要になります。その場合、リクエストデータが入力されている新しいフォームを作成する bindFromRequest によってそれを扱うことができます。もしフォーム内に何らかのエラーがあった場合、エラーを再表示します (ここでは、 200 OK の代わりに 400 Bad Request を使用します) 。もし何のエラーも発生しなければ、タスクを作成し、タスクリストにリダイレクトします。

補足: 詳細は フォーム送信を扱う を参考にしてください。

§データベース内のタスクを永続化する

アプリケーションを使いやすくするため、データベースにタスクの情報を永続化するようにしましょう。本アプリケーション内でデータベースを利用可能にします。 conf/application.conf ファイル内に以下の内容を追記してください:

db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"

現在、 H2 というシンプルなインメモリデータベースを使用するようにしています。この定義を有効にするためにサーバーを再起動する必要はありません、ブラウザを更新するだけでデータベースをセットアップします。

これからチュートリアル内でデータベースを検索するために Anorm を使用します。はじめにデータベーススキーマを定義する必要があります。 conf/evolutions/default/1.sql に最初のエボリューションスクリプトを作成し、 Play エボリューション機能を使っていきましょう。

# Tasks schema
 
# --- !Ups

CREATE SEQUENCE task_id_seq;
CREATE TABLE task (
    id integer NOT NULL DEFAULT nextval('task_id_seq'),
    label varchar(255)
);
 
# --- !Downs
 
DROP TABLE task;
DROP SEQUENCE task_id_seq;

ブラウザを更新すれば、 Play はデータベースがエボリューションを要求していることを警告するでしょう。

Apply script ボタンをクリックするだけで、スクリプトが実行され、あなたのデータベーススキーマは定義済みになります!

補足: エボリューションの詳細については エボリューション を参照してください。

all() 操作を実行するための SQL クエリーを Task コンパニオンオブジェクト内に実装していきましょう。 Anorm を使用することで、 JDBCの ResultSet の一行を Task の値に変換するパーサーを定義することができます。

import anorm._
import anorm.SqlParser._

val task = {
  get[Long]("id") ~ 
  get[String]("label") map {
    case id~label => Task(id, label)
  }
}

ここでは、少なくとも idlabel 列を持つ JDBC の ResultSet の一行が与えられ、 Task の値を作成することが可能な task というパーサーになります。

all() メソッドの実装を構築するパーサーを使用することができます:

import play.api.db._
import play.api.Play.current

def all(): List[Task] = DB.withConnection { implicit c =>
  SQL("select * from task").as(task *)
}

自動的に JDBC 接続の作成と解放を行う Play の DB.withConnection ヘルパーを使用します。

クエリーを作成するために、 AnormSQL メソッドを使用します。 as メソッドが task * パーサーを用いて ResultSet をパースする事ができるようになります。そのため一度に多くのタスク行をする場合は、一割でパースし、 List[Task] を返り値に戻します (しかし、ここでの task パーサーは Task を戻します。)

実装を完成させる時が来ました:

def create(label: String) {
  DB.withConnection { implicit c =>
    SQL("insert into task (label) values ({label})").on(
      'label -> label
    ).executeUpdate()
  }
}

def delete(id: Long) {
  DB.withConnection { implicit c =>
    SQL("delete from task where id = {id}").on(
      'id -> id
    ).executeUpdate()
  }
}

本アプリケーションで遊んでみましょう、新しいタスク作成は動作すると考えられます。

補足: Anormの詳細は Anorm を参照してください。

§タスクの削除

タスクを作成することができるようになった後はそれらのタスクを削除する事ができるようにする必要があります。 deleteTask アクションの実装を (とてもシンプルに) 終わらせるだけです。

def deleteTask(id: Long) = Action {
  Task.delete(id)
  Redirect(routes.Application.tasks)
}

§Heroku へのデプロイ

全ての機能が完成しました。本アプリケーションを成果物としてデプロイしましょう。 Heroku へデプロイします。はじめに Procfile を Heroku 用に作成する必要があります。 Procfile をアプリケーションの root ディレクトリに作成してください。

web: target/start -Dhttp.port=${PORT} -DapplyEvolutions.default=true -Ddb.default.url=${DATABASE_URL} -Ddb.default.driver=org.postgresql.Driver

補足: Heroku へのデプロイの詳細については Heroku へのデプロイ を参照してください。

Heroku 上で起動する際に、アプリケーションの設定を上書きするためにシステムプロパティを使用します。 Heroku は PostgreSQL データベースを提供しているため、 要求されたドライバーをアプリケーションの依存関係に追加する必要があります。

project/Build.scala ファイルに依存関係を明記してください。

val appDependencies = Seq(
  "postgresql" % "postgresql" % "8.4-702.jdbc4"
)

補足: 依存性の管理については 依存性の管理 を参照して下さい。

Heroku はアプリケーションのデプロイに git を使用します。まずは git リポジトリを初期化しましょう。

$ git init
$ git add .
$ git commit -m "init"

これで Heroku 上にアプリケーションを作ることが出来ます。

$ heroku create --stack cedar

Creating warm-frost-1289... done, stack is cedar
http://warm-1289.herokuapp.com/ | [email protected]:warm-1289.git
Git remote heroku added

さらに git push heroku master を使ってアプリケーションをデプロイします。

$ git push heroku master

Counting objects: 34, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (20/20), done.
Writing objects: 100% (34/34), 35.45 KiB, done.
Total 34 (delta 0), reused 0 (delta 0)

-----> Heroku receiving push
-----> Scala app detected
-----> Building app with sbt v0.11.0
-----> Running: sbt clean compile stage
       ...
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 46.3MB
-----> Launching... done, v5
       http://8044.herokuapp.com deployed to Heroku

To [email protected]:floating-lightning-8044.git
* [new branch]      master -> master

Heroku はアプリケーションを構築し、クラウド上のどこかのノードにデプロイします。アプリケーションのプロセス状態を確認できます。

$ heroku ps

Process       State               Command
------------  ------------------  ----------------------
web.1         up for 10s          target/start

起動していたら、ブラウザで開いてみましょう。

あなたの初めてのアプリケーションがアップロードされ、稼働しています!