Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

refactorings and fixes in Iteratees

  • Loading branch information...
commit f979006a7e2c1c08ca56ee0bae67b5463ee099c1 1 parent 57e9280
Sadek Drobi authored
2  documentation/manual
@@ -1 +1 @@
-Subproject commit 7ad1a718207f7a5350e18511b2865d8355985db7
+Subproject commit e43122438f8cd020ae7cc5fad88a354ef9ccabc8
View
127 framework/src/play/src/main/scala/play/api/libs/iteratee/Iteratee.scala
@@ -59,7 +59,26 @@ object Iteratee {
(Cont[E, A](i => step(state)(i)))
}
- def fold2[E, A](state: A)(f: (A, E) => Promise[(A, Boolean)]): Iteratee[E, A] = {
+ /**
+ * Create an [[play.api.libs.iteratee.Iteratee]] which folds the content of the Input using a given function and an initial state
+ *
+ * It also gives the opportunity to return a [[play.api.libs.concurrent.Promise]] so that promises are combined in a complete reactive flow of logic.
+ *
+ *
+ * @param state initial state
+ * @param f a function folding the previous state and an input to a new promise of state
+ */
+ def foldM[E, A](state: A)(f: (A, E) => Promise[A]): Iteratee[E, A] = {
+ def step(s: A)(i: Input[E]): Iteratee[E, A] = i match {
+
+ case Input.EOF => Done(s, Input.EOF)
+ case Input.Empty => Cont[E, A](i => step(s)(i))
+ case Input.El(e) => { val newS = f(s, e); flatten(newS.map(s1 => Cont[E, A](i => step(s1)(i)))) }
+ }
+ (Cont[E, A](i => step(state)(i)))
+ }
+
+ def fold2[E, A](state: A)(f: (A, E) => Promise[(A,Boolean)]): Iteratee[E, A] = {
def step(s: A)(i: Input[E]): Iteratee[E, A] = i match {
case Input.EOF => Done(s, Input.EOF)
@@ -640,7 +659,11 @@ object Enumeratee {
case in @ (Input.El(_) | Input.Empty) =>
Iteratee.flatten(f.feed(in)).pureFlatFold(
- (a, _) => new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(folder)(k)) } &> k(Input.El(a)),
+ (a, left) => new CheckDone[From, To] { def continue[A](k: K[To, A]) =
+ (left match {
+ case Input.El(_) => step(folder)(k)(left)
+ case _ => Cont(step(folder)(k))
+ })} &> k(Input.El(a)),
kF => Cont(step(Cont(kF))(k)),
(msg, e) => Error(msg, in))
@@ -959,42 +982,90 @@ object Enumerator {
import scalax.io.JavaConverters._
- def unfoldM[S, E](s: S)(f: S => Promise[Option[(S, E)]]): Enumerator[E] = new Enumerator[E] {
- def apply[A](it: Iteratee[E, A]): Promise[Iteratee[E, A]] = {
+ def unfoldM[S,E](s:S)(f: S => Promise[Option[(S,E)]] ): Enumerator[E] = checkContinue1(s)(new TreatCont1[E,S]{
- var iterateeP = Promise[Iteratee[E, A]]()
+ def apply[A](loop: (Iteratee[E,A],S) => Promise[Iteratee[E,A]], s:S, k: Input[E] => Iteratee[E,A]):Promise[Iteratee[E,A]] = f(s).flatMap {
+ case Some((newS,e)) => loop(k(Input.El(e)),newS)
+ case None => Promise.pure(Cont(k))
+ }
+ })
- def step(it: Iteratee[E, A], state: S) {
+ def unfold[S,E](s:S)(f: S => Option[(S,E)] ): Enumerator[E] = checkContinue1(s)(new TreatCont1[E,S]{
- val next = it.fold(
- (a, e) => { iterateeP.redeem(it); Promise.pure(None) },
+ def apply[A](loop: (Iteratee[E,A],S) => Promise[Iteratee[E,A]], s:S, k: Input[E] => Iteratee[E,A]):Promise[Iteratee[E,A]] = f(s) match {
+ case Some((s,e)) => loop(k(Input.El(e)),s)
+ case None => Promise.pure(Cont(k))
+ }
+ })
+
+ def repeat[E](e: => E): Enumerator[E] = checkContinue0( new TreatCont0[E]{
+
+ def apply[A](loop: Iteratee[E,A] => Promise[Iteratee[E,A]], k: Input[E] => Iteratee[E,A]) = loop(k(Input.El(e)))
+
+ })
+
+ def repeatM[E](e: => Promise[E]): Enumerator[E] = checkContinue0( new TreatCont0[E]{
+
+ def apply[A](loop: Iteratee[E,A] => Promise[Iteratee[E,A]], k: Input[E] => Iteratee[E,A]) = e.flatMap(ee => loop(k(Input.El(ee))))
+
+ })
+
+ def generateM[E](e: => Promise[Option[E]]): Enumerator[E] = checkContinue0( new TreatCont0[E] {
+
+ def apply[A](loop: Iteratee[E,A] => Promise[Iteratee[E,A]], k: Input[E] => Iteratee[E,A]) = e.flatMap {
+ case Some(e) => loop(k(Input.El(e)))
+ case None => Promise.pure(Cont(k))
+ }
+ })
+
+ trait TreatCont0[E]{
+
+ def apply[A](loop: Iteratee[E,A] => Promise[Iteratee[E,A]], k: Input[E] => Iteratee[E,A]):Promise[Iteratee[E,A]]
+
+ }
+
+ def checkContinue0[E](inner:TreatCont0[E]) = new Enumerator[E] {
+
+ def apply[A](it: Iteratee[E, A]): Promise[Iteratee[E, A]] = {
+
+ def step(it: Iteratee[E, A]): Promise[Iteratee[E,A]] = {
+
+ it.fold(
+ (a, e) => Promise.pure(Done(a,e)),
k => {
- f(state).map {
- case None => {
- val remainingIteratee = k(Input.EOF)
- iterateeP.redeem(remainingIteratee)
- None
- }
- case Some((s, read)) => {
- val nextIteratee = k(Input.El(read))
- Some((nextIteratee, s))
- }
- }
+ inner[A](step,k)
},
- (_, _) => { iterateeP.redeem(it); Promise.pure(None) }
+ (msg, e) => Promise.pure(Error(msg,e))
)
+ }
+ step(it)
+ }
- next.extend1 {
- case Redeemed(Some((i, s))) => step(i, s)
- case Redeemed(None) => // do nothing, already redeemed
- case Thrown(e) => iterateeP.throwing(e)
- }
+ }
- }
+ trait TreatCont1[E,S]{
- step(it, s)
- iterateeP
+ def apply[A](loop: (Iteratee[E,A],S) => Promise[Iteratee[E,A]], s:S, k: Input[E] => Iteratee[E,A]):Promise[Iteratee[E,A]]
+
+ }
+
+ def checkContinue1[E,S](s:S)(inner:TreatCont1[E,S]) = new Enumerator[E] {
+
+ def apply[A](it: Iteratee[E, A]): Promise[Iteratee[E, A]] = {
+
+ def step(it: Iteratee[E, A], state:S): Promise[Iteratee[E,A]] = {
+
+ it.fold(
+ (a, e) => Promise.pure(Done(a,e)),
+ k => {
+ inner[A](step,state,k)
+ },
+ (msg, e) => Promise.pure(Error(msg,e))
+ )
+ }
+ step(it,s)
}
+
}
def fromCallback1[E](retriever: Boolean => Promise[Option[E]],
View
24 framework/src/play/src/main/scala/play/api/libs/iteratee/TraversableIteratee.scala
@@ -58,6 +58,30 @@ object Traversable {
}
}
+ import Enumeratee.CheckDone
+
+ def splitOnceAt[M,E](p: E => Boolean)(implicit traversableLike: M => scala.collection.TraversableLike[E, M]):Enumeratee[M,M] = new CheckDone[M, M] {
+
+ def step[A](k: K[M, A]): K[M, Iteratee[M, A]] = {
+
+ case in @ Input.El(e) =>
+ e.span(p) match {
+ case (prefix,suffix) if suffix.isEmpty => new CheckDone[M, M] { def continue[A](k: K[M, A]) = Cont(step(k)) } &> k(Input.El(prefix))
+ case (prefix,suffix) => Done(if(prefix.isEmpty) Cont(k) else k(Input.El(prefix)), Input.El(suffix.drop(1)))
+
+ }
+
+ case in @ Input.Empty =>
+ new CheckDone[M, M] { def continue[A](k: K[M, A]) = Cont(step(k)) } &> k(in)
+
+ case Input.EOF => Done(Cont(k), Input.EOF)
+
+ }
+
+ def continue[A](k: K[M, A]) = Cont(step(k))
+
+ }
+
def drop[M](count: Int)(implicit p: M => scala.collection.TraversableLike[_, M]): Enumeratee[M, M] = new Enumeratee[M, M] {
def applyOn[A](inner: Iteratee[M, A]): Iteratee[M, Iteratee[M, A]] = {
View
10 framework/src/play/src/test/scala/play/iteratee/EnumerateesSpec.scala
@@ -128,6 +128,16 @@ object EnumerateesSpec extends Specification {
}
+ "Enumeratee.grouped" should {
+ "not throw away left inputs by the folder iteratee" in {
+
+ val upToSpace = Traversable.splitOnceAt[String,Char](c => c != '\n') &>> Iteratee.consume()
+
+ val result = (Enumerator("dasdasdas ", "dadadasda\nshouldb\neinnext") &> Enumeratee.grouped(upToSpace) ><> Enumeratee.map(_+"|")) |>> Iteratee.consume[String]()
+ result.flatMap(_.run).value.get must equalTo("dasdasdas dadadasda|shouldb|")
+ }
+ }
+
"Enumeratee.scanLeft" should {
"transform elements using a sate" in {
View
41 framework/src/play/src/test/scala/play/iteratee/EnumeratorsSpec.scala
@@ -93,5 +93,46 @@ object EnumeratorsSpec extends Specification {
(e |>> it).flatMap(_.run).value.get must equalTo ((10 until 40).sum)
}
}
+
+"Enumerator.generateM" should {
+ "generate a stream of values until the expression is None" in {
+
+ val a = 0 to 10 toList
+ val it = a.iterator
+
+ val enumerator = Enumerator.generateM( play.api.libs.concurrent.Promise.pure(if(it.hasNext) Some(it.next) else None))
+
+ (enumerator |>> Iteratee.fold[Int,String]("")(_ + _)).flatMap(_.run).value.get must equalTo("012345678910")
+
+ }
+
+}
+
+"Enumerator.generateM" should {
+ "Can be composed with another enumerator (doesn't send EOF)" in {
+
+ val a = 0 to 10 toList
+ val it = a.iterator
+
+ val enumerator = Enumerator.generateM( play.api.libs.concurrent.Promise.pure(if(it.hasNext) Some(it.next) else None)) >>> Enumerator(12)
+
+ (enumerator |>> Iteratee.fold[Int,String]("")(_ + _)).flatMap(_.run).value.get must equalTo("01234567891012")
+
+ }
+
+}
+
+"Enumerator.unfoldM" should {
+ "Can be composed with another enumerator (doesn't send EOF)" in {
+
+ val enumerator = Enumerator.unfoldM[Int,Int](0)( s => play.api.libs.concurrent.Promise.pure(if(s > 10) None else Some((s+1,s+1)))) >>> Enumerator(12)
+
+ (enumerator |>> Iteratee.fold[Int,String]("")(_ + _)).flatMap(_.run).value.get must equalTo("123456789101112")
+
+ }
+
+}
+
+
}
Please sign in to comment.
Something went wrong with that request. Please try again.