Lift フレームワークを使って Comet をためす(1)
Lift の練習として、Comet を使用してみることにした。最終的には Comet によるチャットが作れればいいかな・・・(希望)
とりあえずは、表示されている時間を Comet を使用して1秒毎に更新してみる。
情報源は id:yuroyoro さんのエントリと、TheLiftBook.pdf*1(以後、PDFと記す)を使用。
Comet によるチャットは Lift の wiki にもあるのだけど、そこはスルーの方向で。
雛形作成
お約束だけど、mvn の archtype:generate で雛形作成。
Windows の場合、\ で次行に入力を続けることが出来ないので、\ を除去して一行で入力。
>mvn archetype:generate -U \
-DarchetypeGroupId=net.liftweb \
-DarchetypeArtifactId=lift-archetype-blank \
-DarchetypeVersion=1.0.2 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=sandbox.comet \
-DartifactId=comet-chat \
-Dversion=1.0-SNAPSHOT
これで、comet-chat というディレクトリ下に、ずらずらっと雛形が作成される。mvn jetty:run
で試しに実行できる。
とりあえず Comet を試す
フォームの扱いとかモデルとか全部すっとばして、PDF の 9.5 章あたりを参考に、とりあえず Comet を試してみる。
index.html に comet タグを追記
index.html に以下を追記する。lift:comet はページに CometActor を配置するタグ。type でクラス名を指定する。
このクラスは Boot クラス(src/main/scala/bootstrap/liftweb/Boot.scala)で指定したパッケージ配下の comet パッケージから検索される・・・のだと思う。
<lift:comet type="Messages"> Hello COMET!! - <msg:time>Missing time.</msg:time> </lift:comet>
Messages Comet アクターを追加
Messages クラス(src/main/scala/sandbox/comet/comet/Messages.scala)を追加する。<lift:comet type="Messages"/> と対応するため、CometActor トレイトを継承する。PDF との内容をほぼそのまま流用する。
package sandbox.comet.comet import _root_.net.liftweb.http.CometActor class Messages extends CometActor
defaultPrefix をオーバーライドしておく。<lift:comet>の内側で、処理対象とするタグの接頭子を指定する模様。
Full は Box の子クラスであり、Box は Option と同じ目的のものだが、いくつか機能が追加されている*2。None に対応するのは Empty。
ちなみに、scala では import をもっと短くかけるのだが、Lift 学習中なのであえてFQCNで書いている。
import _root_.net.liftweb.util.Full class Messages extends CometActor { override def defaultPrefix = Full("msg") }
続いて、render メソッドを追加する。<lift:comet/>タグのレンダリング時に呼び出されるので、<msg:time/>を span タグで囲まれた日時に置き換える。span タグにはあとで部分的な更新ができるように id 属性で Id を設定しておく。
import _root_.net.liftweb.util.Full import _root_.net.liftweb.util.TimeHelpers.now class Messages extends CometActor { override def defaultPrefix = Full("msg") def render = bind("time" -> <span id="time">{now}</span>) }
一度、この状態で動かしてみる。ページにアクセスした時点での日時が表示されるはず。
1秒毎に更新する
1秒毎に Messages アクタに対してメッセージを送信して、現在時刻の表示を更新する。アクタ開始時に呼び出される localSetup で、1 秒後にメッセージが届くようにスケジュールしておく。
また、lowPriority メソッドをオーバーライドして、メッセージが届くたびに現在日時を更新する。更新処理は SetHtml によって指定された要素にテキストノードを設定することで実施している。SetHtml は JsCmd の一つであり、クライアント側で Javascript を実行するものと思われる。
package sandbox.comet.comet import _root_.net.liftweb.http.CometActor import _root_.net.liftweb.http.js.JsCmds.SetHtml import _root_.net.liftweb.util.ActorPing import _root_.net.liftweb.util.Full import _root_.net.liftweb.util.TimeHelpers.now import _root_.net.liftweb.util.Helpers.TimeSpan import _root_.scala.xml.Text class Messages extends CometActor { override def defaultPrefix = Full("msg") override def render = bind("time" -> <span id="time">{now}</span>) override protected def localSetup = ActorPing.schedule(this, 'update, TimeSpan(1000)) override def lowPriority: PartialFunction[Any, Unit] = { case 'update => { partialUpdate(SetHtml("time", Text(now.toString))) ActorPing.schedule(this, 'update, TimeSpan(1000)) } } }
やっていることは PDF の内容となんら変わらないので、細かい部分は PDF を参照。
次はログインなし、ログなしで、メッセージを送りあうだけのチャットをつくる予定。
*1:http://groups.google.com/group/the-lift-book
*2:Emptyのときに失敗メッセージに変換するとか