skinny事始め
scalaのフレームワークで簡単にとっつきやすそうのを探したらskinnyが良さそうだったので、触り始めた際のメモを残しておきたいと思います。
skinnyインストール(mac)
プロジェクト作成から起動
$ skinny new プロジェクト名 $ cd プロジェクト名 $ ./skinny run
ブラウザから'http://localhost:8080/‘にアクセスしてページが表示されることを確認
ビルド
jetty内臓のjar生成
$ ./skinny package:standalone
デフォルト設定ではstandalone-build/target/scala-x.xxに実行可能jarが出力されることを確認済み
起動
$ ./skinny run
起動スクリプトにてjettyをcontainer:restartしているのを確認
デバッグ
$ ./skinny debug
sbt debugで5005ポート指定でjettyを起動しておりideaから5005ポート指定でリモートでバックできるのを確認。
開発
コントローラー
package controller import skinny._ class RootController extends ApplicationController { def index = { set("name", "jjug") render("/root/index") } }
set(“name”, “jjug”)の部分でリクエストスコープにkey=nameでjjugをセットしている。renderではsspのページを指定している。sspはscalateのテンプレートエンジンで以下を参考にする。 http://scalate.github.io/scalate/documentation/ssp-reference.html#syntax
<%@val name: String%> <h3>Hello ${name}!</h3> <hr/> <p> Your first Skinny app works! </p>
上記のようなsspであればリクエストスコープせセットしたnameがApplicationControllerで指定したjjugに置換して表示される。
ルート
skinnyでURLからメソッドを呼び出す場合、ApplicationControllerを拡張したクラス内にメソッドを追加しておく。
class RootController extends ApplicationController { def index = { set("name", "jjug") render("/root/index") } def ssp_test = { set("name", "jjug") render("/root/ssp_test") } }
それから、サーブレットコンテキストに紐付ける。
package controller import skinny._ import skinny.controller.AssetsController object Controllers { def mount(ctx: ServletContext): Unit = { root.mount(ctx) AssetsController.mount(ctx) } object root extends RootController with Routes { val indexUrl = get("/")(index).as('index) val sspTestUrl = get("/ssp_test/?")(ssp_test)as('ssp_test) } }
上記の例ではURLとメソッドをマッピングさせたrootオブジェクトをroot.mount(ctx)でサーブレットのコンテキストに追加している。get(“/ssp_test/?”)(ssp_test)as(‘ssp_test)の最初のssp_testの方はメソッドの実態でas('ssp_test)の部分はbeforeAction,afterActionで必要になるようで詳しくは以下を参照する。 http://skinny-framework.org/documentation/controller-and-routes.html
Jsonレスポンスを返す
spray-josnでjson形式のレスポンスを返してみる。一応skinny自身のutilにもjsonのパーサーは入っているのだが、とりあえずspray-jsonの方で試してみる。 まずbuild.sbtに以下を追記する
libraryDependencies += "io.spray" %% "spray-json" % "1.3.1"
それから暗黙の型変換で戻り値の形式を指定した上で以下のようにjsonレスポンスを返してみる。
class RootController extends ApplicationController { implicit object AnyJsonFormat extends JsonFormat[Any] { def write(x: Any) = x match { case n: Int => JsNumber(n) case s: String => JsString(s) case b: Boolean if b == true => JsTrue case b: Boolean if b == false => JsFalse } def read(value: JsValue) = value match { case JsNumber(n) => n.intValue() case JsString(s) => s case JsTrue => true case JsFalse => false } } def index = { set("name", "jjug") render("/root/index") } def ssp_test = { contentType = formats("json") val map = Map("japan" -> "tokyo", "america" -> "chicago", "denmark" -> "kopenhagen") map.toJson } }