Skip to content

Commit

Permalink
Reworked the repeater moulder
Browse files Browse the repository at this point in the history
  • Loading branch information
jawher committed Jan 15, 2012
1 parent e09130e commit c2dfae6
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 40 deletions.
9 changes: 8 additions & 1 deletion src/main/scala/moulder/Moulds.scala
Expand Up @@ -22,7 +22,14 @@ object Moulds {

def replace(content: Value[List[Node]]) = Replacer(content)

def repeat[A](items: Value[List[A]]) = Repeater(items)
def repeat[A](items: Value[List[A]], m: (A, Int) => List[Moulder]) = Repeater(items, m)


def repeat[A](items: Value[List[A]]) = Repeater(items, (item: A, index: Int) => Nil)

def repeatN(n: Value[Int]) = Repeater(Value.of(n().map(0 until _).map(_.toList)), (item: Int, index: Int) => Nil)

def repeat[A](items: Value[List[A]], moulds: List[Moulder]) = Repeater(items, (item: A, index: Int) => moulds)

def attr(attr: Value[String], value: Value[String]) = AttrModifier(attr, value)

Expand Down
4 changes: 4 additions & 0 deletions src/main/scala/moulder/Value.scala
Expand Up @@ -10,4 +10,8 @@ object Value {
def apply[A](a: A) = new Value[A]() {
def apply() = Some(a)
}

def of[A](a: Option[A]) = new Value[A]() {
def apply() = a
}
}
20 changes: 18 additions & 2 deletions src/main/scala/moulder/moulds/Repeater.scala
@@ -1,16 +1,32 @@
package moulder.moulds

import helpers.MouldersApplier
import org.jsoup.nodes._
import moulder._

case class Repeater[A](private val items: Value[List[A]]) extends Moulder {
case class Repeater[A](private val items: Value[List[A]], private val mould: (A, Int) => List[Moulder]) extends Moulder {

override def process(element: Element): List[Node] = {
items() match {
case Some(data) => data.map((i: A) => copy(element))
case Some(data) => handleData(element, data)
case None => Nil
}
}

// private def handleData(element: Element, data: List[A]): List[Node] = {
// data.zip(0 until data.length).flatMap((item: A, index: Int) => {
// val elementCopy = List(copy(element))
// MouldersApplier.applyMoulders(mould(item, index), elementCopy)
// })
// }

private def handleData(element: Element, data: List[A]): List[Node] = {
for ((item, index) <- data.zip(0 until data.length);
elementCopy = List(copy(element));
produced <- MouldersApplier.applyMoulders(mould(item, index), elementCopy)
) yield produced
}

private def copy(e: Element) = {
import scala.collection.JavaConversions._

Expand Down
17 changes: 2 additions & 15 deletions src/main/scala/moulder/moulds/SubMoulder.scala
@@ -1,5 +1,6 @@
package moulder.moulds

import helpers.MouldersApplier
import org.jsoup.nodes._
import moulder._
import scala.collection.JavaConversions._
Expand All @@ -17,20 +18,6 @@ case class SubMoulder() extends Moulder {
this
}

private def applyMoulder(m: Moulder, nodes: List[Node]): List[Node] = {
nodes.flatMap(_ match {
case ed: Element => m.process(ed)
case nd: Node => List(nd)
})
}

private def applyMoulders(ms: List[Moulder], nodes: List[Node]): List[Node] = {
if (ms.isEmpty)
nodes
else
applyMoulders(ms.tail, applyMoulder(ms.head, nodes))
}

private def replace(e: Element, nodes: List[Node]) = {
nodes.foreach(n => e.before(n.outerHtml))
e.remove
Expand All @@ -39,7 +26,7 @@ case class SubMoulder() extends Moulder {
override def process(element: Element): List[Node] = {
cfg.foreach(sm => {
val elements = element.select(sm._1)
elements.foreach(e => replace(e, applyMoulders(sm._2, List(e))))
elements.foreach(e => replace(e, MouldersApplier.applyMoulders(sm._2, List(e))))
})
List(element)
}
Expand Down
22 changes: 15 additions & 7 deletions src/main/scala/moulder/moulds/helpers/MouldersApplier.scala
@@ -1,13 +1,21 @@
package moulder.moulds.helpers

/**
* Created by IntelliJ IDEA.
* User: jawher
* Date: 8/1/12
* Time: 6:41 PM
* To change this template use File | Settings | File Templates.
*/
import moulder.Moulder
import org.jsoup.nodes.{Element, Node}

object MouldersApplier {
private def applyMoulder(m: Moulder, nodes: List[Node]): List[Node] = {
nodes.flatMap(_ match {
case ed: Element => m.process(ed)
case nd: Node => List(nd)
})
}

def applyMoulders(ms: List[Moulder], nodes: List[Node]): List[Node] = {
if (ms.isEmpty)
nodes
else
applyMoulders(ms.tail, applyMoulder(ms.head, nodes))
}

}
97 changes: 82 additions & 15 deletions src/test/scala/moulder/MouldersTest.scala
Expand Up @@ -229,33 +229,100 @@ class MouldersSpec extends Specification with Mockito {

"Repeater" should {
XMLUnit.setIgnoreWhitespace(true);
val document = Jsoup.parseBodyFragment("<html><body><outer a='v'>test</outer></body></html>")

val element = document.getElementsByTag("outer").first()

val list = 1 :: 3 :: 4 :: 7 :: Nil
val items = mock[Value[List[Int]]]
items.apply() returns Some(list)
"Given an None value" in {
val document = Jsoup.parseBodyFragment("<html><body><outer a='v'>test</outer></body></html>")

val a = Repeater(items)
val element = document.getElementsByTag("outer").first()

val processed = a.process(element)
val items = mock[Value[List[Int]]]
items.apply() returns None

"call apply on its value" in {
there was one(items).apply()
val a = Repeater(items, (item: Int, index: Int) => Nil)

val processed = a.process(element)

"call apply on its value" in {
there was one(items).apply()
}

"remove its element" in {
XMLUnit.compareXML(new StringReader("<body></body>"), new StringReader(
html(processed))).identical() must beTrue
}
}

"repeat its elements for every item" in {
XMLUnit.compareXML(new StringReader("<body><outer a='v'>test</outer><outer a='v'>test</outer><outer a='v'>test</outer><outer a='v'>test</outer></body>"), new StringReader(
html(processed))).identical() must beTrue
"Given an empty list of values" in {
val document = Jsoup.parseBodyFragment("<html><body><outer a='v'>test</outer></body></html>")

val element = document.getElementsByTag("outer").first()

val items = mock[Value[List[Int]]]
items.apply() returns Some(Nil)

val a = Repeater(items, (item: Int, index: Int) => Nil)

val processed = a.process(element)

"call apply on its value" in {
there was one(items).apply()
}

"remove its element" in {
XMLUnit.compareXML(new StringReader("<body></body>"), new StringReader(
html(processed))).identical() must beTrue
}
}
}

"AttrModifier" should {
XMLUnit.setIgnoreWhitespace(true);
"Given an non empty list of items and no moulders" in {
val document = Jsoup.parseBodyFragment("<html><body><outer a='v'>test</outer></body></html>")

val element = document.getElementsByTag("outer").first()

val list = 1 :: 3 :: 4 :: 7 :: Nil
val items = mock[Value[List[Int]]]
items.apply() returns Some(list)

val a = Repeater(items, (item: Int, index: Int) => Nil)

val processed = a.process(element)

"call apply on its value" in {
there was one(items).apply()
}

"repeat its elements for every item" in {
XMLUnit.compareXML(new StringReader("<body><outer a='v'>test</outer><outer a='v'>test</outer><outer a='v'>test</outer><outer a='v'>test</outer></body>"), new StringReader(
html(processed))).identical() must beTrue
}
}

"Given an non empty list of items and a moulder" in {
val document = Jsoup.parseBodyFragment("<html><body><outer a='v'>test</outer></body></html>")

val element = document.getElementsByTag("outer").first()

val list = "a" :: "b" :: Nil

val mould = (item: String, index: Int) => new Moulder {
def process(element: Element): List[Node] = List(parseNode("<b>" +
item + "</b>"), parseNode("index=" + index))
} :: Nil

val a = Repeater(Value(list), mould)

val processed = a.process(element)

"repeat its elements for every item" in {
XMLUnit.compareXML(new StringReader("<body><b>a</b>index=0<b>b</b>index=1</body>"), new StringReader(
html(processed))).identical() must beTrue
}
}
}

"AttrModifier" should {
XMLUnit.setIgnoreWhitespace(true);

"Given a value that returns something" in {
val document = Jsoup.parseBodyFragment("<html><body><outer a='v'>test</outer></body></html>")
Expand Down

0 comments on commit c2dfae6

Please sign in to comment.