はじめに

Play frameworkはご存知でしょうか?
Playは、現代のwebアプリケーション開発に必要なコンポーネント及びAPIを統合した生産性の高いJavaとScalaのwebアプリケーションフレームワークです。
公式ドキュメントより引用)

人によってはJavaの印象の強いこのPlayですが、Scalaでwebアプリケーションを作ることもできます。そこで今回はこのPlayを使ってScalaでwebアプリケーションを作ってみましょう。
ちなみに、今回はこの記事の作成段階での最新バージョン、Play 2.4.2を使って話を進めます。

インストール

Playを使うにはTypesafe Activatorを使うらしいのですが、別にこれがなくともsbtを直接使ってアプリケーションを作成することも可能です。

もしまだscalaやsbtをインストールしていない人がいれば、こんな感じでインストールしておいてください。

また、動作にはjava8が必要になるので、もし入っていない人がいれば合わせてインストールしておきましょう。

アプリケーションの新規作成

まずは新しいアプリケーションのために新しいディレクトリを作成し、まずはアプリケーションに必要な最低限のファイルを追加しましょう。各ファイルの内容とともに役割も紹介します。

plugins.sbtはビルドに必要なプラグインの読み込みです。
今回はplayをsbtで利用するためのsbt-pluginを導入します。合わせてplayの提供元であるTypesafeのリポジトリを参照できるように追加しておきます。

build.sbtはbuildの基本的な設定を行うファイルです。プロジェクト名やscalaのバージョン、アプリケーション自体のバージョンの他にPlayを使うためにpluginを有効化しておきます。
scalaVersionは自分がインストールした Scalaのバージョンをいれておけばとりあえず大丈夫かと思います。(インストールやバージョン確認はこの辺とかを参考にどうぞ)

次にHello Worldを返すだけの最低限のコントローラーとルーティングです。詳しい解説は以降の章でやるので、ひとまずはおまじないとでも思ってコピペしてもらえば大丈夫です。

あとは中身空っぽで良いので conf/application.conf というファイルを作っておいてください。
(これがないと怒られて起動できません・・・)

サーバーの動かし方

それでは早速動かしてみましょう。  $sbt run を実行すればさくっと動き出してくれます。
http://localhost:9000/ にアクセスしてみましょう。HelloWorldが表示されれば成功です。
runを実行するのに必要なファイルをダウンロードするため初回はとても時間がかかります。コンソールがさっぱり進まなくても焦らないで30分くらいは待ってみましょう。

一度runを実行してしまえば、以降はファイルの変更を自動で検知してコンパイルしなおしてから実行してくれます。ブラウザをリロードするだけでサクサク動作確認できて非常に便利です。

また、公式からの引用ですが、以下の.gitignoreファイルを置いておくと自動生成されたファイルが無視されるのでオススメです。

アプリケーションの基本的な実装

アクションとレスポンス

Actionはリクエストを処理してレスポンスを返す (play.api.mvc.Request => play.api.mvc.Result) 型の関数です。

ControllerはそのActionを生成するためのただのシングルトンオブジェクトです。

適当なレスポンスを追加してみましょう。先ほどのController/Application.scalaに以下の新しいActionを追加します。

エラーを見たいのでこのまま http://localhost:9000/hello にアクセスしてみましょう。( $sbt runがまだDL中の人は終わるまで待つか先に進みましょう。終了してしまった人は実行しなおしてください。)

どうでしょう?そっけない感じじゃなくて「ルーティング探したけれど無理でした><」というやや親切なエラーが出ます。

では親切なエラーに従ってroutesも追加しましょう。

こんな感じの一行をroutesに追記したらそのまま http://localhost:9000/hello に再度アクセスしてみましょう。どうですか?”World!!”が出ましたか?
おめでとうございます。あなたはこれで新しいレスポンスを一つ追加できました。

ルーティング

routesに追記することでルーティングを設定することができました。もっと柔軟な設定をすることもできます。例えば、クライアント ID を受け取るようなルートを定義する場合、URIパターンに次のような”動的パート”を使うことができます。

動的パートでは、 / 以外の文字が使えます。

受け取る側のActionでは以下のように定義します。これでidをAction内部で使うことができます。

また複数の/をまたいでパスをまとめて動的にする場合には、 *id という文法が使えます。

