Kamil Banaszczyk
Kamil Banaszczyk

Reputation: 1153

Implicit conversion with future

I've got problem with implicit conversion from DB model to APP model:

  override implicit def dbBook2AppBook(book: Book): BookApp = {
      val author = Await.result(authRep.getById(book.authID),1 seconds)
      BookApp(book.id.get, book.title, 
              AuthorApp(author.get.id,author.get.name,author.get.surname), book.available)
  }

Basically i want to convert Book to BookApp:

case class BookApp(id: Int,
                   title: String,
                   author: AuthorApp,
                   var available: Boolean)

case class Book(id: Option[Int] = None, title: String, authID: Int, available: Boolean)

And as you can see my Book class have Author ID, and i want my BookApp to have AuthorApp object inside. What i need to do is to obtain from database Author, while doing conversion from Book. From database through authRep: AuthorReposiotory i'm obtaining Future[Author].

My question is, if there is better way to obtain BookApp from convertion without need to use wait for Author object? Any suggestions?

Edit

For the answer from "Jon Anderson", I can't make conversion from Future[Book] to Future[BookApp], because when i'm obtaining more than one row from Database i'm receiving Future[Seq[Book]], then the conversion won't work. Additional information, i still preserve Future in whole application, because when i do conversion i do something like this:

  def findBooks(title: Option[String], authorName: Option[String], 
                authorSurname: Option[String], available: Option[Boolean])
                : Future[Seq[BookApp]] = {
    bookRep.findBooks(title,authorName,authorSurname,available)
                      .map(x => x.map( y => y: BookApp))
  }

So convertion is done on object by map, but still returned converted unwrapped object. Main problem is wait in convertion, so main question is, how can i get rid of that?

Upvotes: 1

Views: 496

Answers (2)

OlivierBlanvillain
OlivierBlanvillain

Reputation: 7768

As you discovered thought the types: you can't.

First of all, forget about using implicits in your design until you are more familiar with the languages, they won't help you here. (And forget about implicit conversion forever...)

One possible approach here would be stay work inside the Future context suing a dbBook2FutureAppBook(book: Book): Future[BookApp].

Converting from a Future[Seq[Book]] to Future[Seq[Bookapp]] can be done using Future#flatMap and Future.sequence:

val fsb: Future[Seq[Book]] = ...

val fsa: Future[Seq[Bookapp]] =
  fsb.flatMap { books => 
    Future.sequence(books.map(dbBook2FutureAppBook))
  }

This answers your question about Future, but be aware that this will emit one database query per book in your Future[Seq[Book]], which is a terrible idea. You probably want to write a Seq[Book] => Seq[Bookapp] query by hand.

Upvotes: 1

Jon Anderson
Jon Anderson

Reputation: 696

You should preserve the future all the way into the app layer. In this case, a for-comprehension works nicely...

override implicit def dbBook2AppBook(bookFuture: Future[Book]): Future[BookApp] = { 
    for {
       book <- bookFuture
       author <- authRep.getById(book.authID)
       //any other futures needed to create APP model here...
    } yield {
        BookApp(book.id.get, book.title, AuthorApp(author.get.id,author.get.name,author.get.surname), book.available)
    }
}

Upvotes: 0

Related Questions