Background
I am trying to understand best practices for bringing implicit objects into scope within a Scala application.
I have a Playframework 2.2.0 (Scala 2.10) web app that mixes in a trait for Authorization. It checks. The Authenticated object checks that there is a user_id in scope, attempts to retrieve the user info, access token, and a data package object called a MagicNotebook from cache, database, and web service call. If the request is valid, then various objects are added to the wrapped request.
object Authenticated extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A],
block: (AuthenticatedRequest[A] => Future[SimpleResult])) = {
request.session.get(userName).map { implicit userId =>
Cache.getAs[DbUser](userKey).map { user =>
Cache.getAs[String](accessTokenKey).map { accessToken =>
Cache.getAs[MagicNotebook](magicNotebookKey(userId)).map { notebook =>
block(AuthenticatedRequest(user, accessToken, notebook, request) )
}.getOrElse(startOver)
}.getOrElse {
requestNewAccessToken(user.token).flatMap { response =>
persistAccessToken(response).map { accessToken =>
Cache.getAs[MagicNotebook](magicNotebookKey(userId)).map { notebook =>
block(AuthenticatedRequest(user, accessToken, notebook, request))
}.getOrElse(startOver)
}.getOrElse(startOver)
}
}
}.getOrElse(startOver) // user not found in Cache
}.getOrElse(startOver) // userName not found in session
}
}
}
case class AuthenticatedRequest[A](user: DbUser,
accessToken: String,
magic: MagicNotebook,
request: Request[A])
extends WrappedRequest[A](request)
Question
What is the best way to bring these implicit variables into scope?
Through an implicit class?
I tried to use an implicit companion class, with the following code:
object Helper {
implicit class Magical(request: AuthenticatedRequest[AnyContent]) {
def folderMap = request.magic.fMap
def documentMap = request.magic.dMap
}
}
However, I don't really get the benefit of an implicit this way:
def testing = Authenticated { implicit request =>
import services.Helper._
request.magic.home.folders // doesn't compile
request.magic.home.folders(Magical(request).ffMap) // compiles, but not implicitly
Ok("testing 123")
}
Through an import statement?
One possibility I considered was through an import statement within the controller. Here, the request has a MagicNotebook
object in scope that I would like to use as an implicit variable.
def testing = Authenticated { implicit request =>
import request.magic._
request.magic.home.folders // implicit map is a parameter to the `folder` method
Ok("testing 123")
}
Through a companion trait?
Here, I create a companion trait that is mixed into the Authenticate
trait that includes the two maps of the MagicNotebook
object into scope of the controller.
trait Magic {
implicit def folderMap[A](implicit request: AuthenticatedRequest[A]) =
request.magic.fMap
implicit def docMap[A](implicit request: AuthenticatedRequest[A]) =
request.magic.dMap
}
My preference is the companion trait solution, but I was wondering if there might be a better way that I overlooked. I ended up re-writing methods that use the implicit variable, to use the MagicNotebook
's two maps instead of whole object as implicit parameters.
But again, I was wondering if there might be a better way.
implicit class Magical(request: AuthenticatedRequest[AnyContent])
would fix your compiler error. – Ashalynd