Multiple Futures in Play and using case classes to hold the future data - scala

The scenario:
I have two different Api calls to make (across the web). ApiCall1, ApiCall2. Eventually ApiCall1 will return an Option[Catalogue] and ApiCall2 will return a Seq[Catalogue]
I then need to take these two and build a FrontPage object. During the instantiation of the FrontPage object it creates a Seq[NewProducts]. Everytime it makes a NewProduct the NewProduct must also call a MongoDB across the web inside a Future. Every Future must have completed prior to handing the FrontPage object off to the view.
Here is the code for the FrontPage class:
case class FrontPage(maybeCat1: Option[Catalogue], maybeCat2: Seq[Catalogue]) {
val newProducts:Seq[NewProduct] = {
maybeCat2.map( { cat =>
NewProduct(cat)
})
}
}
Here is the code so far for the NewProduct class:
case class NewProduct(cat:Catalogue) {
val indivProduct:Option[IndivProduct] = {
// ???
// This next line goes out to Mongo and returns a Future[List[JsObject]]
val indiv:Future[List[JsObject]] = MongoFetch.getIndivProduct(cat)
//need to strip out the 'Future', wait for it to return?
val listJS = indiv .. ???? // <-- need just the List[JsObject]]
return IndivProduct(listJs) // <-- constructs a new Option[IndivProduct]
}
}
Here is the code so far for the controller:
def landing() = Action.async {
for {
catalogue1 <- models.Granite.getCatalogue("front-page") // <- ApiCall1
catalogue2 <- models.Granite.getCatalogue("tags") // <- ApiCall2
} yield {
//??? How to now build the FrontPage object
// surely it also depends on the future?
val fp = FrontPage(catalogue1, catalogue2)
Ok(views.html.frontpage.landing(fp)) // <- at this point all futures must have returned.
}
}
I really want to be able to pass a nice neat FrontPage object to the View (and hence the designers) with a really simple set of function defined on it for them to template with. All the futures must return. Catalogue1 and Catalogue2 are not dependent on anything or even each other. Creating a Seq[NewProducts] inside the FrontPage object is depending on both of them having returned. And I can not then pass the FrontPage object to the view until it to has returned the NewProducts from Mongo.
This level of complexity is above what I am used to. I am getting confused with where and when to use for/yield comprehensions. I am afraid that this will somehow just block because the Futures are too far embedded in a case class, within a case class. The very top level of the controller is wrapped in an Async, so will this mean that any and all Futures within that Async call will be non-blocking?

See the futures as steps to get to a fully complete front page, not parts of it and think what each little part of those steps would be.
For example, to construct an instance of NewProduct, create a method that talks to the db and returns the future completed NewProduct instances.
case class NewProduct(cat:Catalogue, indiv: Option[IndivProduct])
def newProductFor(cat: Catalogue): Future[NewProduct] =
for {
listJs <- MongoFetch.getIndivProduct(cat)
} yield NewProduct(cat, IndivProduct(listJs))
You can then create your front page, again, in a function/method that handles the loading/future:
case class FrontPage(
maybeCat1: Option[Catalogue],
maybeCat2: Seq[Catalogue],
newProducts: Seq[NewProduct])
def loadFrontPage: Future[FrontPage] =
for {
catalogue1 <- models.Granite.getCatalogue("front-page")
tags <- models.Granite.getCatalogue("tags")
newProducts <- loadNewProducts(tags)
} yield FrontPage(catalogue1, tags, newProducts)
def loadNewProducts(catalogues: Seq[Catalogue]): Future[Seq[NewProduct]] = {
Future.traverse(catalogues) { catalogue =>
newProductFor(catalogue)
}
}
Note Future.traverse which takes a collection with A:s in and a function that goes from A => Future[B] and returns a Future[collection[B]].
You can then call that in an async controller to feed to a template:
def page() = Action.async {
for {
frontPage <- loadFrontPage
} yield Ok(views.some.template(frontPage))
}

Related

Scala remote method injection

