§Play 2.1 移行ガイド
Play 2.0.x アプリケーションを Play 2.1.0 に移行するためには、まず project/plugins.sbt
内でPlayの sbt-plugin
のバージョンを上げましょう。
addSbtPlugin("play" % "sbt-plugin" % "2.1.0")
次に project/Build.scala
内で PlayProject
の代わりに新しい play.Project
を使うように修正しましょう。
import を記述します。
import play.Project._
main
プロジェクトの生成部分を次のように変更します。
val main = play.Project(appName, appVersion, appDependencies).settings(
project/build.properties
ファイルを次のようにアップデートします。
sbt.version=0.12.2
最後に Play 2.1.0 のディストリビューションに含まれる play
コマンドを起動して、プロジェクトを一旦 clean して、そして再コンパイルしましょう。
play clean
play ~run
もし何らかのコンパイルエラーが発生したら、このドキュメントを参考にして、エラーの原因となった非推奨機能、互換性のない変更などを発見することができます。
§ビルドファイルへの変更
Play 2.1 では今まで以上にモジュール化が進められました。そのため、これからはアプリケーションに必要なモジュールへの依存性を明示的に指定する必要があります。デフォルトの play.Project
には Play の核となるたった一つのコアモジュールへの依存性のみが定義されています。それ以外のモジュールは必要に応じて選択しましょう。 Play 2.1 で新たに切りだされたモジュールは次の通りです。
jdbc
: JDBC コネクションプールおよびplay.api.db
APIanorm
: AnormjavaCore
: コア Java API.javaJdbc
: Java 向けのデータベースAPI.javaEbean
: Java 向けの Ebean プラグインjavaJpa
: Java 向けの JPA プラグインfilters
: Play の組み込みフィルタ(CSRF フィルタなどを含みます)
Play 2.1 の典型的な Build.scala
ファイルの内容は次のようになります。
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "app-name"
val appVersion = "1.0"
val appDependencies = Seq(
javaCore, javaJdbc, javaEbean
)
val main = play.Project(appName, appVersion, appDependencies).settings(
// Add your own project settings here
)
}
プロジェクトの mainLang
パラメータはもう必要なくなりました。 Play 2.1 からは、プロジェクトのメイン言語は追加されたモジュールによって自動的に決定されます。具体的には、 javaCore
モジュールが追加されていればメイン言語は JAVA
となり、それ以外のケースでは SCALA
となります。上記の Build.scala
ファイルの例でいうと、 appDependencies
の部分にモジュール依存性が書かれています。
§play.mvc.Controller.form() が play.data.Form.form() へ名称変更
Play のモジュール化に関連して、 play.data
パッケージとその依存ライブラリはPlayのコアモジュールから分離して javaCore
モジュールに移動しました。結果的に、 play.mvc.Controller#form
も play.data.Form#form
へ移動しています。
§play.db.ebean.Model.Finder.join() が fetch() へ名称変更
コード整理の一環として、 Finder API の join メソッドが fetch メソッドに置き換えられました。機能には変更ありません。
§Play の Promise から、Scala の Future への移行
Scala 2.10 で scala.concurrent.Future
が導入されたことで、Scala のエコシステムに散在していた様々な Future 、 Promise ライブラリが統合され始めました。
Play が scala.concurrent.Future
を利用するということは、つまり開発者が Play の内部 API および外部ライブラリ両方の Future/Promise をそのまま組み合わせられるようになったということです。
今のところ、Java ユーザは引き続き scala.concurrent.Future をラップした Play の Promise を利用することが想定されています
次のコードスニペットを見てみましょう。
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import akka.util.duration._
def stream = Action {
AsyncResult {
implicit val timeout = Timeout(5.seconds)
val akkaFuture = (ChatRoomActor.ref ? (Join()) ).mapTo[Enumerator[String]]
//convert to play promise before sending the response
akkaFuture.asPromise.map { chunks =>
Ok.stream(chunks &> Comet( callback = "parent.message"))
}
}
}
新しい scala.concurrent.Future
を利用すると、このコードは次のように変わります。
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import play.api.libs.concurrent.Execution.Implicits._
import scala.concurrent.duration._
def stream = Action {
AsyncResult {
implicit val timeout = Timeout(5.seconds)
val scalaFuture = (ChatRoomActor.ref ? (Join()) ).mapTo[Enumerator[String]]
scalaFuture.map { chunks =>
Ok.stream(chunks &> Comet( callback = "parent.message"))
}
}
}
インポートについての変更は次の通りです。
- 実行コンテキスト
play.api.libs.concurrent.Execution.Implicits
を新たにインポートする - Akka APIのDuration の代わりに、
scala.concurrent.duration
を使う asPromise
メソッドが必要無くなっている
一般的に言うと、 “error: could not find implicit value for parameter executor” というエラーが出た場合、以下のインポートが足りない可能性があります。
import play.api.libs.concurrent.Execution.Implicits._
(詳細については Scalaの実行コンテキストに関するドキュメント を参照してください)
最後に、次の二点を改めて確認しましょう。
- Playの
Promise
は Scala のFuture
に置き換えられたこと - Playの
Redeemable
は Scala のPromise
に置き換えられたこと
§Scala JSON API への変更
Play 2.1で は、全く新しい Scala 向けの JSON のバリデータとパスナビゲータ API が導入されました。この API は既存の JSON パーサとは互換性がありません。
play.api.libs.json.Reads
型のシグネチャも変更されました。次のコードを見てください。
trait play.api.libs.json.Reads[A] {
self =>
def reads(jsValue: JsValue): A
}
このコードは、 Play 2.1 では次のように変わりました。
trait play.api.libs.json.Reads[A] {
self =>
def reads(jsValue: JsValue): JsResult[A]
}
例えば、 Play 2.0 で User
型の JSON シリアライザは次のように実装していました。
implicit object UserFormat extends Format[User] {
def writes(o: User): JsValue = JsObject(
List("id" -> JsNumber(o.id),
"name" -> JsString(o.name),
"favThings" -> JsArray(o.favThings.map(JsString(_)))
)
)
def reads(json: JsValue): User = User(
(json \ "id").as[Long],
(json \ "name").as[String],
(json \ "favThings").as[List[String]]
)
}
これを Play 2.1 では次のようにリファクタリングする必要があります。
implicit object UserFormat extends Format[User] {
def writes(o: User): JsValue = JsObject(
List("id" -> JsNumber(o.id),
"name" -> JsString(o.name),
"favThings" -> JsArray(o.favThings.map(JsString(_)))
)
)
def reads(json: JsValue): JsResult[User] = JsSuccess(User(
(json \ "id").as[Long],
(json \ "name").as[String],
(json \ "favThings").as[List[String]]
))
}
JSON を生成する API も同様に進化しました。次のコードを見てください。
val jsonObject = Json.toJson(
Map(
"users" -> Seq(
toJson(
Map(
"name" -> toJson("Bob"),
"age" -> toJson(31),
"email" -> toJson("[email protected]")
)
),
toJson(
Map(
"name" -> toJson("Kiki"),
"age" -> toJson(25),
"email" -> JsNull
)
)
)
)
)
これを Play 2.1 で書くと次のようになります。
val jsonObject = Json.obj(
"users" -> Json.arr(
Json.obj(
"name" -> "Bob",
"age" -> 31,
"email" -> "[email protected]"
),
Json.obj(
"name" -> "Kiki",
"age" -> 25,
"email" -> JsNull
)
)
)
これら機能の詳細については JSON関連のドキュメント を参照してください。
§Cookie 周りの変更
Cookie を永続化するためにはこれまで maxAge
に -1 を設定していました。しかし、 JBoss Netty 側の変更により、これからは -1 の代わりに null
または None
(利用する API に依ります)を設定するようになりました。 maxAge
に 0 以下の値を設定した場合、Cookie は直ちに期限切れします。
SimpleResult
の discardingCookies(String\*)
(Scala) と discardCookies(String…)
(Java) メソッドは特定のパスやドメインにセットされた Cookie や、セキュア Cookie を扱えなかったため、非推奨になりました。代わりに、discardingCookies(DiscardingCookie*)
(Scala) と discardCookie
(Java) メソッドを利用してください。
§RequireJS
Play 2.0 では JavaScript は Google Closure の CommonJS モジュールサポートによって処理されていました。 Play 2.1 からは代わりに RequireJS が使われるようになりました。
実利用においてこれが意味するのは、特に何か設定しない限り、 Play は stage 、 dist 、 start 時のみ JavaScript を minify するということです。開発モード時は、クライアント側で JavaScript モジュール間の依存性が解決されます。
この機能を利用する場合は、 project/Build.scala
内の設定に RequireJS で管理したいモジュールを追加する必要があります。
requireJs := "main.js"
この機能の詳細については RequireJS関連のドキュメント を参照してください。