Skip to content

Commit

Permalink
util: #> and replaceWith use new CanBind type class
Browse files Browse the repository at this point in the history
  • Loading branch information
Naftoli Gugenheim committed Oct 10, 2011
1 parent 2d325f9 commit 8f7db07
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 104 deletions.
6 changes: 4 additions & 2 deletions core/util/src/main/scala/net/liftweb/util/BindHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,8 @@ trait BindHelpers {
* Remove all the <head> tags, just leaving the child tags
*/
def stripHead(in: NodeSeq): NodeSeq = {
("head" #> ((ns: NodeSeq) => ns.asInstanceOf[Elem].child))(in)
"head" #> ((ns: NodeSeq) => ns.asInstanceOf[Elem].child) apply in
/*
import scala.xml.transform._
val rewrite = new RewriteRule {
Expand All @@ -704,14 +705,15 @@ trait BindHelpers {
}
(new RuleTransformer(rewrite)).transform(in)
*/
}

/**
* Replace the element with the id that matches with the replacement
* nodeseq.
*/
def replaceIdNode(in: NodeSeq, id: String, replacement: NodeSeq): NodeSeq = {
(("#"+id) #> replacement)(in)
("#"+id) #> replacement apply in
/*
import scala.xml.transform._
Expand Down
137 changes: 51 additions & 86 deletions core/util/src/main/scala/net/liftweb/util/CssBinding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,59 +25,71 @@ import scala.collection.mutable.ListBuffer


/**
* 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
* 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
*/
final class ToCssBindPromoter(stringSelector: Box[String], css: Box[CssSelector]) {
class CanBind[-T](val apply: Box[String] => Box[CssSelector] => T => CssSel)

object CanBind {
/**
* Inserts a String constant according to the CssSelector rules
*/
def #>(str: String): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] =
List(if (null eq str) NodeSeq.Empty else Text(str))
}

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))
}
)

/**
* Inserts a NodeSeq constant according to the CssSelector rules
*/
def #>(ns: NodeSeq): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(ns)
}
implicit val nodeSeq = new CanBind[NodeSeq](stringSelector => css => ns =>
new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(ns)
}
)

/**
* A function that transforms the content according to the CssSelector rules
*/
def #>(nsFunc: NodeSeq => NodeSeq): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(nsFunc(in))
}
implicit val nodeSeqFunc = new CanBind[NodeSeq=>NodeSeq](stringSelector => css => nsFunc =>
new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(nsFunc(in))
}
)

/**
* Inserts a Bindable constant according to the CssSelector rules.
* Mapper and Record fields implement Bindable.
*/
def #>(bindable: Bindable): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(bindable.asHtml)
}
implicit val bindable = new CanBind[Bindable](stringSelector => css => bindable =>
new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(bindable.asHtml)
}
)

/**
* Inserts a StringPromotable constant according to the CssSelector rules.
* StringPromotable includes Int, Long, Boolean, and Symbol
*/
def #>(strPromo: StringPromotable): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] =
List(Text(strPromo.toString))
}
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))
}
)

/**
* Applies the N constants according to the CssSelector rules.
* This allows for Seq[String], Seq[NodeSeq], Box[String],
* Box[NodeSeq], Option[String], Option[NodeSeq]
*/
def #>(itrConst: IterableConst): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = itrConst.constList(in)
}
implicit def iterableConst[T<%IterableConst] = new CanBind[T](stringSelector => css => itrConst =>
new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = itrConst.constList(in)
}
)

/**
* Apply the function and then apply the results account the the CssSelector
Expand All @@ -87,73 +99,26 @@ final class ToCssBindPromoter(stringSelector: Box[String], css: Box[CssSelector]
* NodeSeq => Box[NodeSeq], NodeSeq => Option[String],
* NodeSeq =>Option[NodeSeq]
*/
def #>(itrFunc: IterableFunc): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = itrFunc(in)
}

/**
* Inserts a String constant according to the CssSelector rules
*/
def replaceWith(str: String): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] =
List(if (null eq str) NodeSeq.Empty else Text(str))
}

/**
* Inserts a NodeSeq constant according to the CssSelector rules
*/
def replaceWith(ns: NodeSeq): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(ns)
}

/**
* A function that transforms the content according to the CssSelector rules
*/
def replaceWith(nsFunc: NodeSeq => NodeSeq): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(nsFunc(in))
}

/**
* Inserts a Bindable constant according to the CssSelector rules.
* Mapper and Record fields implement Bindable.
*/
def replaceWith(bindable: Bindable): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = List(bindable.asHtml)
}

/**
* Inserts a StringPromotable constant according to the CssSelector rules.
* StringPromotable includes Int, Long, Boolean, and Symbol
*/
def replaceWith(strPromo: StringPromotable): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = strPromo.toString match {
case null => NodeSeq.Empty
case str => List(Text(str))
implicit def iterableFunc[T<%IterableFunc] = new CanBind[T](stringSelector => css => itrFunc =>
new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = itrFunc(in)
}
}
)
}

/**
* Applies the N constants according to the CssSelector rules.
* This allows for Seq[String], Seq[NodeSeq], Box[String],
* Box[NodeSeq], Option[String], Option[NodeSeq]
*/
def replaceWith(itrConst: IterableConst): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = itrConst.constList(in)
}

