Play20 + Scala

@tototoshi

About me

Toshiyuki Takahashi

Toshiyuki Takahashi

今日の内容

  • 感想を並べました。
  • Play2.0 最高!みたいな話ではない。
  • 良いこと話してもしょうがないので悪いことを多めにしゃべります。

今日の目標

  • Play2.0 を disりすぎない

Agenda

  • Scalaから見たPlay2.0 (5min)
  • Controller (5min)
  • Model (5min)
  • View (5min)
  • Test (5min)
  • まとめ (5min)

なぜ Play20 か

なぜ Play20 か

  • Scala使えるから

なぜ Scala か

(月並みな説明)

Scala での Web 開発

  • Lift
  • Play
  • Javaのフレームワークを使用
  • unfiltered/scalatra + scalate + ORM

Playが圧倒的にラク

Play20 はラク

ラクであることはたしかだ。

だがしかし…というのが今日のテーマ

Controller

Controller

  • Request[_] を受け取って Results を返す関数
  • Action { ここに処理を記述 }

def list = Action { implicit request =>
  Ok(views.html....)
}
  • implicit request はなんだかんだで使う(bindFromRequest,i18n…etc)ので基本書いておけばいい。

Controller(router)

Controller(router)

Controller(Form)

  • 入力値の受け取り、バリデーションの仕様
  • 入れ子、リストなどにも対応している
  • そのまま ScalaTemplate に渡される。便利だが、密結合、再利用性が低いなどの問題あり。
  • 2.1(たぶん)からはjodatimeが使えるなど細かい改善が多い。
  • テキストデータしか想定されていない?のでファイルアップロードなどは提供されていない。
  • 拡張方法

Controller(BodyParser)

  • リクエストボディをパースする仕組み
  • 明示的に使うのはjsonのリクエストを受け付けたり、ファイルアップロードを実装するとき。
  • trait BodyParser[+A] extends Function1[RequestHeader, Iteratee[Array[Byte], Either[Result, A]]]
  • さすがにこれは難しいわ

Controller(BodyParser)

  • 入力値の受け取り、検証がFormとBodyParserに分かれるのが辛い。

Controller(BodyParser)

本当はこうしたい


val myForm = Form(
  "name" -> nonEmptyText,
  "file" -> file("filename")
)

Controller(BodyParser)

リアル

Action(parse.multipartFormData) { implicit request =>
  myForm.bindFromRequest(request)...
.
  request.body.file("filename").map { f =>
  ...
  }
}

Controller(Json)

  • play.api.libs.json は自分でフォーマットを定義してやる必要がある。
  • めんどう…
  • 2.1で改善されるんですね

Controller(Json)

scala> import play.api.libs.json._
import play.api.libs.json._

scala> import play.api.libs.json.Generic._
import play.api.libs.json.Generic._

scala> case class User(id: Long, name: String, age: Int)
defined class User

scala> implicit def userFormat: Format[User] =
         productFormat3("id", "name", "age")(User.apply)(User.unapply)
userFormat: play.api.libs.json.Format[User]

scala> Json.toJson(User(1, "peter", 20))
res0: play.api.libs.json.JsValue = {"id":1,"name":"peter","age":20}

scala> Json.stringify(Json.toJson(User(1, "peter", 20)))
res1: String = {"id":1,"name":"peter","age":20}

Controller(Json)

lift-json使えば良いと思う

import com.github.tototoshi.play2.json.LiftJson
import net.liftweb.json._

case class Person(id: Long, name: String, age: Int)

object Application extends Controller with LiftJson {

  implicit val formats = DefaultFormats

  def get = Action { implicit request =>
    Ok(Extraction.decompose(Person(1, "ぱみゅぱみゅ", 19)))
  }

  def post = Action(liftJson) { implicit request =>
    Ok(request.body.extract[Person].name)
  }

}

Controllerの拡張1

  • ActionsComposition
  • もうちょっと手軽な仕組みを…
  • ServletFilter的なものが欲しい(Globalじゃなくて)

Controllerの拡張2

  • In: BodyParserを作る
  • Out: Writeable,ContentTypeOfを実装する

Model

dbplugin

  • コネクションリーク!
  • コネクションリーク!
  • コネクションリーク!
  • コネクションリーク!
  • dbplugin=disabled

Evolutions

  • コネクションリーク!
  • inconsistent status!
  • なんだか動きが怪しいぞ…
  • evolutions=disabled
  • 本番環境でやったら…お…おそろしい….

Anorm以外の選択肢

  • Squeryl
  • SLICK(ScalaQuery)
  • O/R Broker
  • Scalikejdbc

Squeryl

  • すくぅいれる
  • ORMとしては一番良い?
  • (個人的にはあまり好きではないが)

SLICK(ScalaQuery)

  • Typesafe Stack!
  • Typesafe DSL
  • かっこいい
  • でもまだ仕事で使うには辛い(Tuple22問題など)

SLICK(ScalaQuery)

object Coffee extends DBTable (
  "jdbc:postgresql://localhost/coffeeshop",
  "coffee"
)

O/R Broker

ごめんなさい。あまり知りません。

Scalikejdbc

View

ScalaTemplate

  • Scalaっぽい書き方ができるがScalaではない
  • ScalaっぽいものをコンパイルしてScalaにして、さらにコンパイル

ScalaTemplate

  • 実装がテキトー
  • 仕様どうなってんの?
  • Generics使いたい
  • Formと密結合すぎるかも

ScalaTemplate

Q.次のうちでコンパイルが通るものは?

  1. @if(true) 1 else 2
  2. @if (true) 1 else 2
  3. @if (true) { 1 } else { 2 }
  4. @if(true) { 1 } else { 2 }
  5. @for(i <- 1 to 10) { …}
  6. @for (i <- 1 to 10) { …}

ScalaTemplate

吐かれるHTMLがわからない。

  • わからなくて良い?
  • Liftとは真逆の方向性

FieldConstructor

その他のテンプレートエンジン

  • (Playを拡張すれば)使えることは使える
  • ただし、Formとの連携まで考えると辛い。

Test

Test

  • 「テスト書かない人とかいるんですか??」→テスト書かないのは人間レベルに達していない
  • 静的型言語はテストの量を減ると言われるが、テストを書かなくても良いことではない。

Scala のテストフレームワーク

Specs2 or ScalaTest?

Fakes

  • FakeApplication
  • FakeRequest
  • FakeHeaders
  • TestBrowser
  • TestServer

Test Helpers

  • routeAndCall
  • inMemoryDatabase

FakeApplication

running(FakeApplication()) {
...
}
  • 設定を変えたり、inMemoryDatabaseを使ってテストができたりする
  • いちいち書くのがめんどう。
  • いちいちプラグイン全てをリロード
  • プラグインがリソースリークさせていたりすると…

Dependency Injection

まとめ

まとめ

Play20 は

  • 微妙なモジュールが多い
  • Pluggableで良かった
  • 実装がテキトー
  • Bug耐性
  • スリル感

まとめ

  • 実戦不足。揉まれてない。
  • がんばりましょう

まとめ

  • それでもPlay2.0を使う
  • Scalaですから

Play2.0 から命を守る

  • MLを読む
  • (疑いながら)ドキュメントを読む
  • ソースを読む
  • 直せそうだったらPullRequest

ご清聴ありがとうございました