Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cache.getOrElse on Futures in Playframework

What is the correct way to cache the result of future in playframework. E.g.:

val userGravatar: Future[JsValue] = RemoteGravatarService.get(user.id, user.email)

object RemoveGravatarService {
  def get(userId: String, email: String): Future[JsValue] = {
    Cache.getOrElse("gravatar-for-$userId", 1.hour) {
      WS.url("gravatar.com/email=$email").get().asJson
    }
  }
}

We don't want to ask (this fictional) "Gravatar" every time, because it doesn't change that often. But we need som userGravatar info quite often locally.

Here we cache the future itself, but actually we only want to cache the result of the future. Is there a convenient/correct way to do this in play?

like image 412
kornfridge Avatar asked Jan 05 '16 13:01

kornfridge


1 Answers

There isn't a method in Play's API that will handle Futures. You can wrap Play's cache API to handle the orElse case where it returns a Future. Generically:

object FutureCache {

    def getOrElse[A](key: String, expiration: Int)(orElse: => Future[A])
      (implicit app: Application, ct: ClassTag[A], ec: ExecutionContext): Future[A] = {
        Cache.getAs[A](key).map(Future.successful).getOrElse {
            val value = orElse
            value onSuccess { case result => Cache.set(key, result, expiration) }
            value
        }
    }

}

Usage:

FutureCache.getOrElse[JsValue]("gravatar-for-$userId", 3600) {
  WS.url("gravatar.com/email=$email").get().map(_.json)
}

You could also create an overload for getOrElse that uses Duration.

like image 190
Michael Zajac Avatar answered Nov 14 '22 04:11

Michael Zajac