Play Framework 2のbad partsとその避け方

play frameworkはscalaのweb frameworkとしてデファクトスタンダードの位置にあります。
しかし、かなり古参のフレームワークであることもあって今日scalaを書く上で良いとされているpracticeやマナーに反する点も多くあります。
この記事では私が個人的にbad partsだと思う点とその回避策を挙げます。

# Play2のbad parts一覧

## activator(旧play)コマンド

play frameworkのチュートリアルでは[activatorコマンド](https://www.playframework.com/documentation/ja/2.3.x/NewApplication)のインストールを要求されます。
activatorコマンドはscalaを書く上で必要不可欠なビルドツールsbtのラッパーですが、追加されている機能は使われているのを聞いたことがない[activator ui](https://www.typesafe.com/activator/docs)とプロジェクトの新規作成テンプレートのみです。
play framework初心者でプロジェクトのテンプレートがほしい人がプロジェクトの新規作成をactivatorコマンドでやることは有益ですが、初心者(特にsbtコマンドにも不慣れな人)がactivatorを使い続けると何がactivatorの機能で何がsbtの機能なのかあやふやになりがちでよくありません。

### 対策: 直接sbtコマンドを叩きましょう

activatorコマンドが便利なのはほぼ新規作成時のみです。
また[こちら](https://www.playframework.com/documentation/ja/2.3.x/Build)を参考にすればactivatorコマンドを使用せずに1からplayプロジェクトを作成することもできます。

### ※2017/04 追記 activatorの開発が終了しました

開発元がやっとactivatorの失敗を認めたようで、[2017/05/24を持ってactivatorプロジェクトは終了しました](https://www.lightbend.com/activator/download)。
テンプレートなどを引き続き使いたい方はリンク先のガイドラインに従ってgiter8などから利用しましよう。

## play-json

play framework付属のデフォルトのjsonライブラリですが、お世辞にも使いやすいとは言いがたいです。

一例

* [Jsonオブジェクトへの要素の追加・削除などが非常に煩雑](https://www.playframework.com/documentation/ja/2.3.x/ScalaJsonTransformers)
* [Jsonオブジェクトの文字列表現の取得がインスタンスメソッドではなくJson objectのメソッド](https://www.playframework.com/documentation/2.4.x/api/scala/index.html#play.api.libs.json.Json$)

### 対策: 補完ライブラリを使用しつつ使い続ける or 代替ライブラリを使う

play-json-zipperなどの補完ライブラリを使えばある程度は不便さを緩和できます。
もしくは別のjsonライブラリを使う手もあります。個人的には[argonaut](http://argonaut.io/)がおすすめです。
ドキュメントが比較的少なく公式サイトのドキュメントもとっつきにくいですが、非常に筋のいいライブラリだと思います。

## config(hcon)

このconfig関連のエラーは非常に発見が難しいです。
書式が間違っている、値が存在しない、そもそもファイルが存在しないなどの場合でも標準ではなんのエラーも吐きません。
そのためfalse positiveや原因不明の不具合の元になりやすく、信頼性が低いです。

### 対策: なし

play framework自体がhconに不可分に依存しているので避ける手段はありません。
play framework固有の設定以外でそれでもhconを使いたくない場合は[twitter社のutil-eval](https://github.com/twitter/util)をおすすめします。hconのように曖昧な領域がなく非常に快適です。

## specs2

play 2.2までは標準のテストスイートだったspecs2ですが、はっきり言ってまったくオススメできません。
2種類のテスト記述方式をサポートするため、より簡略化して記述するため、など複数の理由によりそのコードは非常にデバッグ・拡張しづらいものになっています。
開発サイクルは早いのですが、[変更点の記述が甘く](https://github.com/etorreborre/specs2/tree/master/notes)ライブラリの更新によりnotesに書かれていない理由でコンパイルできなくなることもあります。
またテスト結果の出力も改行の位置にバグがあったりと、複雑すぎる機能をメンテナンスしきれていない印象を受けます。
(play frameworkの標準テストスイートから外れたのはその辺りの問題があったためと推測します)

### 解決策: scalaTestを使いましょう

[play推奨test frameworkのもう一方であるscalaTest](https://www.playframework.com/documentation/2.4.x/ScalaTestingYourApplication)を使用しましょう。
specs2より構造がシンプルながら記述方法にも柔軟性があり、周りの複数のscalaプロジェクトではこちらを使用/specs2から移行することが多く評判も良いです。

## プラグイン機構(play 2.4で廃止されました)

プラグインの仕組みが悪いのか、テスタビリティ低下の主要な原因の1つになっています。
テスト中でWithServer, WithApplicationを使っていると、プラグイン周りで問題が発生してテストが正常に実行できないことがあります。

### 解決策: できるならプラグインの使用を避ける

避けられるようであれば使わないようにしましょう。
2.4ではプラグインは廃止され、プラグインを改良したモジュールになったそうです。
なのでこちらを使う手もあります。
（ただ私はモジュールをそれほど使っていないので、問題がないかはわかりません）

## ws(websocketライブラリ)

2.3未満ではwsはplay frameworkへ[依存性べったり](http://stackoverflow.com/questions/24881145/how-do-i-use-play-ws-library-in-normal-sbt-project-instead-of-play)の機能でした。
2.4までにライブラリとしての切り出し・global stateへの依存性の切り離しなどで単体としてもかなり使いやすくはなりましたが、それでもまだ単体で快適に使えるライブラリとはいえません。

### 解決策: dispatchを使う

scala用http clientライブラリのデファクトスタンダードである[dispatch](https://github.com/dispatch/reboot)を使いましょう。
これなら単体でも非常に楽に使用することができ、ドキュメントも比較的豊富です。

### 2018/07/26追記

dispatchより使いやすい[softwaremill/sttp: The Scala HTTP client you always wanted\!](https://github.com/softwaremill/sttp)が最近出ました。こちらが今はおすすめ。

## 外部DSLであるroutesファイル

[scalaのroutesファイルは外部DSL(言語外DSL)](https://www.playframework.com/documentation/2.4.x/ScalaRouting)であり、正規表現によるroutingなどかなり複雑なこともできます。
が、routesファイル内で改行ができない、引数の書式がscalaでもjavaでもなく混乱を引き起こしやすいなど欠点が多いです。

### 解決策: routesに複雑な設定を書かない

正規表現やOption、デフォルト値などの複雑な処理はroutesに書かないようにしましょう。
それらはコントローラでもできます。コントローラ内であればscalaコードで書けますし、frameworkへ過度に依存せずにすみます。

## play独自のディレクトリ構造

[sbtはmavenと同じディレクトリ構造を採用しています](http://www.scala-sbt.org/0.13/tutorial/Directories.html#Source+code)。
しかしplayはそれを独自に変更しており、sbt標準のそれとは違います。

### 解決策: 諦める or 2.4以降なら対策を入れる

build.sbtでplayframeworkが書き換えた設定を元に戻すこともできますが、
煩雑なので基本的にはplayの方式に慣れざるをえません。

・・・と思っていましたが、[play2.4でsbt標準に戻す方法が提供されました](https://www.playframework.com/documentation/2.4.x/Anatomy)。