k4200’s notes and thoughts

Programmer side of k4200

Play! 2.0から2.1にアップデート

時代はPlay 2.1だよね(棒

Play 2.0から2.1にアップデートした。丸1日位かかったので、これからやる人に向けてちょっとメモ。

  • 旧環境: Play 2.0.4
  • 新環境: Play 2.1.1
なぜアップデートしようと思ったか

ちなみに、元々Play 2.1にアップデートするつもりじゃなかったんだけど、以下の理由から何となく・・・

  • Eclipse 3.6 + Scala IDE 2.0の環境で、"Check for Updates"をしたら、Scala IDEの新しいのが出てたので何も考えずにアップデート
  • 何かうまく動かない・・・調べてみたらScala IDE 3.0はEclipse 3.6では動かないとのこと
  • Scala IDE 2.0に戻そうと思ったけど、Scala 2.8向けのしか無いぞ・・・
  • Eclipse 4.2 + Scala IDE 3.0にすっか
  • せっかくだし、Play 2.1にしてみるか

Eclipse 3.6でScala IDE 3.0にしちゃうと、以下の様なエラーが出る。Eclipse 3.6の場合はアップデート出来ないようにしとけよ!ってみんな思うらしい。

Could not open the editor: scala.tools.eclipse.ScalaPresentationReconciler cannot be cast to org.eclipse.jdt.internal.ui.text.JavaPresentationReconciler
Play, Scala, ライブラリの非互換性に対応する

Play 2.0から2.1にあたって、対応すべき点は、大雑把に言って以下の通り。

  • Play 2.0と2.1間の非互換性
  • Scala 2.9と2.10間の非互換性
  • 使用しているライブラリのバージョンが上がることによる非互換性

Play自身に関しては、こちらのMigration Guideを参考に。

Scala 2.9と2.10に関する非互換性ってどっかにまとまってるのかな。とりあえず、Manifestに関するドキュメントはあったけど。

ライブラリに関しては、2.9で使えるバージョンと2.10で使えるが異なる場合が結構あって、その場合に非互換性がある場合がある。これはまぁライブラリ毎に対応する必要がある。

以降、順に説明していく。

Play自身に関する非互換性に対応

さっき触れたMigration Guideに目を通すこと。

plugins.sbt, build.properties

project/plugins.sbt で、sbt-pluginのバージョンを変更。2.1.1ですよ。

addSbtPlugin("play" % "sbt-plugin" % "2.1.1")

project/build.properties の、sbtのバージョンを記載。0.12.3 でいいんだよね。

sbt.version=0.12.3
Build.scala

次にBuild.scala を変更。

変更点をまとめておく。

  1. Play 2.0のPlayProjectが2.1ではplay.Projectに変わったらしい。
  2. 今まで標準で入っていたライブラリ群がモジュール化されて、個別にdependenciesに追加しなければいけなくなった。play.api.db.*とかAnormとか。
  3. mainLangは指定する必要ない(※)

変更後はこんな感じ(必要なところのみ記載)。

// import PlayProject._ //1: Play 2.0
import play.Project._ // 1: Play 2.1

object ApplicationBuild extends Build {
  //途中省略
  val appDependenciesWebsite = Seq(
    jdbc, // 2関連
    fooLibrary,
    barLibrary
  )
  val website = play.Project( // ← 1関連
    appName + "-website", appVersion, appDependenciesWebsite
    //3: 不要→ , mainLang = SCALA 
  //以後省略

※mailLangつけてると以下のエラーが出た。

[error] /Users/k4200/greatapp/project/Build.scala:61: not found: value mainLang
Promise → Future

Play 2.0のPromiseはなくなって?ScalaのFutureになったよ。ただ、Migration Guideに書いてある例が非同期の例しか載ってなかったので、ここで自分の例も載せておく。

    //2.0ではこんなことしてた
    WS.url("http://example.com/someapi").withQueryString(params:_*).get.map { response =>
      //
    }.value.get

    //2.1ではこうした。
    import scala.concurrent.Await
    import play.api.libs.concurrent.Execution.Implicits._
    import scala.concurrent.duration.Duration

    val timeout = Duration("120 seconds")
    val future = WS.url("http://example.com/someapi").withQueryString(params:_*).get.map { response =>
      //
    }
    Await.result(future, timeout)

もっといい方法ないすか?

JsonObjectの変更点

play.api.libs.json.Reads のシグニチャーが変わったよ。

     // Play 2.0
     def reads(json: JsValue): (String, String) = {
       ((json \ "foo").as[String], (json \ "bar").as[String])
     }
     // Play 2.1
     def reads(json: JsValue): JsResult[(String, String)] = { // JsResultを返す
       JsSuccess(((json \ "foo").as[String], (json \ "bar").as[String])) //JsSuccessでラップ
     }

使う時

    // Play 2.0
    val result = Json.fromJson[(String, String)](jsValue)

    // Play 2.1
    val jsResult = Json.fromJson[(String, String)](jsValue)
    val result = jsResult.get //良い子のみなさんはgetOrElseとかasOptとか使って下さい。

Scala 2.9と2.10間の非互換

自分が引っかかったのは1つだけだと思う。

<% がなくなった?

specs2関連でこんなtraitを作って使ってた。

// Scala 2.9
trait UseFakeApplication extends AroundExample {
  class FakeApp extends FakeApplication()
  def around[R <% Result](r: =>R) = running(new FakeApp)(r) 
} 

上のtraitを実装しているところが以下のようなエラーを出すようになった。

[error] /Users/k4200/greatapp/test/package/SomeClassSpec.scala:10: class SomeClassSpec needs to be abstract, since method around in trait AroundExample of type [T](t: => T)(implicit evidence$1: org.specs2.execute.AsResult[T])org.specs2.execute.Result is not defined

これこれの情報を参考にした。

Seems that we are going to need to change the implementation of Around to, since <% is going to go away in scala 2.10

だって。以下のように書き換えたらOKだった。

// Scala 2.10
import org.specs2.execute.AsResult
trait UseFakeApplication extends AroundExample {
  class FakeApp extends FakeApplication()
  def around[R: AsResult](r: =>R): Result = running(new FakeApp)(AsResult(r))  
}

以前のrpscalaでScalaTest最高!って発表見てから、specs2は捨てたくなったけど、まぁ仕方ない。

その他

Play 2.1でプロジェクトを分割する方法はこちら

自分はPlay 2.0の時からサブプロジェクトに分割してたんだけど、Play 2.1にしたら以下の様なエラーがでた。

[warn] Binary version (2.9.2) for dependency org.scala-lang#scala-library;2.9.2
[warn]  in some-project#some-project_2.10;0.1-SNAPSHOT differs from Scala binary version in project (2.10).
(snip)
[error] error while loading Foo, class file needed by Foo is missing.
[error] reference value meta of package annotation refers to nonexisting symbol.

Build.scalaを以下のように変更したらOKだった。

    val bgtasks = Project(
      appName + "-bgtasks", file("bgtasks")
    )
    .settings(
      scalaVersion := "2.10.1", // これを追加
      libraryDependencies ++= appDependenciesBgtasks,

まとめ

PlayもScalaも開発が活発でどんどん便利になるけど、こういう移行作業はツライっす。この記事が何かの参考になれば幸い。