我正在尝试了解将隐式对象放入Scala应用程序范围内的最佳实践。
我有一个Playframework 2.2.0(Scala 2.10)Web应用程序,它混合了一个特权用于授权。它检查。 Authenticated对象检查范围内是否存在user_id,尝试从缓存,数据库和Web服务调用中检索用户信息,访问令牌和名为MagicNotebook的数据包对象。如果请求有效,则会将各种对象添加到包装的请求中。
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)
将这些隐含变量纳入范围的最佳方法是什么?
我尝试使用隐式伴侣类,使用以下代码:
object Helper {
implicit class Magical(request: AuthenticatedRequest[AnyContent]) {
def folderMap = request.magic.fMap
def documentMap = request.magic.dMap
}
}
但是,我并没有真正得到隐含的好处:
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")
}
我考虑的一种可能性是通过控制器内的import语句。这里,请求在范围内有一个MagicNotebook
对象,我想将其用作隐式变量。
def testing = Authenticated { implicit request =>
import request.magic._
request.magic.home.folders // implicit map is a parameter to the `folder` method
Ok("testing 123")
}
在这里,我创建了一个伴随特征,该特征混合到Authenticate
特征中,其中包含MagicNotebook
对象的两个映射到控制器的范围内。
trait Magic {
implicit def folderMap[A](implicit request: AuthenticatedRequest[A]) =
request.magic.fMap
implicit def docMap[A](implicit request: AuthenticatedRequest[A]) =
request.magic.dMap
}
我的偏好是伴侣特质解决方案,但我想知道是否有更好的方式我忽略了。我最后重写了使用隐式变量的方法,使用MagicNotebook
的两个映射而不是整个对象作为隐式参数。
但同样,我想知道是否有更好的方法。
答案 0 :(得分:3)
我知道定义implicits的方法之一是使用Package Objects。
package implicitIdiomatic {
implicit def nullableLongToOption(l:java.lang.Long) = Option(l)
}
}
package implicitIdiomatic
class ImplicitIdiomaticTest{
val l:Long = 1
longOpt(l)
def longOpt(l:Option[Long]) = l match {case Some(l1) => println(l1); case None => println("No long")}
}
有点无用的例子,但希望你明白这个想法。现在,当longOpt
获得l
时,它会使用隐式转换为Option[Long]
。
只要您使用的是包对象定义的相同包,就不需要import语句。
答案 1 :(得分:2)
对于这类事情,我非常偏爱包装对象。有关说明,请参阅What's New in Scala 2.8: Package Objects。包对象有效地允许您将隐式类放入包中,否则您无法执行此操作。
然而,这种方法的主要障碍是你不能跨多个源文件拆分对象的定义,因为需要在包包中定义隐式类,它们是还需要全部在同一个源文件中。如果您希望导入许多隐式类,则会导致源文件变大且难以处理。但是,这本身就表明你有一个“包god object”,应该拆分。