目次

Version 5, last updated by dpp at Nov 02 05:28 UTC

Lift Web は、Web ページのレンダリングに関数型プログラミングの概念を適用します。Lift は HTTP リクエストを出力の生成として扱うのではなく、入力の出力への変換として扱います。

HTML レンダリングパイプラインにおける最初の変換は、URL のテンプレートへの変換です (これは view first とも呼ばれます)。Scala の記法では、これは Req => NodeSeq となります (Req はリクエストで、NodeSeq は一連の XHTML または HTML5 ノードです)。

次に、テンプレートは LiftSession.processTemplate メソッドに渡され、このメソッドが次の変換を行います。
  • ファイルの最初のタグが <html> タグで、このタグに lift:content_id または l:content_id  のどちらかが含まれているか、あるいは <body> タグの class 属性に lift:content_id=xxx が含まれている場合、処理を開始するノードは、一致する id を持つノードになり、それ以外の場合にはテンプレート全体が処理されます。この機能は Lift 2.2-M1 で導入されたものである点に注意してください。
  • テンプレート内のそれ以外の各ノードは、LiftSession.processSurroundAndInclude を介して処理されます。lift または l という名前空間は、Snippet であることを示します。たとえば、<lift:hello_world some="attr">kids</lift:hello_world><l:HelloWorld>kids<l:HelloWorld> は、スニペット呼び出しの例です。Snippet は、スニペット呼び出しの kids を新たなノードに変換する関数です。コードで表すと、Snippet は NodeSeq => NodeSeq です。「スニペットの名前解決」を参照してください。
    • スニペットタグ内の属性は、S.attr プロパティを介して Snippet で利用できるようになります。各スニペットは、それぞれ異なる形で属性を扱います。スニペットの属性は下位スニペットでも保持されます。このため、属性を下位スニペットに渡すことができます。たとえば <lift:surround foo_bar="baz"/> のようにすると、foo_bar 属性は現在のスニペットの内側で処理されるスニペット群でも利用可能になります。これは、Surround スニペットやその他のすぐに評価されるスニペットで使うと便利です。
    • デフォルトでは、スニペットに渡される子ノード自体は、スニペットとしては評価されません。ただし、子ノードを渡すスニペットで lift:eager_eval="true" と指定すると、子ノードはスニペットとして内容が評価されてから、最初のスニペットの関数に渡されます。
    • スニペットは出現する順序で評価されます。ただし、複数のスニペットを並行して実行したい場合もあります。たとえば、広告サーバーなどの外部リソースにアクセスするスニペットなどがこれに該当します。スニペットを並行して実行するには、目的のスニペットで lift:parallel="true" と指定します。このように指定すると、スニペットは別のスレッドで実行されます。ただし、スニペットの実行が完了するか、タイムアウトになるのいずれかまで、ブラウザにページは返されません。タイムアウトは LiftRules.lazySnippetTimeout で設定されており、デフォルトは 30 秒です。
    • ページがレンダリングされた後に、lスニペットを実行して HTML ページに値を挿入したい場合は、<lift:LazyLoad/> スニペットを使ってください。
    • スニペットは再帰的に処理されます。これは、あるスニペットの結果が、引き続きほかのスニペットとして走査されることを意味します。そのため、スニペットでほかのスニペットを返すようにすれば、返されたスニペットが評価されるようにすることができます。
    • Lift 2.2-M1 以降では、スニペットは属性を介して呼び出すことができます。たとえば、<div class="bar l:snippet=hello_world?my_attr=foo"/> のように記述すると、属性 "my_attr" に "foo" が設定され、"hello_world" スニペットが呼び出され、<div class="bar"/> がスニペットの引数として渡されます。これは、以前のスニペットの呼び出し方と比べ、よりデザイナーフレンドリになっています。プリフィックス lift: または l: が付いたすべての class 属性は、スニペットとして扱われます。スニペットで属性が必要な場合には、属性リストとスニペット名を ? (疑問符) で区切ります。属性は、名前と値の対を = (等号) で区切ったもので、複数の属性は、; (セミコロン)、? (疑問符)、または & (アンパサンド。XML との互換性のために &amp; と記述する必要があります) のいずれかで区切ります。属性の名前と値は、URL エンコードされ、適切に XML エスケープが行われていなければなりません (したがって、& は &amp; になります)。
  • すべてのスニペットが実行された後、Lift はマージフェーズを実行します。マージフェースでは、次の処理が行われます。
    • <body> タグ内のすべての <head> 要素は、その子を <head> タグに移動させます。子に重複がある場合は取り除かれます。たとえば、<body> 内に同一の CSS ファイルまたはスクリプトファイルへの異なる参照が 4 つある場合、これらのタグのうち 1 つだけが <head> 内に置かれます。
    • <body> 内のすべての <tail> 要素は、<body> タグの末尾に移動されます。
    • ページ上に comet コンポーネントが出現する場合、Lift は必要なすべての JavaScript コードを <body> タグの末尾に挿入します。
  • Lift は、これらの処理の結果得られるドキュメントをタグ付けし、これをバイト列に変換し、ヘッダーとクッキーがあればそれを収集し、これらすべてを LiftResponse へと変換します。
  • Lift 2.2-M1 以降では、CSS セレクタのサブセットをスニペットの中で私用できます。次に例を示します。
    "#name" #> userName // id が name の要素を変数 userName で置き換えます。
    "#chat_lines *" #> listOfChats // chat_lines の内容を listOfChats の各要素で置き換えます。
    ".pretty *" #> <b>Unicorn</b> // CSS クラス pretty の各要素の内容を <b>Unicorn</b> で置き換えます。
    "dog=cat [href]" #> "http://dogscape.com" // dog 属性を持つすべての要素の href 属性に cat を設定します。
    "#name" #> userName & "#age" #> userAge // name に userName を設定し、age に userAge を設定します。

    このしくみを利用すると、次のように非常にシンプルにスニペットを記述できます。
    class Time {
      def render = "#time" #> Helpers.formattedTimeNow
    }

    上のスニペットは、次のようにして呼び出すことができます。
    <span
    class="lift:Time" id="time">No time set</span>