Play2.3.xから2.4への移行ではまったところ

6/8に[Play 2.4 ソースコードリーディング](https://playframeworkja.doorkeeper.jp/events/26150)に参加するので、その前にとりあえず試しにPlay2.4アプリを作リ始めてみた。

なんか、色々と変わっていてなかなか衝撃的。。。(--
現在、仕事で開発しているアプリはリリース（今秋予定）までには、2.4に置き換えるつもりだけどわりかし不安。

以下、自分のぶつかった問題のメモ。(と勉強会を受けての追記)

## WS
- asyncHttpClientが1.19.21になった
  - asyncHttpClientは1.18から1.19でメソッド名等が大幅に変更になったため直接使っている場合要注意
- WSを使う場合はWSClientを@Inject()する
  - @InjectはJavaのAnnotationがScalaクラスのコンストラクタについているだけという理解
      - @Injectがobjectに使えないのはコンストラクタ引数がないから？
      - あるいはそもそもobjectではコンストラクタAnnotationは使えないのかも
- controller以外でWSClientを使いたい場合にどうするのかが謎
  - (追記)多分ControllerでInjectされたオブジェクトをimplicitで引き回すのが良いんだと思う。

## Routes
- RouterのデフォルトがDependency Ingected Routerに変更
  - 2.3.x互換の既存のRouterを使う場合はbuild.sbtからroutesGeneratorの行を削除する
- Dependency Ingected Routerを使う場合はcontrollerをobjectからclassに変更する
  - 多分こっちじゃないと@Injectが使えないので、WSClientとか使えない
- 初期生成されるroutesでassetsがassets.atからassets.versionedに変更になった
- (追記)Dependency Ingected Routerではこんな感じでroutesを分割できる

[Playframework.com/conf/routes]https://github.com/playframework/playframework.com/blob/8fa6f8b0dd9ee34daff3959cab8aec8440890725/conf/routes#L33
[Playframework.com/app/controllers/documentation/Router.scala](https://github.com/playframework/playframework.com/blob/8fa6f8b0dd9ee34daff3959cab8aec8440890725/app/controllers/documentation/Router.scala)

通常routesの変更は全クラスリコンパイルになるが、このやり方の場合差分コンパイルになるかも。

## Messages
- これもInjectが必要になったらしい
- このクラスをどうしたもんかと思案中
  - Messagesもimplicitで渡せば動きそうだけどイマイチな気がする
  - defaultを渡そうかと思ったけどMessagesのインスタンスを作るにもimplicitが。。。(--

``` scala
import play.api.i18n.Lang
import play.api.i18n.Messages

import org.json4s._
import org.json4s.jackson.JsonMethods._

class ApplicationException(val code: Int, val messageKey: String, params: Any*) extends Exception {
  def message(implicit lang: Lang) = Messages(messageKey, params:_*)

  def toJson(implicit lang: Lang, formats: Formats): JValue = Extraction.decompose(Map(
    "code" -> code,
    "message" -> message
  ))
}
```

- (追記) 多分こんな感じ。結局のところ他の部分と同様にLangをMessagesに置き換えるだけだった

``` scala
class Application @Inject() (messagesApi: MessagesApi) extends Controller {

  implicit val formats = DefaultFormats
  implicit def message(implicit request: RequestHeader) = messagesApi.preferred(request)

}
```

``` scala
import play.api.i18n.Messages

import org.json4s._
import org.json4s.jackson.JsonMethods._

class ApplicationException(val code: Int, val messageKey: String, params: Any*) extends Exception {
  def message(implicit messages: Messages) = messages(messageKey, params:_*)

  def toJson(implicit messages: Messages, formats: Formats): JValue = Extraction.decompose(Map(
    "code" -> code,
    "message" -> message
  ))
}
```

- confで指定していたapplication.langsはplay.i18n.langsに変更になった
  - ついでにカンマ区切り文字列から配列に変更になった
  - (追記)これ以外にもconfのキーはかなり変わっているので要注意

## (追記)GlobalSettings
- 2.4でも使えるけど非推奨
- GlobalSettingsの各メソッド毎に対応するtraitがあるので、それをInjectする
  - Injectするクラスはconfで設定

## (追記)JSON
- JsValueのトラバースがJsValueではなくJsLookupResultを返すようになった


``` scala
val json: JsValue
val child: JsLookupResult = json \ "hoge"
val child2: JsValue = (json \ "hoge").get
val child3: Option[JsValue] = (json \ "fuga").toOption
```

- JsUndefinedもJsValueの一種ではなくJsLookupに移動
  - JavaScriptではオブジェクトの値として「undefined」が使えるがJSONには「undefined」はないためと思われる

## play-json4s
- 2.4対応バージョンにアップしたら普通に動いた

## scalikejdbc
- 試してないが2.4対応のツィートが流れていたので多分バージョンアップすれば動くであろう

---
何かしら問題が起こった場合、[Migration Guide](https://www.playframework.com/documentation/tr/2.4.x/Migration24)を見ればだいたい答えが書いてある。  
ていうか、これは全部通しで読まねばなるまい。
感触的にはInjectさえ押さえれば、あとはそんなに違和感なく移行できそうな気がしないこともない。
(どっちやねん！このエントリは勉強会終了後に書き直します。)


## 勉強会を終えての感想
2.3.xからの移行では多分、Messagesの変更に対応するのが一番辛そう。  
あとはやっぱりInjectを押さえれば多分なんとかなる。

GlobalSettingsとか旧バージョンからの移行救済措置として残されているものもあるけど、早めに置き換えた方が良いだろうとは思う。