PlayFramework/チュートリアル+α のバックアップソース(No.1)

更新

[[公開メモ]]

* Play Framework [#h8db752b]

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 の経験があったため、
この手のフレームワークを始めてさわる人向けではない記述になっていると思いますが、
ご了承下さい。


* インストール&チュートリアル [#xc603da6]

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 を基本としているので参入障壁が低い

あたりはさすが後発なだけあると思いました。


* チュートリアルに手を加える [#w7e6f770]

以下、さらにもう少し様子をうかがうために、チュートリアルで作成した 
todolist アプリケーションに機能を追加してみることにします。

* task に登録日時を記録する [#a7dbae77]

** データベースに該当するカラムを追加 [#l34f6968]

データベース形式の変更は、その履歴をちゃんとバージョン管理するために、
エボルーションを利用します。

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!] を押すと、データベースが更新される。

** モデルの変更 [#g4d853c3]

クラスに対応するメンバーを追加する。

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 パーサーの変更 [#t2421e3d]

データベースの行データを 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 を与える [#hf439822]

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()
     }
   }

** 一覧に登録日を表示する [#sc2ec10a]

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>

Counter: 6682 (from 2010/06/03), today: 1, yesterday: 0