I have a requirement, I have a method, which takes another function. I want to get the result of this parameter function in my caller.
I have created a code snippet for this scenario:
class ProcessHandler {
def executeInstructions(x:String=>Array[String])
{
//print the resultant list here
}
}
object ProcessHandlerMain {
def main(args: Array[String]) {
val handler = new ProcessHandler
handler.executeInstructions( instruction)
}
def instruction(x:String):Array[String] =
{
List("words", "from", "book").toArray
}
}
This call handler.executeInstructions( instruction) will be made by another process. Here I am using main method to test it out.
Here is what I do not know:
How can I print the result of parameter function
If I have to expose this method executeInstructions() to client side, what is the best approach? Here we will not be passing text instructions, rather a function similar to instruction().
Thanks
Updated: From the response received, my code is updated to
class ProcessHandler {
def executeInstructions(x:String=>Array[String])
{
//print the resultant list here
val result = x("some string here")
//this array will be sent to another service
// dispatcher.dispatch(result)
}
}
My requirements are updated:
How can I print the result of parameter function : Done
If I have to expose this method executeInstructions() to client side, what is the best approach? Here we will not be passing text instructions, rather a function similar to instruction(). Pending
The client interface has will work as below:
1. client will be calling executeInstructions() remotely and pass function as parameter.
2. InstructionId will be passed to the injected function, the resultant array will be dispatched to another service.

Play Framework Search Bar

I'm trying to make a search bar with only one variable - the search input. I'm sure there's a fairly simple way to do this, but everything that I've found about getting input from the DOM (the views file) has been about using a Form and getting multiple variables. Is there a simpler way to do this if it's just a single variable?
I have a function in my Applications
def singleElement = Action { implicit request =>
val databaseSupport = new InteractWithDatabase(comm, db)
val put = Future {
while (true) {
val data = databaseSupport.getFromDatabase()
if (data.nonEmpty) {
comm.communicator ! data.head
}
}
}
Ok(views.html.singleElement)
}
I want to take some input from the user on the page singleElement and pass it into getFromDatabase which calls a MySQL query. How do I do this?
You can use restful and do something like this
routs file
GET /content/search/:search controllers.ContentController.search(search:String)
and in controller:
public Result search(String saerch) {}

Working with futures in slick and scalatra

I am trying to handle a future I got from slick in order to generate a response to a request, but I'm stuck at the "async" part.
Here is a snippet:
get("/tmp") {
new AsyncResult() {
override val is: Future[_] = db.run(Users.findUserWithLogin("user"))
}
}
Now, the db.run call returns a Future[Option[User]]. How do I returns a response depending on the content of the option?
In this case, you need to map the future returned by Slick to the result that you want rather than setting is directly to the Slick result. So, the following would be an example of how you might handle it:
get("/tmp") {
new AsyncResult() {
val is = db.run(Users.findUserWithLogin("user")) map {
case Some(u) => //return some stuff about the user
case None => //return some stuff about user not being found
}
}
}
As noted in the comments below, the AsyncResult is not strictly necessary. You can find more details in the Scalatra documentation.

Composing `Future` result in Play Framework with Scala

