scalaでxmlのタグを置換したりする時に使うBasicTransformerにバグがあるようで、何度も同じタグに
対する置換処理が走ってしまうようです。既にバグの報告がされていて、パッチも投稿されていました。

http://lampsvn.epfl.ch/trac/scala/ticket/3689

投稿されているパッチを使ってみたところ、特定のパターンでやはり同じタグの置換処理が走る場合が
あって、さらに修正を施す必要がありました。
結果、できたものが下記のコードです。うまくオーバーライドとかでできればよかったのですが、だめだったので
BasicTransformerをコピってtransformメソッドを書き換えました。

class BugFixedBasicTransformer {
    /**
   *  @param n  ...
   *  @param ns ...
   *  @return   ...
   */
  protected def unchanged(n: Node, ns: Seq[Node]) =
    ns.length == 1 && (ns.head == n)

  /** Call transform(Node) for each node in ns, append results
   *  to NodeBuffer.
   */
  def transform(it: Iterator[Node], nb: NodeBuffer): Seq[Node] =
    it.foldLeft(nb)(_ ++= transform(_)).toSeq

  /** Call transform(Node) to each node in ns, yield ns if nothing changes,
   *  otherwise a new sequence of concatenated results.
   */
//  def transform(ns: Seq[Node]): Seq[Node] = {
//    val (xs1, xs2) = ns span (n => unchanged(n, transform(n)))
//
//    if (xs2.isEmpty) ns
//    else xs1 ++ transform(xs2.head) ++ transform(xs2.tail)
//  }

  def transform(n: Node): Seq[Node] = {
    if (n.doTransform) n match {
      case Group(xs)  => Group(transform(xs)) // un-group the hack Group tag
      case _          =>
        val ch = n.child
        val nch = transform(ch)

        if (ch eq nch) n
        else           Elem(n.prefix, n.label, n.attributes, n.scope, nch: _*)
    }
    else n
  }

  def apply(n: Node): Node = {
    val seq = transform(n)
    if (seq.length > 1)
      throw new UnsupportedOperationException("transform must return single node for root");
    else seq.head
  }

  def transform(ns: Seq[Node]): Seq[Node] = {
    val xs = ns.toStream map transform
    val (xs1, xs2) = xs zip ns span { case (x, n) => unchanged(n, x) }

    if (xs2.isEmpty) ns
    else {
      (xs1 map (_._2)) ++ transform(xs2.head._1) ++ transform(ns drop (xs1.length + 1))
    }
  }
}

class BugFixedRuleTransformer(val rules: BugFixedRewriteRule*) extends BugFixedBasicTransformer {
  override def transform(n: Node): Seq[Node] =
    rules.foldLeft(super.transform(n)) { (res, rule) => rule transform res }
}

abstract class BugFixedRewriteRule extends BugFixedBasicTransformer {
  /** a name for this rewrite rule */
  val name = this.toString()
  override def transform(ns: Seq[Node]): Seq[Node] = super.transform(ns)
  override def transform(n: Node): Seq[Node] = n
}

Scalaにはクロージャがあるので当然ローンパターンを使用できるが、戻り値の型をパラメータ化しておくと、より便利になる。
例えば下記のように書くと、access()に渡したクロージャの結果をaccess()の結果として取得できるようになる。


def access[T](f:(java.sql.Connection)=>T):T = {
	val conn = openConnection()
	try {
		f(conn)
	} finally {
		conn.close()
	}
}

scalaで特定のXMLタグの置換を行うには、標準ライブラリのRewriteRuleとRuleTransformerを利用できる。
RewriteRuleに置換ルールを記述し、RuleTransformerでRewriteRuleに基づいて置換処理を行う。

下記のXMLのreplacementタグを属性nameの値に応じて置換をしたいとする。

val node = <template><replacement name="key"/></template>

まず、RewriteRuleを継承して、置換ルールを作成する。

import scala.xml._
import scala.xml.transform._

class ReplacementTagRewriteRule(params:Map[String,String]) extends RewriteRule {
	override def transform(node:Node): Seq[Node] = node match {
		case e @ Elem(_,"replacement",_,_,_*) =>
			new Text(params.get(e.attribute("name").get.toString).get)
		case other => other
	}
}

RuleTransformerを使って置換処理を実行する。RuleTransformerは関数オブジェクトなので括弧で適用できる。

new RuleTransformer(new ReplacementTagRewriteRule(Map("key"-> "abc")))(node)

結果は

<template>abc</template>

となる。

squerylのチュートリアルを見たけれどコードが断片になっているのでぱっと見で分かりにかった。
ひとまとめのサンプルコードがあった方が分かりやすい。

環境
-scala2.8.1
-squeryl0.9.4-RC6
-h2database1.3.154

今回の場合、DBをメモリに置いてるのでtransaction毎にテーブルが消えることに注意。
1トランザクションで処理する必要がある。

以下、一通り動作するコード。

import org.squeryl._
import org.squeryl.adapters._
import org.squeryl.Session
import org.squeryl.SessionFactory
import org.squeryl.PrimitiveTypeMode._

class Author(val id: Long,
	     val firstName: String,
	     val lastName: String,
	     val email: Option[String]) {
	def this() = this(0,"","",Some(""))
}

object MySchema extends Schema {
	val authors = table[Author]
}

object App {
	def main(args: Array[String]):Unit = {
		Class.forName("org.h2.Driver")
		SessionFactory.concreteFactory = Some(() =>
					Session.create(
						java.sql.DriverManager.getConnection("jdbc:h2:mem:db1"),
						new H2Adapter))
		transaction {
			MySchema.printDdl
			MySchema.create
			MySchema.authors.insert(new Author(1,"ore","desuyo", Some("example@example.com")))
			println(MySchema.authors.where(a => a.id === 1).single.firstName)
		}
	}
}

スニペットを使うとビューからスニペットとして用意したクラスのメソッドを呼び出して動的に情報を表示することができる。

スニペットはscala.xml.NodeSeqを返すメソッドを持つクラスである。ビューにスニペットを呼び出すタグを記述することでメソッドが返す値をそのまま表示することができる。
スニペットとして使うクラスはLiftRules.addToPackages()によって指定されたパッケージのsnippetsサブパッケージに配置する。

スニペットの例:

import scala.xml._
class Hello {
  def render: NodeSeq = {
    <span>hello</span>
  }
  def world: NodeSeq = {
    <span>world</span>
  }
}

スニペットを呼び出すにはビューに呼び出しのタグを記述する。
タグの書き方は次の3通りある。

  1. <lift:snippet type=”クラス名:メソッド名” />
  2. <lift:クラス名.メソッド名 />
  3. <lift:クラス名 />
    メソッド名が省略されたこの書き方の場合は、renderメソッドが呼び出される。

実際にHelloクラスを呼び出す例は次の通り。

  <lift:snippet type="Hello:render"/>
  <lift:Hello />
  <lift:Hello.world />

参考文献:

http://exploring.liftweb.net/