Play ドキュメントを Skinny で書くと - Actions, Controllers and Results
これは Play framework 2.x Scala Advent Calendar 2013 の 10 日目です。
http://www.adventar.org/calendars/114
ご存知の方もいらっしゃるかと思いますが、私は Skinny Framework というのをつくっています。これは Servlet ベースのフルスタックな Web アプリ開発フレームワークで Web アプリ部分については「Scalatra を便利にする」というスタンスで機能拡張しています。
今回は Play Framework のドキュメントの内容を Skinny の場合だとどう書くかを説明しながら両者の比較をしてみたいと思います。なお Skinny Framework のバージョンは 0.9.20 です。Skinny はまだ 1.0 がリリースされていないフレームワークなので(1.0 は 2014/3 までにリリース予定)、ここでサポートしていない機能が今後追加されたり、また場合によっては API が変更になる場合があります。
http://www.playframework.com/documentation/2.2.x/ScalaHome
なお、明日も明後日もこのアドベントカレンダーはずっと空いているので、もしも誰も入ってこないとなると、毎日こんな感じになりますからね。覚悟してください。
Actions, Controllers and Results
http://www.playframework.com/documentation/2.2.x/ScalaActions
What is an Action?
val echo = Action { request => Ok("Got request [" + request + "]") }
これを Skinny で書くとこのようになります。Action がないだけですね。
def echo = Ok("Got request [" + request + "]")
これは Scalatra の ActionResult を使っています。ドキュメントはこちら。
このケースだと ActionResult を省略して
def echo = "Got request [" + request + "]"
とだけ書いても OK です。
Building an Action
Platy の Action とは違って Scalatra の ActionResult は
case class ActionResult( status: ResponseStatus, body: Any, headers: Map[String, String])
という構造なので特に難しいことはないと思います。これを生成する factory として Ok とか NotFound とかがあるだけです。
Controllers are action generators
Play では Controller のコードはこんな感じになりますが
package controllers import play.api.mvc._ object Application extends Controller { def index = Action { Ok("It works!") } }
同じ内容が Skinny ではこうなります。単に文字列を返すと 200 OK で指定された文字列を body として応答します。Skinny では object ではなく class になっていますが、これはルーティングのところで説明することになります。
package controller import skinny._ class Application extends SkinnyController { def index = "It works!" }
Play ではこのようにパラメータをメソッド引数として取得できますが
def hello(name: String) = Action { Ok("Hello " + name) }
0.9.20 時点で Skinny では同じことはできません。params から取得します。
def hello = "Hello " + params.getAs[String]("name").getOrElse("Anonymous")
Simple results
def index = Action { SimpleResult( header = ResponseHeader(200, Map(CONTENT_TYPE -> "text/plain")), body = Enumerator("Hello world!".getBytes()) ) }
これは Skinny では
def index = { status = 200 contentType = "text/plain" "Hello world!" }
となります。
val ok = Ok("Hello world!") val notFound = NotFound val pageNotFound = NotFound(<h1>Page not found</h1>) val badRequest = BadRequest(views.html.form(formWithErrors)) val oops = InternalServerError("Oops") val anyStatus = Status(488)("Strange response type")
はそれぞれ、ほぼ同じように書くなら以下のようになります。
// val ok = Ok("Hello world!") val ok = Ok("Hello world!") // val notFound = NotFound val notFound = NotFound() // val pageNotFound = NotFound(<h1>Page not found</h1>) val pageNotFound = NotFound(<h1>Page not found</h1>) // val badRequest = BadRequest(views.html.form(formWithErrors)) set("formWithErrors" -> formWithErrors) status = 400 render("/form") // val oops = InternalServerError("Oops") val oops = InternalServerError("Oops") // val anyStatus = Status(488)("Strange response type") status = 488 "Strange response type"
Redirects are simple results too
リダイレクトを意味する API のデフォルトが Play では 303 ですが Scalatra では 302 という違いがあります*1。
// 303 redirect def index = Action { Redirect("/user/home") } // 301 redirect def index = Action { Redirect("/user/home", MOVED_PERMANENTLY) }
これを Skinny でやると以下のようになります。ScalatraBase にある redirect メソッドは 302 でリダイレクトします。Scalatra は redirect と ActionResult を提供していて Skinny が redirect301 のようなメソッド 3 つを提供しています。
// 301 redirect def index = redirect301("/user/home") def index = MovedPermanently("/user/home") // 302 redirect def index = redirect("/user/home") def index = redirect302("/user/home") def index = Found("/user/home") // 303 redirect def index = redirect303("/user/home") def index = SeeOther("/user/home")
“TODO” dummy page
def index(name:String) = TODO
これは存在しないですが
def index = ???
とでもしておけばいいのではないでしょうか。
明日は?
以上、「Actions, Controllers and Results」のページでした。Scalatra をご存知の方はお分かりかと思いますが、半分以上は Scalatra の機能です。Skinny は Scalatra をより便利にするというスタンスなのでこのような形になります。
明日も担当者が現れなかったら・・・続きをやります。
Advent Calendar で公開しなかった場合も続きは普通の記事として公開しますので、割り込みをお待ちしております。
http://www.adventar.org/calendars/114
*1:指定がないときは 302 が妥当な気がしますが