import play.api.libs.iteratee._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent._ import scala.concurrent.duration._ import play.api.libs.concurrent.Promise object iteratees { // Implicit conversion to add 'await' to a Future implicit class WFuture[A](val inner: Future[A]) extends AnyVal { def await(implicit timeout: Duration) = Await.result(inner, timeout) } // Default timeout for futures. implicit val timeout = 10 second //> timeout : scala.concurrent.duration.FiniteDuration = 10 seconds // Simple enumerator with pre-known values val enumerator = Enumerator(1, 2, 3, 4, 5) //> enumerator : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iterate //| e.Enumerator$$anon$21@571a75a2 // Iteratee that just prints all input val printIteratee = Iteratee.foreach[Int] { i => print("[" + i + "]") } //> printIteratee : play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.libs.i //| teratee.Cont$$anon$3@6f25844f // Apply anumerator to iteratee (enumerator |>> printIteratee).await //> [1][2][3][4][5]res0: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.li //| bs.iteratee.Iteratee$$anon$1@148238f4 // Create an iteratee that just takes the first five elements, by transforming the regular // printIteratee with an enumeratee that only takes five elements. def printFiveIteratee = Enumeratee.take(5) &>> printIteratee //> printFiveIteratee: => play.api.libs.iteratee.Iteratee[Int,Unit] // Test whether our printFiveIteratee actually only consumes the // first five elements of an enumerator with more chunks (Enumerator(1, 2, 3, 4, 5, 6, 7, 8) |>> printFiveIteratee).await //> [1][2][3][4][5]res1: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l //| ibs.iteratee.Iteratee$$anon$1@716c9867 // Now we have the printFiveIteratee, we can make endless enumerators and still show // their first chunks, without blowing up. val ones = Enumerator.repeat(1) //> ones : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iteratee.Enu //| merator$$anon$15@5982bcde // Print the first five chunks from an infinite enumerator (ones |>> printFiveIteratee).await //> [1][1][1][1][1]res2: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l //| ibs.iteratee.Done$$anon$2@4e513d61 // So far, our enumerators had no internal state. // Here we create one with the 'unfold' method that has // a state, and uses the state to compute an element and a new state val counting = Enumerator.unfold(0)(counter => Some(counter + 1, counter)) //> counting : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iteratee //| .Enumerator$$anon$12@2bb5340c // Test it (counting |>> printFiveIteratee).await //> [0][1][2][3][4]res3: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l //| ibs.iteratee.Done$$anon$2@71060478 // Create an enumerator with state, that computes the element // and new state asynchronously. The 'Promise' is a play.api.libs.concurrent.Promise, // not the scala.concurrent one, because the Play one has a nice 'timeout' method // to create a future that will be completed after some time. val slowCounting = Enumerator.unfoldM(0) { counter => Promise.timeout(Some(counter + 1, counter), 1 second) } //> slowCounting : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iter //| atee.Enumerator$$anon$12@60491c4c // Apply our slow enumerator on the printing iteratee // Note that this prints one element every second. // Commented out by default, because it's slow // (slowCounting |>> printFiveIteratee).await // So far, our iteratees were stateless. Here we create // an iteratee that maintains state, using the 'fold' method on the // Iteratee object. Notice the similarity with the 'fold' method // on classes from the collections api. val summingIteratee = Iteratee.fold(0)((state, elem: Int) => state + elem) //> summingIteratee : play.api.libs.iteratee.Iteratee[Int,Int] = play.api.libs //| .iteratee.Cont$$anon$3@7632efa7 // With an iteratee that has state, it's actually interesting // to look at the iteratee future that the enumerator produces // after being applied to the original iteratee. // The enumerator returns the iteratee as it is after pushing // in all the chunks. So this iteratee will have an internal state // of '15' val iterateeFuture = (enumerator |>> summingIteratee) //> iterateeFuture : scala.concurrent.Future[play.api.libs.iteratee.Iteratee[I //| nt,Int]] = scala.concurrent.impl.Promise$DefaultPromise@a13f991 // With the 'run' method on an Iteratee, we feed it an EOF // and then extract the value. Await.result(iterateeFuture.flatMap(_.run), 1 second) //> res4: Int = 15 // An enumeratee that only keeps even numbers val evenEnumeratee = Enumeratee.filter[Int](i => i % 2 == 0) //> evenEnumeratee : play.api.libs.iteratee.Enumeratee[Int,Int] = play.api.lib //| s.iteratee.Enumeratee$$anon$18@6f878144 // This transforms (&>>) the iteratee with the enumeratee (counting |>> evenEnumeratee &>> printFiveIteratee).await //> [0][2][4][6][8]res5: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l //| ibs.iteratee.Done$$anon$2@15f48262 // Same as the above, but with parentheses to make the operator // precedence explicit (counting |>> (evenEnumeratee &>> printFiveIteratee)).await //> [0][2][4][6][8]res6: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l //| ibs.iteratee.Done$$anon$2@679bfb30 // This "throughs" the enumerator through the enumerate. Give the // same result as the previous two, but this time the enumeratee is // composed with the enumerator, and not with the iteratee. // So whether you are dealing with an Enumerator or an Iteratee, // you can always compose with an Enumeratee. (counting &> evenEnumeratee |>> printFiveIteratee).await //> [0][2][4][6][8]res7: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l //| ibs.iteratee.Iteratee$$anon$1@7d95d4fe // Same as the above ((counting &> evenEnumeratee) |>> printFiveIteratee).await //> [0][2][4][6][8]res8: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l //| ibs.iteratee.Iteratee$$anon$1@77d2b01b }