Skip to content
This repository
Browse code

refactorings and fixes in Iteratees

  • Loading branch information...
commit f979006a7e2c1c08ca56ee0bae67b5463ee099c1 1 parent 57e9280
authored May 08, 2012
2  documentation/manual
... ...
@@ -1 +1 @@
1  
-Subproject commit 7ad1a718207f7a5350e18511b2865d8355985db7
  1
+Subproject commit e43122438f8cd020ae7cc5fad88a354ef9ccabc8
127  framework/src/play/src/main/scala/play/api/libs/iteratee/Iteratee.scala
@@ -59,7 +59,26 @@ object Iteratee {
59 59
     (Cont[E, A](i => step(state)(i)))
60 60
   }
61 61
 
62  
-  def fold2[E, A](state: A)(f: (A, E) => Promise[(A, Boolean)]): Iteratee[E, A] = {
  62
+  /**
  63
+   * Create an [[play.api.libs.iteratee.Iteratee]] which folds the content of the Input using a given function and an initial state
  64
+   *
  65
+   * 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.
  66
+   *
  67
+   *
  68
+   * @param state initial state
  69
+   * @param f a function folding the previous state and an input to a new promise of state
  70
+   */
  71
+  def foldM[E, A](state: A)(f: (A, E) => Promise[A]): Iteratee[E, A] = {
  72
+    def step(s: A)(i: Input[E]): Iteratee[E, A] = i match {
  73
+
  74
+      case Input.EOF => Done(s, Input.EOF)
  75
+      case Input.Empty => Cont[E, A](i => step(s)(i))
  76
+      case Input.El(e) => { val newS = f(s, e); flatten(newS.map(s1 => Cont[E, A](i => step(s1)(i)))) }
  77
+    }
  78
+    (Cont[E, A](i => step(state)(i)))
  79
+  }
  80
+
  81
+  def fold2[E, A](state: A)(f: (A, E) => Promise[(A,Boolean)]): Iteratee[E, A] = {
63 82
     def step(s: A)(i: Input[E]): Iteratee[E, A] = i match {
64 83
 
65 84
       case Input.EOF => Done(s, Input.EOF)
@@ -640,7 +659,11 @@ object Enumeratee {
640 659
         case in @ (Input.El(_) | Input.Empty) =>
641 660
 
642 661
           Iteratee.flatten(f.feed(in)).pureFlatFold(
643  
-            (a, _) => new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(folder)(k)) } &> k(Input.El(a)),
  662
+            (a, left) => new CheckDone[From, To] { def continue[A](k: K[To, A]) = 
  663
+              (left match {
  664
+                case Input.El(_) => step(folder)(k)(left)
  665
+                case _ => Cont(step(folder)(k))
  666
+              })} &> k(Input.El(a)),
644 667
             kF => Cont(step(Cont(kF))(k)),
645 668
             (msg, e) => Error(msg, in))
646 669
 
@@ -959,42 +982,90 @@ object Enumerator {
959 982
 
960 983
   import scalax.io.JavaConverters._
961 984
 
962  
-  def unfoldM[S, E](s: S)(f: S => Promise[Option[(S, E)]]): Enumerator[E] = new Enumerator[E] {
963  
-    def apply[A](it: Iteratee[E, A]): Promise[Iteratee[E, A]] = {
  985
+  def unfoldM[S,E](s:S)(f: S => Promise[Option[(S,E)]] ): Enumerator[E] = checkContinue1(s)(new TreatCont1[E,S]{
964 986
 
965  
-      var iterateeP = Promise[Iteratee[E, A]]()
  987
+    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 {
  988
+      case Some((newS,e)) => loop(k(Input.El(e)),newS)
  989
+      case None => Promise.pure(Cont(k))
  990
+    }
  991
+  })
966 992
 
967  
-      def step(it: Iteratee[E, A], state: S) {
  993
+  def unfold[S,E](s:S)(f: S => Option[(S,E)] ): Enumerator[E] = checkContinue1(s)(new TreatCont1[E,S]{
968 994
 
969  
-        val next = it.fold(
970  
-          (a, e) => { iterateeP.redeem(it); Promise.pure(None) },
  995
+    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 {
  996
+      case Some((s,e)) => loop(k(Input.El(e)),s)
  997
+      case None => Promise.pure(Cont(k))
  998
+    }
  999
+  })
  1000
+
  1001
+  def repeat[E](e: => E): Enumerator[E] = checkContinue0( new TreatCont0[E]{
  1002
+
  1003
+    def apply[A](loop: Iteratee[E,A] => Promise[Iteratee[E,A]], k: Input[E] => Iteratee[E,A]) = loop(k(Input.El(e)))
  1004
+
  1005
+  })
  1006
+
  1007
+  def repeatM[E](e: => Promise[E]): Enumerator[E] = checkContinue0( new TreatCont0[E]{
  1008
+
  1009
+    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))))
  1010
+
  1011
+  })
  1012
+
  1013
+  def generateM[E](e: => Promise[Option[E]]): Enumerator[E] = checkContinue0( new TreatCont0[E] {
  1014
+
  1015
+    def apply[A](loop: Iteratee[E,A] => Promise[Iteratee[E,A]], k: Input[E] => Iteratee[E,A]) = e.flatMap {
  1016
+      case Some(e) => loop(k(Input.El(e)))
  1017
+      case None => Promise.pure(Cont(k))
  1018
+    }
  1019
+  })
  1020
+
  1021
+  trait TreatCont0[E]{
  1022
+
  1023
+    def apply[A](loop: Iteratee[E,A] => Promise[Iteratee[E,A]], k: Input[E] => Iteratee[E,A]):Promise[Iteratee[E,A]]
  1024
+
  1025
+  }
  1026
+
  1027
+  def checkContinue0[E](inner:TreatCont0[E]) = new Enumerator[E] {
  1028
+
  1029
+    def apply[A](it: Iteratee[E, A]): Promise[Iteratee[E, A]] = {
  1030
+
  1031
+      def step(it: Iteratee[E, A]): Promise[Iteratee[E,A]] = {
  1032
+
  1033
+        it.fold(
  1034
+          (a, e) => Promise.pure(Done(a,e)),
971 1035
           k => {
972  
-            f(state).map {
973  
-              case None => {
974  
-                val remainingIteratee = k(Input.EOF)
975  
-                iterateeP.redeem(remainingIteratee)
976  
-                None
977  
-              }
978  
-              case Some((s, read)) => {
979  
-                val nextIteratee = k(Input.El(read))
980  
-                Some((nextIteratee, s))
981  
-              }
982  
-            }
  1036
+            inner[A](step,k)
983 1037
           },
984  
-          (_, _) => { iterateeP.redeem(it); Promise.pure(None) }
  1038
+          (msg, e) => Promise.pure(Error(msg,e))
985 1039
         )
  1040
+      }
  1041
+      step(it)
  1042
+    }
986 1043
 
987  
-        next.extend1 {
988  
-          case Redeemed(Some((i, s))) => step(i, s)
989  
-          case Redeemed(None) => // do nothing, already redeemed
990  
-          case Thrown(e) => iterateeP.throwing(e)
991  
-        }
  1044
+  }
992 1045
 
993  
-      }
  1046
+  trait TreatCont1[E,S]{
994 1047
 
995  
-      step(it, s)
996  
-      iterateeP
  1048
+    def apply[A](loop: (Iteratee[E,A],S) => Promise[Iteratee[E,A]], s:S, k: Input[E] => Iteratee[E,A]):Promise[Iteratee[E,A]]
  1049
+
  1050
+  }
  1051
+
  1052
+  def checkContinue1[E,S](s:S)(inner:TreatCont1[E,S]) = new Enumerator[E] {
  1053
+
  1054
+    def apply[A](it: Iteratee[E, A]): Promise[Iteratee[E, A]] = {
  1055
+
  1056
+      def step(it: Iteratee[E, A], state:S): Promise[Iteratee[E,A]] = {
  1057
+
  1058
+        it.fold(
  1059
+          (a, e) => Promise.pure(Done(a,e)),
  1060
+          k => {
  1061
+            inner[A](step,state,k)
  1062
+          },
  1063
+          (msg, e) => Promise.pure(Error(msg,e))
  1064
+        )
  1065
+      }
  1066
+      step(it,s)
997 1067
     }
  1068
+
998 1069
   }
999 1070
 
1000 1071
   def fromCallback1[E](retriever: Boolean => Promise[Option[E]],
24  framework/src/play/src/main/scala/play/api/libs/iteratee/TraversableIteratee.scala
@@ -58,6 +58,30 @@ object Traversable {
58 58
     }
59 59
   }
60 60
 
  61
+  import Enumeratee.CheckDone
  62
+
  63
+  def splitOnceAt[M,E](p: E => Boolean)(implicit traversableLike: M => scala.collection.TraversableLike[E, M]):Enumeratee[M,M] =  new CheckDone[M, M] {
  64
+
  65
+      def step[A](k: K[M, A]): K[M, Iteratee[M, A]] = {
  66
+
  67
+        case in @ Input.El(e) =>
  68
+          e.span(p) match {
  69
+            case (prefix,suffix) if suffix.isEmpty => new CheckDone[M, M] { def continue[A](k: K[M, A]) = Cont(step(k)) } &> k(Input.El(prefix))
  70
+            case (prefix,suffix) => Done(if(prefix.isEmpty) Cont(k) else  k(Input.El(prefix)), Input.El(suffix.drop(1)))
  71
+
  72
+          }
  73
+
  74
+        case in @ Input.Empty =>
  75
+          new CheckDone[M, M] { def continue[A](k: K[M, A]) = Cont(step(k)) } &> k(in)
  76
+
  77
+        case Input.EOF => Done(Cont(k), Input.EOF)
  78
+
  79
+      }
  80
+
  81
+      def continue[A](k: K[M, A]) = Cont(step(k))
  82
+
  83
+  }
  84
+
61 85
   def drop[M](count: Int)(implicit p: M => scala.collection.TraversableLike[_, M]): Enumeratee[M, M] = new Enumeratee[M, M] {
62 86
 
63 87
     def applyOn[A](inner: Iteratee[M, A]): Iteratee[M, Iteratee[M, A]] = {
10  framework/src/play/src/test/scala/play/iteratee/EnumerateesSpec.scala
@@ -128,6 +128,16 @@ object EnumerateesSpec extends Specification {
128 128
 
129 129
   }
130 130
 
  131
+  "Enumeratee.grouped" should {
  132
+    "not throw away left inputs by the folder iteratee" in {
  133
+
  134
+      val upToSpace = Traversable.splitOnceAt[String,Char](c => c != '\n')  &>> Iteratee.consume()
  135
+
  136
+      val result = (Enumerator("dasdasdas ", "dadadasda\nshouldb\neinnext") &> Enumeratee.grouped(upToSpace) ><> Enumeratee.map(_+"|")) |>> Iteratee.consume[String]()
  137
+      result.flatMap(_.run).value.get must equalTo("dasdasdas dadadasda|shouldb|")
  138
+    }
  139
+  }
  140
+
131 141
   "Enumeratee.scanLeft" should {
132 142
 
133 143
     "transform elements using a sate" in {
41  framework/src/play/src/test/scala/play/iteratee/EnumeratorsSpec.scala
@@ -93,5 +93,46 @@ object EnumeratorsSpec extends Specification {
93 93
     (e |>> it).flatMap(_.run).value.get must equalTo ((10 until 40).sum)
94 94
   }
95 95
 }
  96
+
  97
+"Enumerator.generateM" should {
  98
+  "generate a stream of values until the expression is None" in {
  99
+
  100
+    val a = 0 to 10 toList
  101
+    val it = a.iterator
  102
+
  103
+    val enumerator = Enumerator.generateM( play.api.libs.concurrent.Promise.pure(if(it.hasNext) Some(it.next) else None))
  104
+
  105
+    (enumerator |>> Iteratee.fold[Int,String]("")(_ + _)).flatMap(_.run).value.get must equalTo("012345678910")
  106
+
  107
+  }
  108
+
  109
+}
  110
+
  111
+"Enumerator.generateM" should {
  112
+  "Can be composed with another enumerator (doesn't send EOF)" in {
  113
+
  114
+    val a = 0 to 10 toList
  115
+    val it = a.iterator
  116
+
  117
+    val enumerator = Enumerator.generateM( play.api.libs.concurrent.Promise.pure(if(it.hasNext) Some(it.next) else None)) >>> Enumerator(12)
  118
+
  119
+    (enumerator |>> Iteratee.fold[Int,String]("")(_ + _)).flatMap(_.run).value.get must equalTo("01234567891012")
  120
+
  121
+  }
  122
+
  123
+}
  124
+
  125
+"Enumerator.unfoldM" should {
  126
+  "Can be composed with another enumerator (doesn't send EOF)" in {
  127
+
  128
+    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)
  129
+
  130
+    (enumerator |>> Iteratee.fold[Int,String]("")(_ + _)).flatMap(_.run).value.get must equalTo("123456789101112")
  131
+
  132
+  }
  133
+
  134
+}
  135
+
  136
+
96 137
 }
97 138
 

0 notes on commit f979006

Please sign in to comment.
Something went wrong with that request. Please try again.