目次

Version 4, last updated by marius.danciu at Nov 28 02:17 UTC

スニペットの使い方のヒント

コンテンツをバインドするためのスニペットの基本的な使い方については、「テンプレートとバインディング」を参照してください。

この記事はもともと Timothy Perrett が書いたもので、Timothy の許可を得てブログから転載したものでせす。Timothy のブログは blog.getintheloop.eu にあります。

Lift の初心者にとってわかりづらいものの 1 つにスニペットがあります。コンセプトだけでなく、スニペットの実装スタイルを選ぶときにたくさんの選択肢があることも原因でしょう。ユーザーは、スニペットリフレクションやディスパッチスニペットのほかに、スニペットディスパッチングを継承するステートフルスニペットの中から目的のスニペットを選択する必要があります。

リフレクションスニペット

リフレクションによるクラスベースのスニペットのロードが、Lift ではデフォルトのスキームです。これは、開発をすばやくスタートできることが理由です。自分のスニペットのパッケージに、次のようなコードがあるとします。

import _root_.scala.xml.{NodeSeq,Text}

class HelloWorld {
  def speak = <span>Hello World</span>
}

これは、次のようにして任意の Lift テンプレートで呼び出すことができます。

<lift:hello_world.speak />

この場合、ほかに面倒なことをしなくても、スニペットの呼び出しはきちんと動作します。機能的には大してみるところはありませんが、この例のニーズは十分に満たしています。この方法は、開発やトラフィックの少ないデプロイメントで使う場合にはうってつけですが、サイトの規模が大きくなると、こうしたスニペットの作成方法では目的を果たせなくなることがあります。なぜでしょうか。まず第一に、タグ名とメソッドの間のバインディングが固定化されています。また、ディスパッチスニペットを使う方がよいという考えもあります。そのほうが、開発チームのほかのメンバーにもわかりやすいからです。さらに、わずかなパフォーマンスの低下も気になるところです。目的のスニペットの実装とメソッドを見つけるため、リクエストのたびに最大 1 回のリフレクション呼び出しが発生ためからです。

リフレクションスニペットの新しいインスタンスは、1 つの HTTP リクエストで最大 1 回作成されます。したがって、1 つページ上で同じスニペットを複数回使う場合、インスタンスは 1 つだけ作成されます。インスタンスは RequestVar に格納されるので、こうしたスニペットのライフタイムは、HTTP リクエストと一致し、ページから行われるすべての Ajax 呼び出しにまたがることになります。基本的に、こうしたスニペットのライフタイムは、レンダリングされたページのライフタイムと等しくなります。

リフレクションスニペットの欠点を補う DispatchSnippet

DispatchSnippet を使うと、タグ名とメソッドの間のバインディングをコントロールできます。すでに示した例で変更する必要があるのは、DispatchSnippet トレイトを継承し、dispatch メソッドを実装することだけです。

import _root_.scala.xml.{NodeSeq,Text}
import _root_.net.liftweb.http.DispatchSnippet

class HelloWorld extends DispatchSnippet {
  def dispatch = { 
    case "talk" => speak
  }
  def speak = <span>Hello World</span>
}

dispatch メソッドにはロジックを含めることができ、その中で、どのメソッドを呼び出すかを指定することができます。このロジックでは S (現在の状態) などを使うことができます。ただし、リクエストごとにスニペットの新しいインスタンスが作成される点は同じです。

シングルトン DispatchSnippets

もちろん、Scala では object キーワードを使ってシングルトンオブジェクトを作成することができます。これは、多数のインスタンスを作成するやり方に比べ、スニペットでは非常に有益な方法です。インスタンス 1 つで、はるかに使い勝手のよいスニペットシステムになるからです。では実際に最初のスニペットを、シングルトンのディスパッチスニペットに変更してみましょう。

import _root_.scala.xml.{NodeSeq,Text}
import _root_.net.liftweb.http.DispatchSnippet

object HelloWorld extends DispatchSnippet {
  def dispatch = { 
    case "talk" => speak
  }
  def speak = <span>Hello World</span>
}

もちろん、この方法なりの欠点もわずかながらあります。リフレクションスニペットでは面倒なことは一切必要ありませんでしたが、DispatchSnippet では、XHTML の中で何かを呼び出すときに、どのスニペットのインスタンスを参照しているのかを Lift に教えてやる必要があります。これは、Boot.scala ファイルの中で SnippetDispatchPF 呼び出しによって可能になります。

LiftRules.snippetDispatch.append(
    Map("hello_world" -> HelloWorld)
  )

上のように記述すれば、XHTML では次のようにして呼び出すことができます。

<lift:hello_world.talk />

出力はリフレクションスタイルのスニペットの場合とまったく同じになりますが、アプリケーションでは 1 つのスニペットインスタンスしか使わない点が異なります。

StatefulSnippet

ステートフルスニペットのライフタイムはコントロールできるので、ステートフルスニペットは複数のリクエストにまたがって存続することができます。ほかのフレームワークでは、しばしば「対話スコープ (conversation scope)」という用語が使われます。ステートフルスニペットは、たとえば複数ページから構成されるウィザードを作成する場合などに便利です。

StatefulSnippet は DispatchSnippet を継承しているので、dispatch メソッドを実装する必要があります。また、リンクを生成するときは、S のメソッドではなく、StatefulSnippet の link メソッドを使う必要があります。

スニペットのインスタンスが必要なくなったときは、スニペットのメソッドから unregisterThisSnippet() を呼び出します。

詳細については、書籍[訳注:『Exploring Lift』のことと思われます]を参照してください。

AttributeSnippet

場合によっては、既存のノードに対して動的に構築された属性を使うと便利なことがあります。次の例を見てください。

   <div class="badWheather">
      ...
   </div>

div の内容は実際には何でもかまわないのですが、場合によっては div に CSS の class ‘goodWheather’ を割り当てたいことがあります。こうした場合に役立つのが、AttributeSnippet です。

   <div lift:MyClass.meteo="badWheather">
      ...
   </div>

Scala コードは次のように記述します。

class MyClass {

   def meteo(in: MetaData): MetaData = {
      // ここでメタデータ (通常は UnpreffixedAttribute) を返す.
   }

}

上のコードで、NodeSeq => NodeSeq ではなく、MetaData => MetaData になっていることに注意してください。もちろんこれは、操作の対象が XML ノードではなく、ノードの属性だからです。