ブラウザとCometで接続するCometTwit Snippet
次に、ブラウザとCometで接続するCometTwit Snippetを作成します。LiftでCometを利用するSnippetは、プロジェクトで指定したパッケージ以下の「comet」パッケージ内に作成する必要があるので注意が必要です。
プロジェクトディレクトリ以下の 「src/main/scala/demo/twitterclone/comet/」ディレクトリに、「CometTwit.scala」というファイル名で新しいファイルを、以下のリスト5の通りに作成します。
package demo.twitterclone.comet
import _root_.scala.actors._
import _root_.scala.actors.Actor._
import _root_.scala.xml.{NodeSeq, Text }
import _root_.net.liftweb.http._
import _root_.net.liftweb.http.S._
import _root_.net.liftweb.http.SHtml._
import _root_.net.liftweb.util.Helpers._
import _root_.net.liftweb.util.{Box, Full}
import _root_.net.liftweb.http.js.JsCmds._
import _root_.net.liftweb.http.js.JE._
import _root_.net.liftweb.http.js.jquery.JqJsCmds._
import _root_.demo.twitterclone.model._
/**
* ブラウザとComet接続し、非同期にメッセージを
* 表示させるSnippet。
* CometActorを継承し、他のListenerManagerを継承した
* Actorに登録するため、CometListeneeトレイトを継承する。
*/
class CometTwit extends CometActor with CometListenee { // *1
// このSnippetのNameSpace
override def defaultPrefix = Full("twit")
// 非同期にメッセージを表示させるタグのID
private lazy val spanId = uniqueId + "_messages_span"
// SinpetがTemplateから呼び出された時、
// <Twit:messages/>タグの内容を出力する
def render = bind("messages" -> // *2
<span id={spanId}><div></div></span>)
// このActorを他のActorに登録する。
// ここでは、TwitServer Actorに登録
protected def registerWith = TwitServer // *3
// このActorがメッセージを受け取った時の処理
override def highPriority = { // *4
// caseクラス Messagesを受け取る
case Messages(msgs) =>
// partialUpdate関数にをPrependHtmlオブジェクト渡すことで、
// htmlを部分的に書き換えるJavaScrpitが生成される
partialUpdate( PrependHtml(spanId, // *5
<xml:Group>
{ msgs.map( m =>
<ul class="status">
<li class="message">{ m.status.is }</li>
<li class="user">{ userName( m.user.obj )}</li>
<li class="dateOf">{ m.dateOf.is.toString }</li>
<hr/>
</ul> )
}
</xml:Group>
))
}
/**
* Userモデルオブジェクトからユーザー名を取得するユーティリティ関数
*/
def userName( user:Box[User] ) = user.dmap( "Guest" ){ user =>
user.shortName
}
}
このCometTwit Snippetは、CometActorを継承し、CometListeneeトレイトを継承します。CometListeneeトレイトを継承しておくことで、他のListenerManagerを継承したActorに登録できるようになります。このCometTwitは、投稿されたメッセージを受信するために、先ほど作成したTwitServer Actorに登録する必要があるからです。
*2のreder関数は、このSnippetがテンプレートから呼び出された際に、表示するHTMLを生成するためのものです。Cometでメッセージを表示するため、初期表示では空の<span>タグを出力しているだけです。ただし、ここで出力している<span>タグは、spanIdフィールドで定義されたIDを、タグのIDにしています。このようにすることで、メッセージ受信時の処理でspanIDのタグに対して追加でメッセージを表示させることができるようになります。
*3のregisterWith関数では、このActorを他のActorに登録しています。先ほども述べた用に、TwitServerに登録する必要があるため、このように記述しています。実際の登録処理は継承しているCometListeneeトレイトが行います。
*4のhighPriority関数は、メッセージ受信時の処理です。関数の書き方は、先ほどのTwitServerクラスのhighPriority関数と同じ形式です。
このCometActorでは、メッセージとしてはcaseクラスMessagesを受信します。TwitServerに登録することで、TwitServerでupdateListeners()関数を呼び出されると、このActorにMessageモデルを内容として持つcaseクラスMessagesが送信され、最終的にこの関数が呼び出されます(この呼び出しは非同期です)。
では、メッセージ受信時の処理について解説します(*5)。
partialUpdate関数(CometActorトレイトがもつ関数)を呼び出すことで、指定したIDのタグの内容を部分的に書き換えることができます。
引数は、書き換える内容を渡します。直接HTMLを返してもよいのですが、ここでは<span>タグの先頭にメッセージを次々と追加していくために、PrependHtmlオブジェクトを渡しています。
PrependHtmlオブジェクトの第1引数には、書き換え対象のタグのIDを渡します。ここでは、表示の時に生成しておいた<span>タグのID(spanIDフィールドで定義)を渡します。
第2引数は、追加するHTMLです。受信したcaseクラスMessagesから、登録されたMessageモデルオブジェクトを取り出して、表示する内容を生成しています。
PrependHtmlオブジェクトは、Ajaxの例のところで説明したSetHtmlオブジェクトと同じく、JavaScriptを生成します。このように、partialUpdate関数にPrependHtmlオブジェクトのようなJavaScriptを生成するオブジェクトを渡すと、CometによりブラウザにJavaScriptが送信されて実行されます。
