sgykfjsm.github.com

Scalatraでいろいろやる その3

今までServlet内にベタ書きしていたHTMLを外出しする。

Scalatraの場合はScalateを導入することで以下のテンプレートエンジンを使うことができる。

  • Ssp (Scala Server Pages) VerocityとかJSPみたいな記述が可能。他のテンプレートエンジンとくらべて一番HTMLっぽい。
  • Scaml (Scala Markup Language) HamlのScala版。
  • Jade Scamlに似てるけど変数に%シンボルがつかないのでちょっと読みやすい。
  • Mustache 本家はこちら。実際に使ってなのでよくわかってないが、テンプレート内には制御文を入れないで、テンプレートに値を渡すServlet内でロジックを記述する形式っぽい。

基本的な使い方

これらのテンプレートエンジンを使うためにはServletにScalatraSupportをmix inする必要がある。

1
class MyFirstScalatraServlet extends ScalatraTestStack with ScalateSupport {

例えばsspを使いたい場合は以下のようにする。

1
2
3
4
get("/") {
  contentType="text/html"
  ssp("index.ssp")
}

ssp(“index.ssp”)src/main/webapp/WEB-INF/templates/views/index.sspを指している。
また、ベースのレイアウトとなるのは、src/main/webapp/WEB-INF/templates/layouts/defaluts.sspとなる。
レイアウトファイルではServletが返すレスポンスボディを受け取るように記述しなければならない。

1
2
3
4
5
6
7
8
<%@ val body: String %>
<!DOCTYPE html>
<html>
<head><title>Scaltra Sample</title></head>
<body>
  <%= unescape(body) %>
</body>
</html>

uneascapeは出力するHTMLをエスケープしないようにする。これをエスケープするとHTML文がそのままでるので、注意。

テンプレートに値を渡す

Servletからテンプレートに値を渡したいことはよくあることで、Scalateでは以下のようにやれば良い。

1
2
3
4
get("/get/:param") {
  contentType="text/html"
  ssp("get/param.ssp", "param" -> params("param"))
}

第2引数以降に“テンプレート内で使う変数名” -> “割り当てたい値”のようにすれば良い。

View側は以下のようになる。

1
2
3
4
5
<%@ val param: String %>
<h1>Scalatra POST Tutorial</h1>
<p>You submitted: <%= param %></p>
<hr/>
<a href="/">Beck To Index</a>

一行目でServletから受け取る値を宣言して、それを<%= 変数名 %>で使用する。

コンパイルされるとどうなるか

テンプレートのViewファイルはコンパイルされるとどうなるかはtarget/scala-2.10/src_managed/main/scalate/templates/viewsを見ると良い。例えば、上記で変数を受け取ったテンプレートの場合だと以下のようになる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* NOTE this file is autogenerated by Scalate : see http://scalate.fusesource.org/ */
package templates.views.post

object $_scalate_$param_ssp {
  def $_scalate_$render($_scalate_$_context: _root_.org.fusesource.scalate.RenderContext): Unit = {
    ;{
      val context: _root_.org.fusesource.scalate.RenderContext = $_scalate_$_context.attribute("context")
      import context._


      ;{
        val param: String = $_scalate_$_context.attribute("param")
        $_scalate_$_context << ( "<h1>Scalatra POST Tutorial</h1>\n<p>You submitted: " );
        $_scalate_$_context <<< (          param
 );
        $_scalate_$_context << ( "</p>\n<hr/>\n<a href=\"/\">Beck To Index</a>\n" );
      }
    }
  }
}


class $_scalate_$param_ssp extends _root_.org.fusesource.scalate.Template {
  def render(context: _root_.org.fusesource.scalate.RenderContext): Unit = $_scalate_$param_ssp.$_scalate_$render(context)
}

コンパイルされているのでちょっと見にくいが、Servletから受け取った値を$_scalate_$_context.attribute(“param”)で取り出して、あとは記述されたHTMLなどと一緒に$_scalate_$_contextに流し込んでいる様子が見て取れる。

制御文を使う

ここから先はSspのお話。

Sspでは${}<%= %>内に書かれたコードが評価されて出力される。加えて、Velocity形式として#{}による制御文もサポートしており、いわゆるifやforももちろん使えて、一般的なテンプレートエンジンと同じ使い勝手を持っている。
以下Sspと出力されるHTMLコードを比較する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<p><%= List("hello,", "World!").mkString(" ") %></p>
<p>#{
    import java.util.Date
    val now = new Date
    List("hello,", "Ssp on Scalate!").mkString(" ")
}#</p>
<p>the time is ${now}</p>
<p>${ "1 + 2 =" + (1+2)}</p>
<%
    var foo = "this"
    foo +=  " is"
    foo +=  " is a pen."
