Scala + Lift - ForeFrontier

Scala + Liftフレームワーク
その2
Scalaの概要




JVM上で動作するオブジェクト指向+関数型言語
JVMのスケーラビリティを適用できる
Javaとの相互利用が可能
traitを利用した多重継承(Mix-In)が可能
Liftの概要(復習)

フルスタックのWebアプリケーションフレームワーク

簡潔な関数型Webフレームワーク

Snippetアプローチ

Viewを中心としたアーキテクチャ
Liftでのチャットアプリケーションに機能追加


メッセージを削除する機能を追加する
LiftのJqJsCmdsを利用してメッセージの処理を行
う
追加する定義
①
sealed trait ChatCmd
object ChatCmd {
②
implicit def strToMsg(msg: String): ChatCmd =
new AddMessage(Helpers.nextFuncName, msg, new Date)
}
③
final case class AddMessage(guid: String, msg: String, date: Date) extends ChatCmd
final case class RemoveMessage(guid: String) extends ChatCmd
① seald を指定することで、同一ファイル内からしか継承できなくなる。プラス、分岐漏れをコ
ンパイル時に検知できるようになる。
② implicit
とは、暗黙変換の定義。必要なときに、自動的に変換処理を行ってくれる
③ caseクラスとは、コンストラクタの引数を自動的にフィールド定義してくれる。また、マッチ
ングに使用でき、比較メソッド等を持つ。
チャットサーバーオブジェクトの定義
object ChatServer extends LiftActor with ListenerManager {
private var messages: List[ChatCmd] = List("ようこそチャットルームへ")
def createUpdate = messages
override def lowPriority = {
④
⑤
case s: String => messages ::= s ; updateListeners()
case d: RemoveMessage => messages ::= d ; updateListeners()
}
}
④ メソッドの内容がcase文だけであれば、引数は省略し、その引数をマッチングにそのまま使用
できる。Scalaでは省略できるものは基本的に省略するスタイルが多い。
⑤ ChatCmdのリストであるmessagesに、String型の s を追加しようとしている。普通であればコ
ンパイルエラーとなるはずだが、②の暗黙変換が利用され、AddMessageに変換されて追加され
る。
Chat コンポーネントの定義 1
class Chat extends CometActor with CometListener {
private var msgs: List[ChatCmd] = Nil
private var bindLine: NodeSeq = Nil
def registerWith = ChatServer
override def lowPriority = {
case m: List[ChatCmd] => {
val delta = m diff msgs
msgs = m
updateDeltas(delta)
}
}
Listの型をChatCmdに変更。
LowPriorityの中では、ローカルのリストと新しいリストの差異をとり、ローカルのリストを更新
後にupdateDeltasに差異を渡している。updateDeltasは後述。
Chat コンポーネントの定義 2-1
def updateDeltas(what: List[ChatCmd]) {
partialUpdate(what.foldRight(Noop) {
case (m: AddMessage , x) =>
x & AppendHtml("ul_dude", doLine(m)) &
Hide(m.guid) & FadeIn(m.guid, TimeSpan(0),TimeSpan(500))
case (RemoveMessage(guid), x) =>
x & FadeOut(guid,TimeSpan(0),TimeSpan(500)) &
After(TimeSpan(500),Replace(guid, NodeSeq.Empty))
})
}
updataDeltasの引数としてwhatを定義。whatのfoldRidghtを呼び出す。
foldRightとは、二項関数を使い、引数からはじまりリストの全ての要素を右から左に結合する。
foldRight [B](z : B)(f : (A, B) => B) : B
particalUpdateは、引数としてJsCmdを受け取る。つまり、foldRightの二項関数下で、リスト内部
を一つのJsCmdオブジェクトに変換を行う。
Chat コンポーネントの定義 2-2
def updateDeltas(what: List[ChatCmd]) {
partialUpdate(what.foldRight(Noop) {
case (m: AddMessage , x) =>
⑥
x & AppendHtml("ul_dude", doLine(m)) &
Hide(m.guid) & FadeIn(m.guid, TimeSpan(0),TimeSpan(500))
case (RemoveMessage(guid), x) =>
x & FadeOut(guid,TimeSpan(0),TimeSpan(500)) &
After(TimeSpan(500),Replace(guid, NodeSeq.Empty))
})
}
Noopはjqueryの何もしないという空の処理。
二項関数は、現在のリスト要素と、今までの累積値の2つの引数を受け取る。
関数内で、追加と削除処理をcaseクラスによるマッチングで分岐させ、それぞれのJsCmdオブジェ
クトを作成し、&メソッドにより結合していく。
⑥ doLineは追加するHtmlを作成する処理。後述。
Chat コンポーネントの定義 3
private def doLine(m: AddMessage): NodeSeq =
bind("chat", addId(bindLine, m.guid),
"msg" -> m.msg,
“btn” -> SHtml.ajaxButton(“削除",
() => {
ChatServer !
RemoveMessage(m.guid)
Noop}))
doLineは、メッセージを表示するためのhtmlタグを作成する。
この例では、メッセージごとにそれを削除するボタンを出力。
ajaxButtonのラベルに「削除」を指定し、ボタン押下時の処理として、削除メッセージをbangメソ
ッドでChatServerに送信、最後にNoopを指定(こうしないとJsCmdのオブジェクトとならないた
め)
Index.htmlの中身
<lift:surround with="default" at="content">
<lift:comet type="Chat">
<ul id="ul_dude">
<chat:line>
<li><chat:msg/> <chat:btn/></li>
</chat:line>
</ul>
<lift:form>
<chat:input/>
<input type="submit" value="chat"/>
</lift:form>
</lift:comet>
</lift:surround>
総評



SnippetとJqJsCmds で、完全に隠蔽されたAjax
とjqueryの処理実装が可能!
相変わらず、scala自体が難しいので、あんまり楽
になった気はしない
実コーディング量はかなり少なくなるため、玄人な
ら開発効率向上するかも?