skinny事始め

scalaフレームワークで簡単にとっつきやすそうのを探したらskinnyが良さそうだったので、触り始めた際のメモを残しておきたいと思います。

skinnyインストール(mac)

$ brew updated $ brew install skinny

プロジェクト作成から起動

$ 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
  }
}

上記のssp_testを呼び出してjson形式のレスポンスが帰ることを確認