Permalink
Browse files

util: CanBind/CanBindN

  • Loading branch information...
1 parent 8f7db07 commit 07b59b97d583040bd5a770f2bb9680a63fd5b591 @nafg nafg committed Sep 1, 2011
@@ -1339,7 +1339,7 @@ trait BindHelpers {
new ToCssBindPromoter(Full(str), CssSelectorParser.parse(str))
/**
- * promote a String to a ToCssBindPromotor
+ * promote a CssSelector to a ToCssBindPromotor
*/
implicit def cssSelectorToCssBindPromoter(sel: CssSelector): ToCssBindPromoter =
new ToCssBindPromoter(Empty, Full(sel))
@@ -1479,7 +1479,7 @@ sealed trait CssSel extends Function1[NodeSeq, NodeSeq] {
/**
* Inserts a String constant according to the CssSelector rules
*/
- def sel(selector: String, str: IterableConst): CssSel = this & (selector #> str)
+ //def sel(selector: String, str: IterableConst): CssSel = this & (selector #> str)
/**
* Inserts a String constant according to the CssSelector rules
@@ -2101,7 +2101,7 @@ final class CssJBridge {
/**
* Inserts a String constant according to the CssSelector rules
*/
- def sel(selector: String, str: IterableConst): CssSel = (selector #> str)
+ //def sel(selector: String, str: IterableConst): CssSel = (selector #> str)
/**
* Inserts a String constant according to the CssSelector rules
@@ -24,98 +24,112 @@ import java.util.{List => JavaList}
import scala.collection.mutable.ListBuffer
+
/**
- * A type class that creates CssSel instances for a value of some type
- * @param apply a function stringSelector => css => x => cssSel. stringSelector and css are the arguments for CssBindImpl's constructor
+ * A type class that creates a NodeSeq=>Seq[NodeSeq] for a value of some type.
+ * The apply method delegates to the function passed to the constructor.
+ * @param f a function T=>NodeSeq=>Seq[NodeSeq] that returns a NodeSeq=>NodeSeq for a T
*/
-class CanBind[-T](val apply: Box[String] => Box[CssSelector] => T => CssSel)
+class CanBindN[-T](f: T => NodeSeq => Seq[NodeSeq]) extends (T => NodeSeq => Seq[NodeSeq]) {
+ /**
+ * Given a value of type T, return a function that can be used in Lift binding.
+ * The function takes a NodeSeq (the content from the template) and returns
+ * a Seq[NodeSeq]. The return value is collection-valued because some types repeatedly
+ * transform the same template content (e.g., "th *" #> List("Column Header 1", "Column Header 2"))
+ */
+ def apply(v: T) = f(v)
+}
-object CanBind {
+/**
+ * Defines the CanBindN implicits that are available by default
+ */
+object CanBindN {
/**
- * Inserts a String constant according to the CssSelector rules
+ * Bind a single bindable value.
+ * Given a type that has an implicit CanBind, delegate to that CanBind,
+ * wrapping the result in a List is its single element
*/
- implicit val string = new CanBind[String](stringSelector => css => str =>
- new CssBindImpl(stringSelector, css) {
- def calculate(in: NodeSeq): Seq[NodeSeq] =
- List(if (null eq str) NodeSeq.Empty else Text(str))
- }
- )
+ implicit def single[T](implicit canBind: CanBind[T]): CanBindN[T] =
+ new CanBindN[T](v => ns => List(canBind(v)(ns)))
/**
- * Inserts a NodeSeq constant according to the CssSelector rules
+ * Bind zero or more bindable values.
+ * Given a type that has an implicit CanBind, and a context viewable as an
+ * Iterable of that type, apply all the CanBinds
*/
- implicit val nodeSeq = new CanBind[NodeSeq](stringSelector => css => ns =>
- new CssBindImpl(stringSelector, css) {
- def calculate(in: NodeSeq): Seq[NodeSeq] = List(ns)
- }
- )
+ implicit def iterable[T, I[_]](implicit canBind: CanBind[T], toIter: I[T]=>Iterable[T]): CanBindN[I[T]] =
+ new CanBindN[I[T]](v => ns => toIter(v).toSeq map (_ apply ns))
/**
- * A function that transforms the content according to the CssSelector rules
+ * Bind a function that, given a NodeSeq, returns zero or more bindable values.
+ * Given a type that has an implicit CanBindN, and a context viewable as an
+ * Iterable of that type, apply all the CanBindNs.
*/
- implicit val nodeSeqFunc = new CanBind[NodeSeq=>NodeSeq](stringSelector => css => nsFunc =>
- new CssBindImpl(stringSelector, css) {
- def calculate(in: NodeSeq): Seq[NodeSeq] = List(nsFunc(in))
- }
- )
+ implicit def iterableFunc[T, I[_]](implicit canBindN: CanBindN[T], toIter: I[T]=>Iterable[T]): CanBindN[NodeSeq=>I[T]] =
+ new CanBindN[NodeSeq=>I[T]](v => ns => toIter(v(ns)).toSeq flatMap (_ apply ns))
+}
+
+class CanBind[-T](f: T => NodeSeq => NodeSeq) extends (T => NodeSeq => NodeSeq) {
+ def apply(v: T) = f(v)
+}
+
+//TODO obviate StringPromotable
+//TODO obviate Bindable
+object CanBind {
/**
- * Inserts a Bindable constant according to the CssSelector rules.
- * Mapper and Record fields implement Bindable.
+ * Bind a string by replacing content with a Text node (or NodeSeq.empty in the case of null)
*/
- implicit val bindable = new CanBind[Bindable](stringSelector => css => bindable =>
- new CssBindImpl(stringSelector, css) {
- def calculate(in: NodeSeq): Seq[NodeSeq] = List(bindable.asHtml)
- }
- )
+ implicit val string = new CanBind[String](str => _ => (if (null eq str) NodeSeq.Empty else Text(str)))
/**
- * Inserts a StringPromotable constant according to the CssSelector rules.
- * StringPromotable includes Int, Long, Boolean, and Symbol
+ * Bind a NodeSeq by replacing content with it
*/
- implicit def stringPromotable[T<%StringPromotable] = new CanBind[T](stringSelector => css => strPromo =>
- new CssBindImpl(stringSelector, css) {
- def calculate(in: NodeSeq): Seq[NodeSeq] =
- List(Text(strPromo.toString))
- }
- )
+ implicit val nodeSeq: CanBind[NodeSeq] = new CanBind[NodeSeq](ns => _ => ns)
/**
- * Applies the N constants according to the CssSelector rules.
- * This allows for Seq[String], Seq[NodeSeq], Box[String],
- * Box[NodeSeq], Option[String], Option[NodeSeq]
+ * Bind a Seq[Node] by replacing content with it, via NodeSeq.fromSeq
*/
- implicit def iterableConst[T<%IterableConst] = new CanBind[T](stringSelector => css => itrConst =>
- new CssBindImpl(stringSelector, css) {
- def calculate(in: NodeSeq): Seq[NodeSeq] = itrConst.constList(in)
- }
- )
+ implicit val seqNode: CanBind[Seq[Node]] = new CanBind[Seq[Node]](ns => _ => NodeSeq fromSeq ns)
/**
- * Apply the function and then apply the results account the the CssSelector
- * rules.
- * This allows for NodeSeq => Seq[String], NodeSeq =>Seq[NodeSeq],
- * NodeSeq => Box[String],
- * NodeSeq => Box[NodeSeq], NodeSeq => Option[String],
- * NodeSeq =>Option[NodeSeq]
+ * Bind a function NodeSeq=>U where U has an implicit CanBind, by applying it
+ * to content and replacing it with the result
*/
- implicit def iterableFunc[T<%IterableFunc] = new CanBind[T](stringSelector => css => itrFunc =>
- new CssBindImpl(stringSelector, css) {
- def calculate(in: NodeSeq): Seq[NodeSeq] = itrFunc(in)
- }
- )
+ implicit def func[U](implicit canBind: CanBind[U]): CanBind[NodeSeq => U] =
+ new CanBind[NodeSeq => U](f => in => f(in) apply in)
+
+ /**
+ * Bind an object that extends Bindable, by calling its asHtml method
+ * and replacing content with its result.
+ * Mapper and Record fields implement Bindable.
+ */
+ implicit val bindable: CanBind[Bindable] = new CanBind[Bindable](bindable => _ => bindable.asHtml)
+
+ /**
+ * Bind something that has a conversion to StringPromotable, by
+ * calling the StringPromotable's toString method and replacing content with it wrapped in a Text node.
+ * StringPromotable includes Int, Long, Boolean, and Symbol
+ */
+ implicit def stringPromotable[T](implicit view: T=>StringPromotable) = new CanBind[T](strPromo => _ => Text(view(strPromo).toString))
+
+
}
+
/**
* An intermediate class used to promote a String or a CssSelector to
* something that can be associated with a value to apply to the selector
* @param stringSelector the unparsed css selector string
* @param css the parsed CssSelector object
*/
final class ToCssBindPromoter(stringSelector: Box[String], css: Box[CssSelector]) {
- def #>[T](v: T)(implicit canBind: CanBind[T]): CssSel = canBind.apply(stringSelector)(css)(v)
- def replaceWith[T: CanBind](v: T): CssSel = #>[T](v)
+ def #>[T](v: T)(implicit canBindN: CanBindN[T]): CssSel =
+ new CssBindImpl(stringSelector, css) {
+ def calculate(in: NodeSeq): Seq[NodeSeq] = canBindN.apply(v)(in)
+ }
+ def replaceWith[T: CanBindN](v: T): CssSel = #>[T](v)
}
@@ -196,6 +210,7 @@ object IterableConst {
/**
* Converts anything that can be converted into an Box[NodeSeq]
+ * Bind a value that has a CanBindN implicit available
*/
implicit def boxNodeSeq(it: Box[NodeSeq]): IterableConst =
NodeSeqIterableConst(it.toList)
@@ -210,6 +225,7 @@ object IterableConst {
* Converts anything that can be converted into an Iterable[NodeSeq]
* into an IterableConst. This includes Seq[NodeSeq], Option[NodeSeq],
* and Box[NodeSeq]
+ * Bind a value that has a CanBindN implicit available
*/
implicit def itNodeSeq(it: JavaList[NodeSeq]): IterableConst =
new NodeSeqIterableConst(it)
@@ -304,6 +320,10 @@ object IterableFunc {
trait StringPromotable
object StringPromotable {
+ implicit def hasStringConversion[T <% String](in: T): StringPromotable =
+ new StringPromotable {
+ override val toString: String = in
+ }
implicit def jsCmdToStrPromo(in: ToJsCmd): StringPromotable =
new StringPromotable {
override val toString = in.toJsCmd
@@ -893,7 +893,18 @@ object CssBindHelpersSpec extends Specification {
(answer \ "a" \ "@href").text must_== "Hi"
(answer \\ "li").length must_== 0
}
-
+
+ "bind something implicity convertable to a String" in {
+ class X
+ implicit def X2str(x: X): String = "This is X"
+ implicitly[X => String] apply new X must_== "This is X"
+ (implicitly[X => StringPromotable] apply new X toString) must_== "This is X"
+ val s: String = new X
+ s must_== "This is X"
+ val sp: StringPromotable = new X
+ sp.toString must_== "This is X"
+ ("foo" #> new X apply <foo/> text) must_== "This is X"
+ }
}
}
@@ -908,6 +919,12 @@ object CheckTheImplicitConversionsForToCssBindPromoter {
import BindHelpers._
+ implicitly[CanBind[NodeSeq]]
+ implicitly[CanBindN[Iterable[NodeSeq]]]
+ implicitly[CanBindN[NodeSeq => Iterable[NodeSeq]]]
+ implicitly[CanBindN[NodeSeq => Seq[Node]]]
+ implicitly[CanBind[NodeSeq => String]]
+
"foo" #> "baz"
bog #> "Hello"
@@ -113,7 +113,7 @@ trait StatefulSnippet extends DispatchSnippet {
if (formElem.isDefined) {
import util.Helpers._
- ("form *" #> ((kids: NodeSeq) => toMerge ++ kids))(res)
+ "form *" #> ((kids: NodeSeq) => toMerge ++ kids) apply res
} else if (isForm) {
toMerge ++ res
} else {
@@ -615,15 +615,15 @@ trait HtmlFixer {
import scala.collection.mutable.ListBuffer
val lb = new ListBuffer[JsCmd]
- val revised = ("script" #> ((ns: NodeSeq) => {
+ val revised = "script" #> ((ns: NodeSeq) => {
ns match {
case FindScript(e) => {
lb += JE.JsRaw(ns.text).cmd
NodeSeq.Empty
}
case x => x
}
- }))(xhtml)
+ }) apply xhtml
S.htmlProperties.htmlWriter(Group(revised), w)

0 comments on commit 07b59b9

Please sign in to comment.