ScalaJPのメーリングリストで話題に上がったので、Scala製WEBフレームワークツールキットのUnfilteredで遊んでみました。
Unfilteredの公式サイトを進めていったメモです。
ちょっと日本語の情報少なすぎませんか!
ゆろよろ先生が0.33のドキュメントを翻訳してた
この記事でやったこと
- Unfilteredで遊べるように環境を整えた
- Sinatraのサンプルっぽいのを試した。"/hello/任意のid/"にブラウザでアクセスすると"Hello, #{入力したID}"と表示させるだけ。
間違いの指摘は大歓迎。
導入
学習用ファイルが用意されたリポジトリがあるので、これを使わせてもらうことに。導入は例によってgiter8を利用します。
$ g8 softprops/unfiltered --name=justplayin $ cd justplayin $ sbt console
HelloWorldする前に、Unfilteredで作るサーバーの大まかな概要を説明します。
- 対応するリクエストと、レスポンス(ただの文字列/HTML/その他のデータ)を定義したモノ(Intent)をつくる
- IntentはPartial Functionのように合成ができる。合成したものをintent()メソッドに紐付けたobject(Plan)をつくる。
- 開発用サーバーを定義したクラスにPlanをくっつけて(Planも合成可)、サーバーを起動する。
細かいルーティングをたくさん書いて、組み合わせていく、というスタイルなのでしょうか。
ではとりあえずHelloWorldしてみましょう。sbt console した状態で実行して下さい。(ファイルのmainに書いてもできると思いますが)
import unfiltered.request._ import unfiltered.response._ val hello = unfiltered.filter.Planify { case _ => ResponseString("Hello, world!") } unfiltered.jetty.Http.local(8080).filter(hello).run()
localhost:8080/#{適当なアドレス} にアクセスしてみて下さい。どのパスに行っても、Hello, world! と返すだけの無意味なサーバーの完成です。
で、IntentとPlanというのはどこに行ったんでしょう?実はもう書いてます。
val hello = ... の部分は、これと一緒です。
object Hello extends unfiltered.filter.Plan { def intent = { case _ => ResponseString("Hello, world!") } }
小文字のクラスを作るのがアレだったのでhを大文字にしましたが、同じものが出来ました。
もちろん、先ほどと同じ方法でサーバーを起動できます。
では次に、/hello にだけ挨拶して、ほかは Not Found! と表示するのを書いてみましょう。
あ、面倒なので、unfiltered.filter.Planifyのほうでやりますね。
val hello = unfiltered.filter.Planify { case Path(Seg("hello" :: Nil)) => ResponseString("Hello, world!") case _ => ResponseString("Not Found!") }
Path とか Seg とかは request matcher と呼ばれているようです。(内部的には、ただのExtractor)
Path(Seg("hello" :: Nil)) で /hello に対応しています。
あと、"Not Found!"のところだけど、「404を返す」というオブジェクトを ~> というメソッドで連結したりできます。(でも、またそこまで勉強が進んでいないので勘弁してください)
せっかくなので、分離してから合成してみましょう。
val hello = unfiltered.filter.Planify { case Path(Seg("hello" :: Nil)) => ResponseString("Hello, world!") } val not_found = unfiltered.filter.Planify { case _ => ResponseString("Not Found!") } unfiltered.jetty.Http.local(8080).filter(hello).filter(not_found).run()
関数合成によって、ルーティングが設定出来ました。
最後に、"/hello/任意のid"にブラウザでアクセスすると"Hello, #{入力したID}"と表示させるようにしてみましょう。
Extractorを使うことで、文字列をキャプチャーできます。
ついでに、"/hello"にアクセスしたときは"Hello, world!"と返すようにしてみました。
import unfiltered.request._ import unfiltered.response._ val hello = unfiltered.filter.Planify { case Path(Seg("hello" :: name :: Nil)) => ResponseString("Hello, %s!" format name) case Path(Seg("hello" :: Nil)) => ResponseString("Hello, world!") } val not_found = unfiltered.filter.Planify { case _ => ResponseString("Not Found!!") } unfiltered.jetty.Http.local(8080).filter(hello).filter(not_found).run()