テンプレートをメニューに登録する
最後に、作成したテンプレートをメニューに登録します。「src/main/scala/bootstrap/liftweb/Boot.scala」を編集します。
Boot.scala
は、Liftにおけるすべての設定を行うためのクラスです。mavenコマンドでLiftプロジェクトを生成すると、既にDBの設定や文字コードをUTF-8に変更する設定があらかじめ定義されています。
ここでは、作成したtwit.html
を登録します。メニューの登録を行うには、LiftRules.setSiteMap
関数に、SiteMapオブジェクトを渡します。SiteMap
オブジェクトは、Menu
型のList
を引数に取りますので、リスト8のようにtwit.html
を登録します。
class Boot { def boot { ... // entriesはList[Menu]型 // Menu( Loc( "システムで一意な名称", // "テンプレートへの相対パス", // "メニュー表示用文字列) ) // の形でメニューを追加する val entries = Menu(Loc("Home", List("index"), "Home")) :: Menu(Loc("Twit", List("twit"), "ついったーのようなもの")) :: User.sitemap // LiftRules.setSitemapに上で作成したList[Menu]型の // オブジェクトを渡して登録する LiftRules.setSiteMap(SiteMap(entries:_*)) ... } }
メニューの登録は、Menu( Loc( "システムで一意な名称",List("テンプレートへの相対パス"),"メニュー表示用文字列) )
の形式でMenu
オブジェクトを生成して、List[Menu]
型の変数entries
に追加しておきます。このentries
を引数にSiteMap
を生成して、LiftRules
に登録しています。
動作確認
プロジェクトディレクトに移動し、「mvn jetty:run」コマンドで開発サーバーを起動します。サーバー起動後に、http://localhost:8080/にアクセスすると、追加されたメニューが表示されていることが確認できるでしょう。
追加されたメニューをクリックすると、次のような画面が表示され、テキストエリアに入力して[投稿する]ボタンを押すたびに、下の部分に入力した文字列が追加されていくのが確認できるでしょう。
Function Mappingについて
従来のMVC型のWebアプリケーションフレームワークと異なり、Liftではボタンのクリックやフォームの送信など、ブラウザ上のイベントを直接scalaで書かれた関数に実行させることができます。この機能を、Function Mappingと言います。
bind("twit", xhtml, "name" -> username, "status" -> textarea( "", m => message(Full(m)), "cols" -> "40", "rows" -> "3"), "submit" -> submit( "投稿する", () => { Twit.add( message ) }) *1 )
上記のリスト9は、先ほどのTwit.post
関数の一部ですが、SHtml
オブジェクトのtextareta
関数やsubmit
関数の第2引数で渡している(String) => Any
型や() => Any
型の関数オブジェクトを渡しています。
ブラウザでボタンを押してフォームをサブミットすると、サーバー側でこれらの関数が実行されているように見えます。実際にそのとおりなのですが、この動作は従来のMVC型のフレームワークになれている方には、不思議に思えるかもしれません。
実は、この動作はFunction Mappingという機能で実現されています。
Liftでは、テンプレートをレンダリングする過程で、任意のHTML要素のイベントに関数オブジェクトを割り当てることができます。上記の例ですと、submitボタンが押されてフォームが送信された場合に、*1で無名関数としてテンプレートを生成したときに作成された() => { Twit.add( message ) }
が実行されます。
この動作を図にすると、次のような関係になります。
Liftでは、SHtml#Submit
関数などで渡された関数オブジェクトは、AFuncHolder
クラスのサブクラスに変換されて、セッション上に保持されます。セッションに登録する際に、関数オブジェクトごとにユニークなトークンを生成しておいて、ブラウザ上の要素に埋め込みます。先ほどのフォーム画面として生成されたHTMLソースを見てみると、textarea要素やsubmit要素のname属性に、name="F931561949122WAG"
のようなトークンが設定されているのが確認できると思います。このトークンはLiftが生成して関数オブジェクトと関連づけているのです。
以下のリスト10は、リスト9のSnippetが生成したHTMLです。textareaタグやsubmitタグに、name="F765221706401EBZ"
のようなトークンが設定されているのが分かると思います。このトークンを含めたHttpリクエストを送信すると、Liftがトークンに対応する関数オブジェクトを呼び出すことで、あたかもボタンを押したときに設定した関数が実行されているように見えるのです。
<form method="post" action="/twit"> <h2>こんにちわ!Guestさん!</h2> <h2>いま何してる?</h2> <ul class="status"> <li><textarea cols="40" name="F765221706401EBZ" rows="3"></textarea></li> <li><input name="F765221706402ZPT" type="submit" value="投稿する" /></li> </ul> </form>
Function Mappingを利用することで、Liftではテンプレートのレンダリングとイベント処理を明確に分離させることができるようになっています。Function Mappingは、通常のHttpリクエストのほかに、AjaxやCommetと組み合わせることで、ブラウザ上のイベントとサーバ上の処理をシームレスに結合させることができるのです。
まとめ
今回は、LiftのView層について、最も基本的な内容を解説しました。実は、このほかにもLiftが提供する機能として、ajax/cometのサポートや、画像などのバイナリデータの作成方法、RestFulなURLへのマッピングなどさまざまなものがあります。可能であれば、本連載で必要に応じて解説していきたいと思います。
今回は作成したTwitterのクローンアプリケーションは、投稿されたメッセージがメモリ上に保持されていたり、認証されていなかったり、スレッドセーフでなかったりとさまざまな問題を持っています。この連載を通して、Liftの機能を利用しながらアプリケーションをカスタマイズしていきます。
次回は、Liftのモデルを利用して、このアプリケーションがデータベースを利用するようにカスタマイズしながら、Liftのモデルについて解説する予定です。
参考資料
- The Scala Programing Language
- Lift
- 『Programming in Scala』 Martin Odersky・Lex Spoon・Bill Venners 著、Artima Inc、2008年11月
- 『The Definitive Guide to Lift: A Scala-based Web Framework』 Derek Chen-Becker・Tyler Weir・Marius Danciu 著、Apress、2009年5月