Search code examples
scalaplayframeworkenumerator

How to add state to a Play Enumerator?


To feed Play response I want to pass Enumerator to result's feed method. I need to pass a state from produce/consume iteration step to the next step (or to keep state). Here http://engineering.klout.com/2013/01/iteratees-in-big-data-at-klout/ I have found an example but am not sure it is thread-safe:

def pagingEnumerator(url:String):Enumerator[JsValue]={
        var maybeNextUrl = Some(url) //Next url to fetch
        Enumerator.fromCallback[JsValue] ( retriever = {

        val maybeResponsePromise =
            maybeNextUrl map { nextUrl=>
                WS.url(nextUrl).get.map { reponse =>
                    val json = response.json
                    maybeNextUrl = (json \ "next_url").asOpt[String]
                    val code = response.status //Potential error handling here
                    json
                }
            }

        maybeResponsePromise match {
            case Some(responsePromise) => responsePromise map Some.apply
            case None                  => PlayPromise pure None
        }
    })
}

What is you way to add a state to Play Enumerator? Is this example thread-safe?

(in the above example old Play API related to Promise/Future is used; let's neglect this fact as far as it doesn't influence the issue itself)


Solution

  • You should use Enumerator.unfoldM:

    Enumerator.unfoldM(Some(firstURL)) { maybeNextUrl =>
      maybeNextUrl map { nextUrl =>
        WS.url(nextUrl).get.map { response =>
          val json = response.json
          val newUrl = (json \ "next_url").asOpt[String]
          val code = response.status //Potential error handling here
          Some((newUrl, json))
        }
      } getOrElse (Future.successful(None))
    }