Closed KatrinaHoffert closed 9 years ago
I do not really understand where the implicit class will be added, I created a Implicit ActiveDatabase for the default database,
class ActiveDatabase(name : String) {
val dbName = name
}
package object globals {
implicit lazy val database = new ActiveDatabase("default")
}
But I don't really understand how the implicit parameters work
First of all, note that the class can be simplified to merely class ActiveDatabase(val name: String)
. That will do the same as you've written, but the parameter will be name
.
Anyway, now when you import globals
(should be named Globals
for consistency), the implicit value is in scope of the entire file. So then our model functions would take in a curried, implicit parameter (implicit parameters have to be curried).
Sample syntax: def getLocation(locationId: Int)(implicit activeDb: ActiveDatabase): Try[Location]
. But it would be used the exact same way (ie, we don't have to specify the implicit parameter -- it'll be passed automatically).
We'd then use the variant of play.api.db.DB.withConnection
that takes in the database name as a parameter (see the docs).
EDIT: The above link isn't working because Github breaks it... Copy it from here: https://www.playframework.com/documentation/2.0/api/scala/play/api/db/DB$.html
.
This is what I have been trying to do but have been running into problems. I changed the getLocation exactly as you just said, but then the calls in the controller need a implicit database, which I cannot seem to get from just importing the globals._
You imported that in the controller, right? If you're getting an error, what is it?
globals._ is imported in LocationController but there is an error
def showLocation(locationId: Int) = Action {
Location.getLocationById(locationId) match {
case Success(location) =>
Ok(views.html.locations.displayLocation(location))
case Failure(ex) =>
// TODO: Create a prettier error page
InternalServerError("Encountered an error.\n\nDetails: " + ex.toString)
}
}
on line 2 the error is
could not find implicit value for parameter db: globals.ActiveDatabase not enough arguments for method getLocationById: (implicit db: globals.ActiveDatabase)scala.util.Try[models.Location]. Unspecified value parameter db.
What's the full signature of getLocationById
? What does the globals file look like?
I quickly threw together some code on my end and there's no errors. I copied the package object you previously mentioned into a file named Globals.scala
and imported it into the LocationController. The signature of getLocationById
is def getLocationById(locationId: Int)(implicit active: ActiveDatabase): Try[Location]
. And I defined ActiveDatabase
as class ActiveDatabase(val name: String)
.
This is now finished
Any methods that work with the database should have (implicit val activeDB: ActiveDatabase)
as a parameter and use that ActiveDatabase to specify which database to connect to via
DB.getConnection(activeDB.name) { conn =>
//use the connection in some way
}
This can be done by creating a custom type that stores the name of the database to use (recommended name:
ActiveDatabase
). The Play Framework's connections can specify the DB name, which we would get from this custom type.Then every function that works on the database takes in an
ActiveDatabase
implicitly. This is why it must be a custom type: implicit parameters look for an implicit variable of a given type and use the first found.Create a "globals" page (suggested name:
Globals.scala
) that is a package object and have it contain an instance ofActiveDatabase
with the non-testing database (declare it asimplicit
andlazy
). This must only be done for controllers and will only work for directly accessing the model. So for integration tests, you have to use the normal database. No way around that.In the non-testing pages, we include this (ie, with
import Globals._
). Testing pages will declare their own implicitActiveDatabase
(or make their own globals file).