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>