120 likes | 277 Views
Scala + Lift フレームワーク その2. Scala の概要. JVM 上で動作するオブジェクト指向+関数型言語 JVM のスケーラビリティを適用できる Java との相互利用が可能 trait を利用した多重継承( Mix-In )が可能. Lift の概要 (復習). フルスタックの Web アプリケーションフレームワーク 簡潔な関数型 Web フレームワーク Snippet アプローチ View を中心としたアーキテクチャ. Lift で の チャットアプリケーションに機能追加. メッセージを削除する機能を追加する
E N D
Scalaの概要 • JVM上で動作するオブジェクト指向+関数型言語 • JVMのスケーラビリティを適用できる • Javaとの相互利用が可能 • traitを利用した多重継承(Mix-In)が可能
Liftの概要(復習) • フルスタックのWebアプリケーションフレームワーク • 簡潔な関数型Webフレームワーク • Snippetアプローチ • Viewを中心としたアーキテクチャ
Liftでのチャットアプリケーションに機能追加Liftでのチャットアプリケーションに機能追加 • メッセージを削除する機能を追加する • LiftのJqJsCmdsを利用してメッセージの処理を行う
追加する定義 ① • sealedtrait ChatCmd • object ChatCmd { • implicitdef strToMsg(msg: String): ChatCmd = • new AddMessage(Helpers.nextFuncName, msg, new Date) • } • finalcaseclass AddMessage(guid: String, msg: String, date: Date) extends ChatCmd • finalcaseclass RemoveMessage(guid: String) extends ChatCmd ② ③ ① seald を指定することで、同一ファイル内からしか継承できなくなる。プラス、分岐漏れをコンパイル時に検知できるようになる。 ② implicit とは、暗黙変換の定義。必要なときに、自動的に変換処理を行ってくれる ③ caseクラスとは、コンストラクタの引数を自動的にフィールド定義してくれる。また、マッチングに使用でき、比較メソッド等を持つ。
チャットサーバーオブジェクトの定義 • object ChatServer extends LiftActor with ListenerManager { • privatevarmessages: List[ChatCmd] = List("ようこそチャットルームへ") • def createUpdate = messages • overridedef lowPriority = { • cases: String => messages ::= s ; updateListeners() • cased: RemoveMessage => messages ::= d ; updateListeners() • } • } ④ ⑤ ④ メソッドの内容がcase文だけであれば、引数は省略し、その引数をマッチングにそのまま使用できる。Scalaでは省略できるものは基本的に省略するスタイルが多い。 ⑤ ChatCmdのリストであるmessagesに、String型の s を追加しようとしている。普通であればコンパイルエラーとなるはずだが、②の暗黙変換が利用され、AddMessageに変換されて追加される。
Chat コンポーネントの定義 1 • class Chat extends CometActor with CometListener { • privatevarmsgs: List[ChatCmd] = Nil • privatevarbindLine: NodeSeq = Nil • def registerWith = ChatServer • overridedef lowPriority = { • casem: List[ChatCmd] => { • valdelta = 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 • privatedef 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:surroundwith="default"at="content"> • <lift:comettype="Chat"> • <ulid="ul_dude"> • <chat:line> • <li><chat:msg/><chat:btn/></li> • </chat:line> • </ul> • <lift:form> • <chat:input/> • <inputtype="submit"value="chat"/> • </lift:form> • </lift:comet> • </lift:surround>
総評 • SnippetとJqJsCmds で、完全に隠蔽されたAjaxとjqueryの処理実装が可能! • 相変わらず、scala自体が難しいので、あんまり楽になった気はしない • 実コーディング量はかなり少なくなるため、玄人なら開発効率向上するかも?