I'm new to scala and the playframework. Can somebody please translate the following snippet below into plain english? For context its found here: http://www.playframework.org/documentation/2.0.4/ScalaSecurity
/**
* This method shows how you could wrap the withAuth method to also fetch your user
* You will need to implement UserDAO.findOneByUsername
*/
def withUser(f: User => Request[AnyContent] => Result) = withAuth { username => implicit request =>
UserDAO.findOneByUsername(username).map { user =>
f(user)(request)
}.getOrElse(onUnauthorized(request))
}
Part 1: First let's address the curried syntax:
withUser
is a method that takes a curried function f
of type User => Request[AnyContent] => Result
. It takes a User
object and returns another function that takes a Request
and returns a Result
. Breaking it down, if f
is that function then:
val g = f(user) // g is a function
val result = g(request) // returns a result
// same as:
val result = f(user)(request)
Practically speaking f
is just like a function that takes two parameters but instead of calling f(a, b)
you call f(a)(b)
.
withAuth
is also a method that takes a curried function. It has almost the same type as withUser
.
Part 2: Now how do you use those methods:
As explained here, play makes you defined your application logic by telling it how to transform Request
objects into Result
objects.
withAuth
is a helper function that takes care of the authentication for you and conveniently retrieves the username. So you use it like this:
def index = withAuth { username => implicit request =>
Ok(html.index(username))
}
It returns a function that takes a Request
and returns a Result
, which is what play needs. But what it takes is a curried function (that takes a username) and return a function (that takes a request). The request parameter is marked as implicit so it can be passed implicitly to any function/method call that needs an implicit request parameter. For the purpose of this explanation, just ignore the implicit
keyword.
Part 3: Translation of withUser
Well, its signature is similar to withAuth
and the goal is for it to be used in the same way except the first parameter will be a User
instead of a String
. So it has to take a User => Request => Result
. The request trait takes a type parameter which indicates the type of its content. Here it is AnyContent
. So the correct type for the argument of withUser
is User => Request[AnyContent] => Result
. That means you will be able to use it like this:
withUser { user => implicit request =>
// do something with user and request to return a result
}
If you look at the definition of withUser
, all it does is to call withAuth
:
def withUser(f: User => Request[AnyContent] => Result) = withAuth {
// ...
}
So it will return the same type as withAuth
which means it will return a function that turns a Request
into a Result
(see Part 2 above). Which means we will be able to use it like this:
def index = withUser { user => implicit request =>
Ok(html.index(user))
}
What is passed as an argument of withAuth
is a curried function. I introduced intermediate val
so that you can follow the types:
username => // first param is the username as a String
implicit request => // second param is the request
// here we have to return a Result...
// we lookup the user - we may not have one:
val userOption: Option[User] = UserDAO.findOneByUsername(username)
// f is the code block that will be provided inside withUser { f }
// Part 1 explains the f(user)(request) syntax
// We call f to get a result, in the context of the Option monad
val resultOption: Option[Result] = userOption.map(user => f(user)(request))
// if we have a result, return it; otherwise return an error.
resultOption.getOrElse(onUnauthorized(request))