I am trying to write a Play Framework asynchronous Action for the following URL:
POST /users/:userId/items
My database calls all return Future[...], where ... is Option[A] for find methods and Option[Id] for create methods.
I would like to check for the existence of the userId before trying to create the new item. I have a method Users.findById(userId) that returns a Future[Option[User]]. The result is Some(User) if the user exists and None if not. Items.create() also returns a Future[Option[itemId]].
I am trying to compose something using for:
for {
user <- Users.findById(userId)
if user.isDefined
} yield {
Items.create(...) map { itemId => Ok(itemId) } getOrElse NotFound
}
I would like to return Ok(itemId) if the item is successfully created. I'm not sure how to handle the error case. I would like to return NotFound if either the userId is invalid or the item cannot be created (maybe a field conflicts with a unique value already in the database).
I'm not sure what to put after the for structure. I tried getOrElse, but that does not compile, since Future does not have a getOrElse method.
Ideally, I can handle URLs containing several ids to check, e.g.:
PUT /users/:userId/foo/:fooId/bar/:barId
and confirm that userId, fooId, and barId are all valid before doing the update. All of those calls (Users.findById, Foo.findById, and Bar.findById) will return Future[Option[A]].
It's that double-nesting (Future of Option) that seems to get people every time. Things become a lot easier if you can flatten stuff out first.
In this case, Future already has a way of representing an error condition, it can wrap an Exception as well as a success value, that's something you can use...
// making this a Singleton avoids the cost of building a stack trace,
// which only happens when an Exception is constructed (not when it's thrown)
object NotFoundException extends RuntimeException("Empty Option")
// The map operation will trap any thrown exception and fail the Future
def squish[T](x: Future[Option[T]]) =
x map { _.getOrElse(throw NotFoundException) }
It's now a lot easier to use those squished results in a comprehension:
val result = for {
user <- squish(Users findById userId)
itemId <- squish(Items.create(user, ...))
} yield {
Ok(itemId)
} recover {
case NotFoundException => NotFound
}
Which will, of course, evaluate to a Future. This is async programming, after all :)
Any exceptions other than NotFoundException will still be exposed.

Changing Akka actor state by passing a method with arguments to "become"

I am having some trouble using become in my Akka actor. Basically, my actor has a structure like so:
// This is where I store information received by the actor
// In my real application it has more fields, though.
case class Information(list:List[AnyRef]) {
def received(x:AnyRef) = {
Information(list :+ x)
}
}
class MyActor extends Actor {
// Initial receive block that simply waits for a "start" signal
def receive = {
case Start => {
become(waiting(Information(List())))
}
}
// The main waiting state. In my real application, I have multiple of
// these which all have a parameter of type "Information"
def waiting(info:Information):Receive = {
// If a certain amount of messages was received, I decide what action
// to take next.
if(someCondition) {
decideNextState(x)
}
return {
case Bar(x) => {
//
// !!! Problem occurs here !!!
//
// This is where the problem occurs, apparently. After a decision has been
// made, (i.e. decideNextState was invoked), the info list should've been
// cleared. But when I check the size of the info list here, after a decision
// has been made, it appears to still contain all the messages received
// earlier.
//
become(waiting(info received x))
}
}
}
def decideNextState(info:Information) {
// Some logic, then the received information list is cleared and
// we enter a new state.
become(waiting((Information(List())))
}
}
Sorry for the long code snippet, but I couldn't really make it any smaller.
The part where the problem occurs is marked in the comments. I am passing a parameter to the method that returns the Receive partial function which is then passed to the become method. However, the created partial function seems to somehow preserve state from an earlier invocation. I find the problem a bit difficult to explain, but I did my best to do so in the comments in the code, so please read those and I'll answer anything that is unclear.
Your logic is a little convoluted but I'll take a shot at what could be the problem:
If someCondition is true then your actor steps into a state, let's call it S1 characterized by a value Information(List()). And then you return (by the way, avoid using return unless it is absolutely necessary) a receive method which will put your actor into a state S2 characterized by a list Information(somePreviousList :+ x). So at this point your stack of states has S1 on top. But when you receive a Bar(x) message the state S2 will be pushed, thus covering S1 and you actually transition into a state characterized by an Information with the old values + your new x.
Or something like that, the recursion in your actor is a bit mesmerizing.
But I'll suggest rewriting that code since it seems that the state which changes is something of type Information and you are manipulating this state using Akka's actor state transitions which is not at all the best tool to do that. become and unbecome are meant to be used to transition from different states of the actor's behavior. That is, an actor can have a different behavior at any time and you use become and unbecome to change between these behaviors.
Why not do something like this ?
class MyActor extends Actor {
private var info = Information(List.empty)
def receive = {
case Start => info = Information(List()) //a bit redundant, but it's just to match 1:1 with your code
case Bar(x) => {
if (someCondition) {
info = Information(List.empty)
}
info = info received x
}
}
}
I might not have captured your entire idea, but you get the picture.