PlayFramework/チュートリアル+α の履歴(No.1)
更新Play Framework†
http://www.playframework-ja.org/
Java あるいは Scala ベースで開発できる軽量 Web アプリケーションフレームワークだそうで、 面白そうだったのでさわってみました。
Debian squeeze 上で、環境は
LANG:console $ java -version java version "1.6.0_18" OpenJDK Runtime Environment (IcedTea6 1.8.13) (6b18-1.8.13-0+squeeze2) OpenJDK Client VM (build 14.0-b16, mixed mode, sharing)
です。
Scala を開発言語としていじってみます。
以下、私自身は Ruby on Rails の経験があったため、 この手のフレームワークを始めてさわる人向けではない記述になっていると思いますが、 ご了承下さい。
インストール&チュートリアル†
Java 環境が揃っていれば、あとは .zip ファイルを落としてきて展開するだけというお手軽インストールで、
Play 2.0 のインストール
http://www.playframework-ja.org/documentation/2.0.4/Installing
初めての Play アプリケーション (Scala 版)
http://www.playframework-ja.org/documentation/2.0.4/ScalaTodoList
を見れば、チュートリアルを追うところまでは迷うところはありませんでした。
- コンパイルが必要な言語で開発を行っているにもかかわらず、ブラウザで再読込するだけで ソースの変更を自動で検出してシームレスにアプリケーションをアップデートしてくれる
- その際、コンパイルエラーもブラウザ上で確認可能
- エボルーション(Rails におけるマイグレーション)までブラウザから行える
- 生の SQL を基本としているので参入障壁が低い
あたりはさすが後発なだけあると思いました。
チュートリアルに手を加える†
以下、さらにもう少し様子をうかがうために、チュートリアルで作成した todolist アプリケーションに機能を追加してみることにします。
task に登録日時を記録する†
データベースに該当するカラムを追加†
データベース形式の変更は、その履歴をちゃんとバージョン管理するために、 エボルーションを利用します。
conf/evolutions/default/2.sql
LANG:sql # Tasks schema # --- !Ups ALTER TABLE task ADD COLUMN registered DATETIME NOT NULL; # --- !Downs ALTER TABLE task DROP COLUMN registered;
チュートリアルのまま H2 を使っているので、SQL 文法は
http://www.h2database.com/html/main.html
を参考にしました。
ブラウザから todolist ページを再読込すると、
Database 'default' needs evolution! An SQL script will be run on your database - [Apply this script now!]
としてエボルーションするかどうかを聞かれるので、 [Apply this script now!] を押すと、データベースが更新される。
モデルの変更†
クラスに対応するメンバーを追加する。
app/models/Task.scala
LANG:scala
case class Task(
id: Long,
label: String,
registered: java.util.Date
)
registered の型は NULL を許容するのであれば Option[java.util.Date] とするらしいけれど、 今の場合は NOT NULL を指定してあるので生の java.util.Date とした。
Anorm パーサーの変更†
データベースの行データを Task クラスのインスタンスへ変換するためのパーサーを registered に対応するよう書き直します。
Anorm によるシンプルなデータアクセス
http://www.playframework-ja.org/documentation/2.0.4/ScalaAnorm
の「Parser API の利用」を参照しました。
app/models/Task.scala
LANG:scala
object Task {
val task = {
get[Long]("id") ~
get[String]("label") ~
get[java.util.Date]("registered") map {
case id~label~registered => Task(id, label, registered)
}
}
def all(): List[Task] = DB.withConnection { implicit c =>
SQL("select * from task").as(task *)
}
val task の部分でパーサーを定義して、それを all() の中の .as(task *) の部分で使っています。
SQL("select * from task") が返す行データを task パーサーで変換して List[Task] 型として返しているのが all() になります。
task の中身は、id, label, registered の値を使って Task(id, label, registered) を作成して返しているのですが、 私自身この部分の細かい文法はまだ理解できていません。
とりあえず呪文としては基本的な使い方を理解しました。
INSERT 文に registered を与える†
registered に NOW() を与えるだけです。
app/models/Task.scala
LANG:scala
def create(label: String) {
DB.withConnection { implicit c =>
SQL(
"""
INSERT INTO task (label, registered) VALUES ({label}, NOW())
"""
).on(
'label -> label
).executeUpdate()
}
}
一覧に登録日を表示する†
app/views/index.scala.html
LANG:scala
@main("Todo list") {
<h1>@tasks.size task(s)</h1>
<ul>
@tasks.map { task =>
<li>
@task.label (@task.registered)
@form(routes.Application.deleteTask(task.id)) {
<input type="submit" value="Delete">
}
</li>
}
</ul>