%>
<p>${foo}</p>
1
2
3
4
5
<p>hello, World!</p>
<p></p>
<p>the time is 2013年3月17日</p>
<p>1 + 2 =3</p>
<p>this is is a pen.</p>

Scalaのコードは<%= %>または${}あるいは#{}内に記述する。
しかし、出力されるHTMLをみてみると、標準出力されるものはHTMLに出力されていないことがわかる。
コンパイルされたソースを見てみよう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
object $_scalate_$Ssp_ssp {
  def $_scalate_$render($_scalate_$_context: _root_.org.fusesource.scalate.RenderContext): Unit = {
    ;{
      val context: _root_.org.fusesource.scalate.RenderContext = $_scalate_$_context.attribute("context")
      import context._


      $_scalate_$_context << ( "<p>" );
      $_scalate_$_context <<< (        List("hello, ", "World!").mkString(" ")
 );
      $_scalate_$_context << ( "</p>\n<p>" );

    import java.util.Date
    val now = new Date
    List("hello,", "Ssp on Scalate!").mkString(" ")


      $_scalate_$_context << ( "</p>\n<p>the time is " );
      $_scalate_$_context <<< (       now
 );
      $_scalate_$_context << ( "</p>\n<p>" );
      $_scalate_$_context <<< (        "1 + 2 =" + (1+2)
 );
      $_scalate_$_context << ( "</p>\n\n" );

後半は割愛しているが、#{}に記述したコードは$_scalate_$_contextに流し込まれていないことがわかる。
どうやら#{}に記述したコードはimport文や変数の格納などに使うようだ。

続いて、forとかif文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<ul>
#for(i <- 1 to 15)
    #if (i % 15 == 0)
    <li>${i}: FIzzBuzz</li>
    #elseif (i % 5 == 0)
    <li>${i}: Buzz</li>
    #elseif (i % 3 == 0)
    <li>${i}: Fizz</li>
    #else
    <li>${i}: -</li>
    #end
#end
</ul>

<ul>
    #for(i <- 1 to 5; j <- 1 to 2)
    <li>(${i}, ${j})</li>
    #end
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<ul>
    <li>1: -</li>
    <li>2: -</li>
    <li>3: Fizz</li>
    <li>4: -</li>
    <li>5: Buzz</li>
    <li>6: Fizz</li>
    <li>7: -</li>
    <li>8: -</li>
    <li>9: Fizz</li>
    <li>10: Buzz</li>
    <li>11: -</li>
    <li>12: Fizz</li>
    <li>13: -</li>
    <li>14: -</li>
    <li>15: FIzzBuzz</li>
    </ul>

<ul>
        <li>(1, 1)</li>
        <li>(1, 2)</li>
        <li>(2, 1)</li>
        <li>(2, 2)</li>
        <li>(3, 1)</li>
        <li>(3, 2)</li>
        <li>(4, 1)</li>
        <li>(4, 2)</li>
        <li>(5, 1)</li>
        <li>(5, 2)</li>
</ul>

ざっとだが、Scalatraでのテンプレートエンジンの使い方を概観した。
リファレンス見ながら以上を見てきたわけだが、Scalateというか、テンプレート側にもう少し機能があるかなぁとは思ったが、そんなことはなかった…。Twigぐらいは機能を持ってるかと思ったんだけどなぁ。調べ方が足りなかったかな?
とはいえ、他のテンプレートエンジンはまた別の機能を持っているを持っていたりするので、用途に合わせて使い分けていけばいいのだろうと思う。

まぁとりあえずということで、次はDBとの接続を試すことにする。試すならSlickかなぁ。