これで GET /files/images/logo.png のようなリクエストについて、 images/logo.png をnameで受け取ることができます。

URIパターンに書かずにアクションの呼び出しにパラメータを書いた場合、リクエストのURIに見つからなかった分はクエリから取得されることになります。(?page=hogehoge のような部分)

任意の正規表現を使いたい場合もあるでしょう。(数字のみのidだけひっかけたい場合等)
その場合、 $  と  <> を使うことで任意の正規表現でマッチングできます。 <> に挟まれた部分が正規表現になります。

この例だと 1桁以上の数字がマッチするでしょう。

レスポンス

さて、Actionで何も考えずに Ok("HelloWorld") を返した場合、Content-Typeはtext/plainがセットされることになります。これを任意で変更したい場合には as(newContentType) というメソッドを呼び出すことでそのタイプに変更ができます。

例)text/htmlで返す場合

その他のレスポンスを返したり、ヘッダーの変更やCookieの設定も簡単にできます。残りは公式ドキュメントで確認してみてください。

テンプレートエンジン

さて、実際のところWebアプリケーションを作る際にControllerで直接レスポンスを生成することは極めて稀でしょう。普通はなんらかのテンプレートエンジンを使用してHTML文章をさくっと生成して返すものかと思います。
PlayにはTwirlというScalaベースのテンプレートエンジンが含まれています。
(ちなみにたぶん、読みは「トゥアル」です。某禁書目録とはなんら関係はございません。)

簡単な例

このテンプレートは以下のようにあらゆるScalaコードから呼び出せるそうです。

ちなみに、タイプセーフです。コンパイルされます。実行してみたら配列のその添字は空っぽだった、みたいな悲しい事態は起こりません。

唯一の特殊文字”@”

テンプレートでは @ が唯一の特殊文字です。ここから先が動的なコード、つまりScalaと認識されます。終わりは自動的に推論される(!)ために、閉じタグなんかは必要ありません。

もし + などで結合したちょっと複雑なコードが使いたい場合は括弧を使います。さらに複数行に渡るようなコードの場合には中括弧を利用することができます。

公式より引用

また、この @ 自身を使いたいときは @@ と書けばエスケープできます。

テンプレートの先頭行はそのテンプレートの引数として扱われます。
また、関数と同じくデフォルト値を設定したり複数の引数グループを作成することもできます。

例)

制御構文、再利用

ifforも使い慣れた感じで自然に使うことができます。

例)

特定の処理を関数のようにまとめておいて再利用することもできます。

もちろん、古今東西どんなフレームワークであろうともviewにロジックを入れすぎると後で痛い目に会うのは共通です。ほどほどにしておきましょう。必要であればviewsパッケージ配下にでもScalaクラスは置くことができます。

簡単なwebアプリケーションの例

ただ表示するだけ

それでは、ごく簡単なview、controller、ルーティングを用意してHTMLを吐き出させてみましょう。

たったこれだけです。あとは http://localhost:9000/ にアクセスすれば、設定したtitleが入ったHTMLが表示されると思います。

タイプセーフを活かした例

これだけでは面白くないので、タイプセーフなテンプレートを活かした例です。ControllerとViewを編集してください。

ここで注目したいのは @import です。Itemで受け取って処理ができる、とはいえItemがなんなのかview側で知らなければコンパイルが通りません。そこで、Applicationで定義したItemをviewでimportすることでviewでもItemが使えるようになるのです。

どうでしょう?これならItemに存在しない item.category なんかを勝手に追加すればコンパイルが通らず気がつくことができますし、逆に item.ID を追加してもviewが壊れることがありません。modelの戻り値をviewにそのまま渡したりしているとviewが増えるにつれてこの手のエラーに気がつくのが難しくなりますが、タイプセーフであればコンパイルエラーという形でもれなく気がつくことができます。

まとめ

どうでしょうか?今回はmodel周りをまったく紹介しなかったためにDBを使ったちゃんとしたアプリケーションにはまだ距離がありました。しかし、viewとcontrollerだけでもかなり早く、かつ安全に実装できると感じたのではないでしょうか?

飛ばしてしまった項目も多く、公式ドキュメントだけでもまだまだたくさんの情報があります。ぜひこれをきっかけにいろいろ触ってみてください。