Liftの話の前に、明日仙台でscala勉強会@東北が立ち上がります。興味ある方、google-groupも参加してみてはいかがでしょうか。
さて、LiftでWebアプリを作成するときのフレームワークをざっくり説明すると、こうなる。

HTMLでのデザイン・遷移を決めておいて、動的に変化する箇所をsnippetで入れ替える。という開発手順になると思います。
snippetとは、xhtmlを生成するメソッドのこと。Railsなどでいうヘルパーメソッドと同等のものです。一般的なMVCフレームワークのControl(あるいはAction)に相当するものは、とりあえず「無い」と考えておきます。
上図のアプリケーションを作成するには、/src/mainの配下に
■/scala/firstapp/snippet/Hello.scala
package firstapp.snippet
class Hello {
def aTitle = <div>Welcome</div>
def aLink = <a href="/show">Show</a>
def aParagraph = <p>contents</p>
}
■/webapp/index.html
<html>
<body>
<lift:Hello.aTitle/>
<lift:Hello.aLink/>
</body>
</html>
■/webapp/show.html
<html>
<body>
<lift:Hello.aTitle/>
<lift:Hello.aParagraph/>
</body>
</html>
これらの部品をデプロイするために、ブートストラップにSiteMapを設定します。
■/scala/bootstrap/liftweb/Boot.scala
package bootstrap.liftweb
import net.liftweb.util._
import net.liftweb.http._
import net.liftweb.sitemap._
import net.liftweb.sitemap.Loc._
class Boot {
def boot {
LiftRules.addToPackages("firstapp")
val entries =
Menu(Loc("Home", "/", "Home")) ::
Menu(Loc("Show Blog", "show", "Show Blog"))::
Nil
LiftRules.setSiteMap(SiteMap(entries:_*))
}
}
以上を記述したら、mvn jetty:run でサーバを起動して、
http://localhost:8080/
http://localhost:8080/show
にアクセスして動作を確認します。
snippetをもうちょっといじってみる
scalaの特徴として、XMLをソースコード上に埋め込むと、それがそのままXMLオブジェクトになります。
val xml:NodeSeq =
<item>
<name>Gum</name>
<price>10</price>
</item>
これを踏まえ、snippetの型を厳密にして、もうちょっと「メソッド」ライクにいろんな書き方してみると、
■/scala/firstapp/snippet/Hello.scala
package firstapp.snippet
import scala.xml._
import blogonlift.lib.Formatter._
class Hello {
def aTitle( xhtml: Group ): NodeSeq = {
// xmlを返す
<h1>Welcome</h1>
<h2>Welcome</h2>
<h3>Welcome</h3>
}
def aLink( xhtml: Group ): NodeSeq = {
val label = "Show" //文字列をXMLに埋め込んで返す
val xml:NodeSeq = <a href="/show/1">{label}</a>
return xml
}
def aParagraph( xhtml: Group ): NodeSeq = {
val html:String = "<p>contents</p>" //文字列を
PCDataXmlParser.apply(html).open_! // XMLにパースして返す
}
}
Liftを使いこなすために
最初に気をつけるべき点は、
snippetのパラメータと戻り値は、
厳密なXMLでなければならない
ことではないでしょうか。厳密とは、scala.xmlパッケージがXMLオブジェクトとしてコンパイルできる形という意味。(※Validであれば良いのかというと、そうでもないクセのようなものがある気がしているが、まだはっきりしない)
たとえば、次のようなコードは、コンパイルエラーやランタイムエラーになります。
■悪いHTMLの例
<html>
<body>
<lift:Hello.some>
<div class="<lift:Hello.style/>"></div>
</lift:Hello.some>
</body>
</html>
■悪いsnippetの例
def list = {
<li>a
<li>b
}
def title = {
"mojiretsu" //XML以外を返しても処理できない
}
実際にこのブログサイトを製作してみての感想ですが、はっきり言って、この制約はツライです!条件によってstyleを変えるなどの凝ったHTMLにしたい場合は、liftは向かないフレームワークのように思います。
逆に向いているのは、データ指向なサイト。モデル-XML-HTMLが、ズバズバっと連動するので、気持ちよくコーディングできるような気がします。(ホントかどうかはまだ不明w)
コードを修正してデプロイするには
1つの端末で mvn jetty:run でしておいて、もう1つの端末で mvn compile します。
Jettyが新しいバイトコードをロードするタイミングは、pom.xmlで調整できます。
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>5</scanIntervalSeconds>
</configuration>
</plugin>
このエントリ付近を掘り下げたい方は、サンプルhellodarwinを解読すると良いと思います。
次はもう少し複雑なsnippetとHTMLを編みこむ方法と、リクエストやセッションのスコープ話の予定。そもそもsnippetって、どのスコープで動くんだ?みたいなことを調べる。
モデルの実装はもうちょっと先になる気がしてきた。