Here are the codes:
def find(loginInfo: LoginInfo): Future[Option[UserProfile]] = {
val res = DB.withSession { implicit session =>
//if loginInfo.providerID == "waylens"
userProfileTable.filter(u => u.userName === loginInfo.providerKey).list
}
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
def findByEmail(providerID: String, email: String): Future[Option[UserProfile]] = {
val res = DB.withSession { implicit session =>
//if loginInfo.providerID == "waylens"
userProfileTable.filter(u => u.email === email && u.status === 1).list
}
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
def findByProfileID(profileID: Long): Future[Option[UserProfile]] = {
val res = DB.withSession { implicit session =>
userProfileTable.filter(u => u.id === profileID).list
}
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
These codes appeared many times, which is really annoying
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
Does anyone have ideas about refactoring this in Slick?
You can pretty easily use high order functions here, first let's get your queries out of the methods:
lazy val a = userProfileTable.filter(u => u.userName === loginInfo.providerKey)
lazy val b = userProfileTable.filter(u => u.email === email && u.status === 1)
lazy val c = userProfileTable.filter(u => u.id === profileID)
Note that I'm making this lazy because you don't have a session in scope yet.
Now let's create a function to execute this queries (you can make this even more generic using a type parameter):
def executeQuery(q: ⇒ : Query[UserProfiles, UserProfiles#TableElementType]): Int = {
db.withSession { implicit session ⇒
q.list
}
}
Then we need to check the length:
def listToUserProfile(list: List[UserProfile]): Future[Option[UserProfile]] = {
if (list.length <= 1)
Future(list.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
Now you could do something like:
listToUserProfile(executeQuery(a))
There may be some error because your code is not runnable and I couldn't compile it.
First create a generic method to execute the queries which either returns a future or an error using Either
def getElement[E, U, C[_]](query: Query[E, U, C]) = {
db.withSession { implicit session =>
query.list.headOption match {
case Some(ele) => Right(Future(ele))
case None => Left(new Exception("db error"))
}
}
}
Now in the code, you can simply do
def find(loginInfo: LoginInfo): Future[Option[UserProfile]] =
getElement(userProfileTable.filter(u => u.userName === loginInfo.providerKey)).right.map(userProfileRecordToUserProfile)
Similarly for others:
def findByEmail(loginInfo: LoginInfo): Future[Option[UserProfile]] =
getElement( userProfileTable.filter(u => u.email === email && u.status === 1))).right.map(userProfileRecordToUserProfile)
On a side note, you are not wrapping the future over the DB call but after you get the result. DB call will be major source of blocking and not the processing of the DB result. You should look into wrapping the DB call inside the future
Related
I want to insert a new row with different status value if that row exists and return the new instance as Some to the caller. If it does not exist, nothing happens and return None. What I have is
def revoke(name: String, issueFor: String): Future[MyLicense] = {
val retrieveLicense = slickLicenses.filter(l => l.name === name && l.issueFor === issueFor).
sortBy(_.modifiedOn.desc).result.headOption
val insertLicense = for {
licenseOption <- retrieveLicense
} yield {
licenseOption match {
case Some(l) => Some(slickLicenses += l.copy(status = RevokedLicense))
case None => None
}
}
db.run(insertLicense).map {r =>
r.map { dbLicense => MyLicense(dbLicense.name, dbLicense.status)
}
}
How do I fix this?
Edit 1
I changed the code to
override def revoke(name: String, issueFor: String): Future[String] = {
val retrieveLicense = slickLicenses.filter(l => l.name === name && l.issueFor === issueFor).
sortBy(_.modifiedOn.desc).result.headOption
val insertLicenseAction = for {
l <- retrieveLicense
newLicense <- slickLicenses += l.get.copy(status = RevokedLicense)
} yield newLicense
db.run(insertLicenseAction).map(_ => name)
}
Hence, the question changes to how do I get the instance I insert into the database? Thanks
This is what I did finally,
override def revoke(name: String, issueFor: String): Future[String] = {
val retrieveLicense = slickLicenses.filter(l => l.name === name && l.issueFor === issueFor).
sortBy(_.modifiedOn.desc).result.headOption
val insertLicenseAction = for {
l <- retrieveLicense
_ <- l.map(x => slickLicenses += x.copy(status = RevokedLicense,
modifiedOn = new Timestamp(System.currentTimeMillis())))
.getOrElse(DBIO.successful(l))
} yield ()
db.run(insertLicenseAction).map(x => name)}
I try to simplify validation process for serving responses for HTTP requests in Spray (I use Slick for database access). Currently, I check in one query if I should go further to the following query or not (return error). This end up with nested pattern matching. Every validation case can return different error so I can not use any flatMap.
class LocationDao {
val db = DbProvider.db
// Database tables
val devices = Devices.devices
val locations = Locations.locations
val programs = Programs.programs
val accessTokens = AccessTokens.accessTokens
def loginDevice(deviceSerialNumber: String, login: String, password: String): Either[Error, LocationResponse] = {
try {
db withSession { implicit session =>
val deviceRowOption = devices.filter(d => d.serialNumber === deviceSerialNumber).map(d => (d.id, d.currentLocationId.?, d.serialNumber.?)).firstOption
deviceRowOption match {
case Some(deviceRow) => {
val locationRowOption = locations.filter(l => l.id === deviceRow._2.getOrElse(0L) && l.login === login && l.password === password).firstOption
locationRowOption match {
case Some(locationRow) => {
val programRowOption = programs.filter(p => p.id === locationRow.programId).firstOption
programRowOption match {
case Some(programRow) => {
val program = Program(programRow.name, programRow.logo, programRow.moneyLevel, programRow.pointsForLevel,
programRow.description, programRow.rules, programRow.dailyCustomerScansLimit)
val locationData = LocationData(program)
val locationResponse = LocationResponse("access_token", System.currentTimeMillis(), locationData)
Right(locationResponse)
}
case None => Left(ProgramNotExistError)
}
}
case None => Left(IncorrectLoginOrPasswordError)
}
}
case None => Left(DeviceNotExistError)
}
}
} catch {
case ex: SQLException =>
Left(DatabaseError)
}
}
}
What is a good way to simplify this? Maybe there is other approach..
Generally, you can use a for-comprehension to chain together a lot of the monadic structures you have here (including Try, Option, and Either) without nesting. For example:
for {
val1 <- Try("123".toInt)
} yield for {
val2 <- Some(val1).map(_ * 2)
val3 = Some(val2 - 55)
val4 <- val3
} yield val4 * 2
In your style, this might otherwise have looked like:
Try("123".toInt) match {
case Success(val1) => {
val val2 = Some(val1).map(_ * 2)
val2 match {
case Some(val2value) => {
val val3 = Some(val2value - 55)
val3 match {
case Some(val4) => Some(val4)
case None => None
}
}
case None => None
}
case f:Failure => None
}
}
You can define a helper method for Eiterising your control flow.
The benefit of doing this is that you will have great control and flexibility in your flow.
def eitherMe[ I, T ]( eitherIn: Either[ Error, Option[ I ] ],
err: () => Error,
block: ( I ) => Either[ Error, Option[ T ] ]
): Either[ Error, Option[ T ] ] = {
eitherIn match {
case Right( oi ) => oi match {
case Some( i ) => block( i )
case None => Left( err() )
}
case Left( e ) => Left( e )
}
}
def loginDevice(deviceSerialNumber: String, login: String, password: String): Either[Error, LocationResponse] = {
try {
db withSession { implicit session =>
val deviceRowOption = devices.filter(d => d.serialNumber === deviceSerialNumber).map(d => (d.id, d.currentLocationId.?, d.serialNumber.?)).firstOption
val locationRowEither = eitherMe(
Right( deviceRowOption ),
() => { DeviceNotExistError },
deviceRow => {
val locationRowOption = locations.filter(l => l.id === deviceRow._2.getOrElse(0L) && l.login === login && l.password === password).firstOption
Right( locationRowOption )
}
)
val programRowEither = eitherMe(
locationRowEither,
() => { IncorrectLoginOrPasswordError },
locationRow => {
val programRowOption = programs.filter(p => p.id === locationRow.programId).firstOption
Right( programRowOption )
}
)
val locationResponseEither = eitherMe(
programRowEither,
() => { ProgramNotExistError },
programRow => {
val program = Program(programRow.name, programRow.logo, programRow.moneyLevel, programRow.pointsForLevel,
programRow.description, programRow.rules, programRow.dailyCustomerScansLimit)
val locationData = LocationData(program)
val locationResponse = LocationResponse("access_token", System.currentTimeMillis(), locationData)
Right(locationResponse)
}
)
locationResponseEither
}
} catch {
case ex: SQLException =>
Left(DatabaseError)
}
}
For me, when I sometime can't avoid nested complexity, I would extract out section of the code that make sense together and make it into a new method and give it a meaningful name. This will both document the code and make it more readable, and reduce the complexity inside each individual method. And usually, once I have done that, I am able to see the flow better and might be able to refactor it to make more sense (after first writing the tests to cover the behavior I want).
e.g. for your code, you can do something like this:
class LocationDao {
val db = DbProvider.db
// Database tables
val devices = Devices.devices
val locations = Locations.locations
val programs = Programs.programs
val accessTokens = AccessTokens.accessTokens
def loginDevice(deviceSerialNumber: String, login: String, password: String): Either[Error, LocationResponse] = {
try {
db withSession { implicit session =>
checkDeviceRowOption(deviceSerialNumber, login, password)
}
} catch {
case ex: SQLException =>
Left(DatabaseError)
}
}
def checkDeviceRowOption(deviceSerialNumber: String, login: String, password: String): Either[Error, LocationResponse] = {
val deviceRowOption = devices.filter(d => d.serialNumber === deviceSerialNumber).map(d => (d.id, d.currentLocationId.?, d.serialNumber.?)).firstOption
deviceRowOption match {
case Some(deviceRow) => {
val locationRowOption = locations.filter(l => l.id === deviceRow._2.getOrElse(0L) && l.login === login && l.password === password).firstOption
locationRowOption match {
case Some(locationRow) => { checkProgramRowOption(locationRow) }
case None => Left(IncorrectLoginOrPasswordError)
}
}
case None => Left(DeviceNotExistError)
}
}
def checkProgramRowOption(locationRow: LocationRowType): Either[Error, LocationResponse] = {
val programRowOption = programs.filter(p => p.id === locationRow.programId).firstOption
programRowOption match {
case Some(programRow) => {
val program = Program(programRow.name, programRow.logo, programRow.moneyLevel, programRow.pointsForLevel,
programRow.description, programRow.rules, programRow.dailyCustomerScansLimit)
val locationData = LocationData(program)
val locationResponse = LocationResponse("access_token", System.currentTimeMillis(), locationData)
Right(locationResponse)
}
case None => Left(ProgramNotExistError)
}
}
}
Note that this is just an illustration and probably won't compile because I don't have your lib, but you should be able to tweak the code for it to compile.
I have multiple Option's. I want to check if they hold a value. If an Option is None, I want to reply to user about this. Else proceed.
This is what I have done:
val name:Option[String]
val email:Option[String]
val pass:Option[String]
val i = List(name,email,pass).find(x => x match{
case None => true
case _ => false
})
i match{
case Some(x) => Ok("Bad Request")
case None => {
//move forward
}
}
Above I can replace find with contains, but this is a very dirty way. How can I make it elegant and monadic?
Edit: I would also like to know what element was None.
Another way is as a for-comprehension:
val outcome = for {
nm <- name
em <- email
pwd <- pass
result = doSomething(nm, em, pwd) // where def doSomething(name: String, email: String, password: String): ResultType = ???
} yield (result)
This will generate outcome as a Some(result), which you can interrogate in various ways (all the methods available to the collections classes: map, filter, foreach, etc.). Eg:
outcome.map(Ok(result)).orElse(Ok("Bad Request"))
val ok = Seq(name, email, pass).forall(_.isDefined)
If you want to reuse the code, you can do
def allFieldValueProvided(fields: Option[_]*): Boolean = fields.forall(_.isDefined)
If you want to know all the missing values then you can find all missing values and if there is none, then you are good to go.
def findMissingValues(v: (String, Option[_])*) = v.collect {
case (name, None) => name
}
val missingValues = findMissingValues(("name1", option1), ("name2", option2), ...)
if(missingValues.isEmpty) {
Ok(...)
} else {
BadRequest("Missing values for " + missingValues.mkString(", ")))
}
val response = for {
n <- name
e <- email
p <- pass
} yield {
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
Or, with Scalaz:
val response = (name |#| email |#| pass) { (n, e, p) =>
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
if ((name :: email :: pass :: Nil) forall(!_.isEmpty)) {
} else {
// bad request
}
I think the most straightforward way would be this:
(name,email,pass) match {
case ((Some(name), Some(email), Some(pass)) => // proceed
case _ => // Bad request
}
A version with stone knives and bear skins:
import util._
object Test extends App {
val zero: Either[List[Int], Tuple3[String,String,String]] = Right((null,null,null))
def verify(fields: List[Option[String]]) = {
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) =>
val u = i match {
case 0 => t copy (_1 = s)
case 1 => t copy (_2 = s)
case 2 => t copy (_3 = s)
}
Right(u)
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
val res = verify(List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify(List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
}
The same thing, using reflection to generalize the tuple copy.
The downside is that you must tell it what tuple to expect back. In this form, reflection is like one of those Stone Age advances that were so magical they trended on twitter for ten thousand years.
def verify[A <: Product](fields: List[Option[String]]) = {
import scala.reflect.runtime._
import universe._
val MaxTupleArity = 22
def tuple = {
require (fields.length <= MaxTupleArity)
val n = fields.length
val tupleN = typeOf[Tuple2[_,_]].typeSymbol.owner.typeSignature member TypeName(s"Tuple$n")
val init = tupleN.typeSignature member nme.CONSTRUCTOR
val ctor = currentMirror reflectClass tupleN.asClass reflectConstructor init.asMethod
val vs = Seq.fill(n)(null.asInstanceOf[String])
ctor(vs: _*).asInstanceOf[Product]
}
def zero: Either[List[Int], Product] = Right(tuple)
def nextProduct(p: Product, i: Int, s: String) = {
val im = currentMirror reflect p
val ts = im.symbol.typeSignature
val copy = (ts member TermName("copy")).asMethod
val args = copy.paramss.flatten map { x =>
val name = TermName(s"_$i")
if (x.name == name) s
else (im reflectMethod (ts member x.name).asMethod)()
}
(im reflectMethod copy)(args: _*).asInstanceOf[Product]
}
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) => Right(nextProduct(t, i + 1, s))
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}.asInstanceOf[Either[List[Int], A]]
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
type T3 = Tuple3[String,String,String]
val res = verify[T3](List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify[T3](List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
I know this doesn't scale well, but would this suffice?
(name, email, pass) match {
case (None, _, _) => "name"
case (_, None, _) => "email"
case (_, _, None) => "pass"
case _ => "Nothing to see here"
}
I am working on a method that has 3 possible outcomes for multiple items: Error, Invalid and Success. For each of these I need to return a json list identifying which items were in error, invalid and successful.
My current attempt follows. I have used Object to represent the class my objects are as fully explaining would take too long. The Object class has a method process which returns a boolean to indicate success or error and throws an exception when the object is invalid:
def process(list: List[Objects]) = {
val successIds = new ListBuffer[Int]();
val errorIds = new ListBuffer[Int]();
val invalidIds = new ListBuffer[Int]();
list.foreach( item => {
try {
if (item.process) {
successIds ++ item.id
} else {
errorIds ++ item.id
}
} catch {
case e: Exception => invalidIds ++ item.id
}
})
JsonResult(
Map("success" -> successIds,
"failed" -> errorIds,
"invalid" -> invalidIds)
)
}
Problem is using Mutable data structures isn't very "Scala-y". I would prefer to build up these lists in some more functional way but I am quite new to scala. Any thoughts or hints as to how this might be done?
My though is using something like the flatMap method that takes a tuple of collections and collates them in the same way the flatMap method does for a single collection:
def process(list: List[Objects]) = {
val (success, error, invalid) = list.flatMap( item => {
try {
if (item.process) {
(List(item.id), List.empty, List.empty)
} else {
(List.empty, List(item.id), List.empty)
}
} catch {
case e: Exception =>
(List.empty, List.empty, List(item.id))
}
})
JsonResult(
Map("success" -> success,
"failed" -> error,
"invalid" -> invalid)
)
}
flatMap isn't what you need here - you need groupBy:
def process(list: List[Objects]) = {
def result(x: Objects) =
try if (x.process) "success" else "failed"
catch {case _ => "invalid"}
JsonResult(list groupBy result mapValues (_ map (_.id)))
}
There's always recursion:
class Ob(val id: Int) { def okay: Boolean = id < 5 }
#annotation.tailrec def process(
xs: List[Ob],
succ: List[Int] = Nil,
fail: List[Int] = Nil,
invalid: List[Int] = Nil
): (List[Int], List[Int], List[Int]) = xs match {
case Nil => (succ.reverse, fail.reverse, invalid.reverse)
case x :: more =>
val maybeOkay = try { Some(x.okay) } catch { case e: Exception => None }
if (!maybeOkay.isDefined) process(more, succ, fail, x.id :: invalid)
else if (maybeOkay.get) process(more, x.id :: succ, fail, invalid)
else process(more, succ, x.id :: fail, invalid)
}
Which works as one would hope (skip the reverses if you don't care about order):
scala> process(List(new Ob(1), new Ob(7), new Ob(2),
new Ob(4) { override def okay = throw new Exception("Broken") }))
res2: (List[Int], List[Int], List[Int]) = (List(1,2),List(7),List(4))
Adapted to make it compile without "Objects"
def procex (item: String): Boolean = ((9 / item.toInt) < 1)
def process (list: List[String]) = {
val li: List[(Option[String], Option[String], Option[String])] = list.map (item => {
try {
if (procex (item)) {
(Some (item), None, None)
} else {
(None, Some (item), None)
}
} catch {
case e: Exception =>
(None, None, Some (item))
}
})
li
}
// below 10 => failure
val in = (5 to 15).map (""+_).toList
// 0 to throw a little exception
val ps = process ("0" :: in)
val succeeders = ps.filter (p=> p._1 != None).map (p=>p._1)
val errors = ps.filter (p=> p._2 != None).map (p=>p._2)
val invalides = ps.filter (p=> p._3 != None).map (p=>p._3)
What doesn't work:
(1 to 3).map (i=> ps.filter (p=> p._i != None).map (p=>p._i))
_i doesn't work.
I'm trying to get the hang of working "the Scala way" so I was wondering if the following code is how things should be done in this case.
So I have the entities User and Company (mapped with LiftWeb mapper). User has currentUser which contains an Option[User] and Company has currentCompany which is an Option[Company]. In order to compare if the current user is the owner of the current company I'm doing something like:
Company.currentCompany.map{_.owner.get == User.currentUser.map{_.id.get}.openOr(-1) }.openOr(false)
It works but somehow it feels kinda verbose. Is it good? Is it not? Any better ideas?
Thanks!
Have you looked at using for-comprehensions? You could do something like the following:
for(
company <- Company.currentCompany.map{_.owner};
user <- User.currentUser.map{_.id}
) yield (company == user).getOrElse(false)
This will return true if Company.currentCompany is Some[value], and User.currentCompany is Some[value], and company.owner == user.id.
I feel there should be some way of getting rid of that getOrElse on the end, and returning the unwrapped boolean directly, hopefully someone else might be able to shed some light on this!
Using for-comprehension is definitively the solution, actually... or flatMap but less readable
To recall every generators are bound using flatMap function of the Monadic Option, except the last which is mapped (like any for and yield). Here is a good slideshow on the underneath concepts Monad
So the for comprehension is used to pass through all steps while they aren't encoded in the fail state (None for Option).
Here is a full example with four tests (the four basic cases) for each options (for and flatMap)
case class User(id: String) {
}
object User {
def currentUser(implicit me: Option[User]): Option[User] = me
}
case class Company(owner: Option[User]) {
}
object Company {
def currentCompany(implicit myCompany: Option[Company]): Option[Company] = myCompany
}
object Test extends App {
test1()
test2()
test3()
test4()
test5()
test6()
test7()
test8()
def test1() {
implicit val me: Option[User] = None
implicit val myCompany: Option[Company] = None
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test2() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany: Option[Company] = None
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test3() {
implicit val me: Option[User] = None
implicit val myCompany = Some(Company(me))
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test4() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany = Some(Company(me))
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test5() {
implicit val me: Option[User] = None
implicit val myCompany: Option[Company] = None
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
def test6() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany: Option[Company] = None
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
def test7() {
implicit val me: Option[User] = None
implicit val myCompany = Some(Company(me))
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
def test8() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany = Some(Company(me))
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
}
Given:
Case class user(name:String)
Case class company(owner:Option[User])
Val currentcompany=company(Some("Karl"))
Val currentuser=Some(user("Karl"))
Possible solution:
currentcompany.foldLeft(false) {
case (a,b) => currentuser.isDefined && b.owner == currentUser.get
}