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

更新

[[公開メモ]]

#contents

* Play Framework [#h8db752b]

http://www.playframework-ja.org/

Java あるいは Scala ベースで開発できる軽量 Web アプリケーションフレームワークだそうで、
面白そうだったのでさわってみました。

Windows 7 64bit 上で、環境は

 LANG:console
 C:\Users\osamu\play>java -version
 java version "1.7.0_07"
 Java(TM) SE Runtime Environment (build 1.7.0_07-b11)
 Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)

です。

Scala を開発言語としていじってみます。

以下、私自身は Ruby on Rails の経験があったため、
この手のフレームワークを始めてさわる人向けではない記述になっていると思いますが、
ご了承下さい。

一方、Scala については [[O'Reilly の「プログラミング Scala」>Google:ISBN 4873114810]] 
を買って、さらっと目を通した物のかなり初心者なので、

@IT総合トップ > Java Agile > スケーラブルで関数型でオブジェクト指向なScala入門 ~
http://www.atmarkit.co.jp/fjava/index/index_scala.html ~

@IT > Java Agile > Scala+Play 2.0でWebアプリ開発入門(1):Play framework~
http://www.atmarkit.co.jp/ait/articles/1210/26/news018.html ~

あたりを見ながら勉強を進めているところです。

* インストール&チュートリアル [#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>

これで

 4 task(s)
 
     abc (2013-01-09 19:44:58.491) [Delete]
     def (2013-01-09 19:45:03.709) [Delete]
     ghi (2013-01-09 19:45:09.16) [Delete]
     jklmn (2013-01-09 19:45:12.683) [Delete]

のように、(不格好ながらも)登録日を記録・表示できるようになりました。

** 生の SQL を書くことについて [#p10ebd02]

Rails だとデータベースドライバ依存性を吸収するために
生の SQL は極力書かないようにして、代わりに ActionRecord に面倒を
見てもらっていました。そうすることで、同じコードを異なるデータベース上で
動かすことができて、例えば開発初期にはオンメモリデータベースなどを用いて
開発を進め、ある程度できあがってから本番環境に移すとか、本番環境で
動くようになってからも、仕様変更のユニットテストはオンメモリデータベースで
高速に行うとか、そういった柔軟な運用が可能でした。ただその分、
まず始めに ActionRecord の文法を学ばなければなにもできないという
参入障壁がありました。

Play Framework では基本的に生の SQL をそのまま書くという方針なため、
後からデータベースを切り替える時には大幅にコードを見直す必要が生じそうで
その点がちょっと心配です。

一方で、Rails では ActionRecord に隠蔽されてしまっていた
データベースに格納された行データと、アプリケーション内における
モデルクラスのインスタンスとの相互変換が、Play では Parser 
などを用いた形で明示的に書かれることになるため、
コードの見通しは、かえって良くなりそうに思えました。

Scala の卓越した記述力により、
CRUD (create、read、update、delete) のインタフェースや、
その他の必要なクエリを1から書くことが非常に簡潔に行えて、
モデルクラスのコードを見るだけで「できること」をそのまま
一覧できるのが良い感じです。

** 静的型付け言語であること [#h14cd692]

Ruby on Rails では Ruby が型に対してかなり緩いインタプリタ言語であったために、
各場面で用意されている変数の数や、そこに入っている値の型が曖昧で、ライブラリの
ソースを何度も grep しないとコードを書けないようなことが良くありました。

それに比べて Scala では変数の型がコンパイル時に決まるため、慣れないライブラリで
開発するのがずいぶん楽になるのではと期待しています。

さらに Eclipse でのデバッグもできるようで、変数値を直接覗けたりするのも
非常に助かりそうです。

* Eclipse による開発&デバッグ [#uc095017]

ブレークポイントやステップ実行まで使えるそうなので、ぜひやってみたい。~
http://www.atmarkit.co.jp/ait/articles/1301/07/news013_3.html

** eclipsify で Eclipse Project を作成 [#xfb32160]

まずは eclipsify してみます。

 LANG:console
 C:\Users\osamu\play\todolist> play
 [todlist] $ eclipsify with-source=true
 ...
 
 [info] Successfully created Eclipse project files for project(s):
 [info] todolist
 [todlist] $ 

todolist プロジェクトフォルダに .project とか .settings とか
できたみたい?

** Eclipse で Project を Import [#xdd1ce29]

よく分からないけれど、インストール直後の 
eclipse Juno Service Release 1 Build id: 20120920-0800 から、

[File]-[Import...]-[General]-[Existing Projects into Workspace] を選んで、
Select root Directory で todolist のプロジェクトディレクトリを選択すると、
Projects の部分に todolist が表示されました。

チェックされていることを確認して [Finish] したところ、
ちゃんと Navigator にフォルダが一覧されます。~
#途中 [Window]-[Preference]-[Team]-[Git] を確認しろと言われました。後で見なければ。

** Eclipse に Scala IDE をインストール [#i0afcbb1]

そのままだと Eclipse の Text Editor で scala ファイルを開いても、
シンタックスハイライトすらされなかったため、

http://www.atmarkit.co.jp/fjava/rensai4/scala01/02.html

を参考に Scala IDE を入れることにする。

http://scala-ide.org/download/current.html

へ行って、

http://download.scala-ide.org/sdk/e37/scala29/stable/site/

をコピー、Eclipse の [Help]-[Install New Software...] の [Work With] に貼り付けて Enter

一覧に表示された、

 Scala IDE for Eclipse

を選択して [Next] したところ、

 Cannot complete the install because one or more required items could not be found.
   Software being installed: 
       Scala IDE for Eclipse 2.0.2.v-2_09-201207120929-81d0972
       (org.scala-ide.sdt.feature.feature.group 2.0.2.v-2_09-201207120929-81d0972)
   Missing requirement: 
       Scala Plugin 2.0.2.v-2_09-201207120929-81d0972 
       (org.scala-ide.sdt.core 2.0.2.v-2_09-201207120929-81d0972) 
           requires 
       'bundle org.eclipse.jdt.core [3.6.0,3.7.10)' 
           but it could not be found
   Cannot satisfy dependency:
       From: Scala IDE for Eclipse 2.0.2.v-2_09-201207120929-81d0972 
             (org.scala-ide.sdt.feature.feature.group 2.0.2.v-2_09-201207120929-81d0972)
      To: org.scala-ide.sdt.core [2.0.2.v-2_09-201207120929-81d0972]

というエラーが出た。

https://groups.google.com/forum/?fromgroups=#!topic/scala-ide-user/inkKUonHDLQ

を参考に、[Work With] へ貼り付ける URL を

 http://download.scala-ide.org/nightly-update-juno-master-29x

としたところ、今度はうまくいって、

- License Agreement
- Unsigned 警告

が出た後に、

- Scala IDE for Eclipse
-- JDT Weaving for Scala

がインストールされた。

Eclipse を再起動すると、Scala ソースの編集に Scala Editor が使えるようになっていて、
ちゃんと文法を認識してくれた。

* 完了予定日を追加する [#bba1ef88]

Form に項目を追加するのも時間を見つけてやってみたい。

* データベースにオンメモリ H2 以外を使う [#gaf9d2ba]

H2 はオンメモリ以外にもファイルベースやサーバー型でも動かせるらしいので試してみたい。~
http://d.hatena.ne.jp/rabbit2go/20110503/1304423200

MySQL にも繋いでみたい。

ここを見ればできそう・・・かな?~
https://github.com/playframework/Play20/wiki/ScalaDatabase ~

* Web サーバーへのデプロイ [#be64ac4c]

これもやってみないと。

* コメント [#g43d3783]

#article_kcaptcha

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