/**
* 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]
*/
def replaceWith(itrFunc: IterableFunc): CssSel = new CssBindImpl(stringSelector, css) {
def calculate(in: NodeSeq): Seq[NodeSeq] = itrFunc(in)
}
/**
* 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)
}


/**
* A trait that has some helpful implicit conversions from
* Iterable[NodeSeq], Seq[String], Box[String], and Option[String]
Expand Down
32 changes: 16 additions & 16 deletions core/util/src/test/scala/net/liftweb/util/BindHelpersSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ object CssBindHelpersSpec extends Specification {
}

"substitute a String by id" in {
("#foo" #> "hello")(<b><span id="foo"/></b>) must ==/ (<b>hello</b>)
("#foo" #> "hello") apply (<b><span id="foo"/></b>) must ==/ (<b>hello</b>)
}


Expand Down Expand Up @@ -430,11 +430,11 @@ object CssBindHelpersSpec extends Specification {
}

"substitute a String by id" in {
("#foo" replaceWith "hello")(<b><span id="foo"/></b>) must ==/ (<b>hello</b>)
("#foo" replaceWith "hello") apply (<b><span id="foo"/></b>) must ==/ (<b>hello</b>)
}

"Select a node" in {
("#foo ^^" #> "hello")(<div><span id="foo"/></div>) must ==/ (<span id="foo"/>)
("#foo ^^" #> "hello") apply (<div><span id="foo"/></div>) must ==/ (<span id="foo"/>)
}

"Another nested select" in {
Expand Down Expand Up @@ -559,19 +559,19 @@ object CssBindHelpersSpec extends Specification {

"option transform on *" in {
val opt: Option[String] = None
val res = ("* *" #> opt.map(ignore => "Dog"))(<top>cat</top>)
val res = ("* *" #> opt.map(ignore => "Dog")) apply (<top>cat</top>)
res.head must_== <top></top>
}

"append attribute to a class with spaces" in {
val stuff = List("a", "b")
val res = ("* [class+]" #> stuff)(<top class="q">cat</top>)
val res = ("* [class+]" #> stuff) apply (<top class="q">cat</top>)
(res \ "@class").text must_== "q a b"
}

"append attribute to an href" in {
val stuff = List("&a=b", "&b=d")
val res = ("* [href+]" #> stuff)(<top href="q?z=r">cat</top>)
val res = ("* [href+]" #> stuff) apply (<top href="q?z=r">cat</top>)
(res \ "@href").text must_== "q?z=r&a=b&b=d"
}

Expand Down Expand Up @@ -614,47 +614,47 @@ object CssBindHelpersSpec extends Specification {

"option transform on *" in {
val opt: Option[Int] = Full(44)
val res = ("* *" #> opt.map(ignore => "Dog"))(<top>cat</top>)
val res = "* *" #> opt.map(ignore => "Dog") apply <top>cat</top>
res must ==/ (<top>Dog</top>)
}


"option transform on *" in {
val opt: Box[String] = Empty
val res = ("* *" #> opt.map(ignore => "Dog"))(<top>cat</top>)
val res = "* *" #> opt.map(ignore => "Dog") apply <top>cat</top>
res.head must_== <top></top>
}

"option transform on *" in {
val opt: Box[Int] = Some(44)
val res = ("* *" #> opt.map(ignore => "Dog"))(<top>cat</top>)
val res = "* *" #> opt.map(ignore => "Dog") apply <top>cat</top>
res must ==/ (<top>Dog</top>)
}

"transform on *" in {
val res = ("* *" #> "Dog")(<top>cat</top>)
val res = "* *" #> "Dog" apply <top>cat</top>
res must ==/ (<top>Dog</top>)
}

"transform child content on *+" in {
val res = ("* *+" #> "moose")(<a>I like </a>)
val res = "* *+" #> "moose" apply <a>I like </a>
res.text must_== "I like moose"
}

"transform child content on -*" in {
val res = ("* -*" #> "moose")(<a> I like</a>)
val res = "* -*" #> "moose" apply <a> I like</a>
res.text must_== "moose I like"
}

"transform on li" in {
val res = ("li *" #> List("Woof", "Bark") & ClearClearable)(
<ul><li>meow</li><li class="clearable">a</li><li class="clearable">a</li></ul>)
val res = "li *" #> List("Woof", "Bark") & ClearClearable apply
<ul><li>meow</li><li class="clearable">a</li><li class="clearable">a</li></ul>
res must ==/ (<ul><li>Woof</li><li>Bark</li></ul>)
}

"substitute multiple Strings by id" in {
(("#foo" replaceWith "hello") &
("#baz" replaceWith "bye"))(<b><div id="baz">Hello</div><span id="foo"/></b>) must ==/ (<b>{Text("bye")}{Text("hello")}</b>)
("#foo" replaceWith "hello") &
("#baz" replaceWith "bye") apply <b><div id="baz">Hello</div><span id="foo"/></b> must ==/ (<b>{Text("bye")}{Text("hello")}</b>)
}

"substitute multiple Strings with a List by id" in {
Expand Down

0 comments on commit 8f7db07

Please sign in to comment.