I have this play framework 2 code (simplified):
import formatters.json.IdeaTypeFormatter._
object IdeaTypes extends Controller {
def list = Action { request =>
Ok(toJson(IdeaType.find(request.queryString)))
}
def show(id: Long) = Action {
IdeaType.findById(id).map { ideatype =>
Ok(toJson(ideatype))
}.getOrElse(JsonNotFound("Type of idea with id %s not found".format(id)))
}
}
IdeaType
class extends Entity
, and it's companion object, IdeaType
, extends EntityCompanion
.
As you may expect, I have this kind of code in every controller, so I'd like to extract the basic behavior to a trait, something like this:
abstract class EntityController[A<:Entity] extends Controller {
val companion: EntityCompanion
val name = "entity"
def list = Action { request =>
Ok(toJson(companion.find(request.queryString)))
}
def show(id: Long) = Action {
companion.findById(id).map { entity =>
Ok(toJson(entity))
}.getOrElse(JsonNotFound("%s with id %s not found".format(name, id)))
}
}
But I get the following error:
[error] EntityController.scala:25: No Json deserializer found for type List[A].
[error] Try to implement an implicit Writes or Format for this type.
[error] Ok(toJson(companion.find(request.queryString)))
[error] ^
[error] EntityController.scala:34: No Json deserializer found for type A.
[error] Try to implement an implicit Writes or Format for this type.
[error] Ok(toJson(entity))
[error] ^
I don't know how to tell that the implicit Writes
will be implemented by the classes implementing the EntityController
trait (or inheriting the abstract class EntityController
)
-- edit
so far now I'm doing it like this:
abstract class CrudController[A <: Entity](
val model: EntityCompanion[A],
val name: String,
implicit val formatter: Format[A]
) extends Controller {
and use it like this
object CrudIdeaTypes extends CrudController[IdeaType](
model = IdeaType,
name = "type of idea",
formatter = JsonIdeaTypeFormatter
)
I could't get scala to automatically pick it using implicits. I tried with this import but it didn't work
import formatters.json.IdeaTypeFormatter._
If you want the controler classes themselves to define the implicit, then just declare abstract implicit values, and define them in the derived classes.
abstract class EntityController[A<:Entity] extends Controller {
protected implicit def entityWriter: Writes[A]
protected implicit def entityListWriter: Writes[List[A]]
...
}
class MyEntity extends Entity {
...
}
class MyEntityController extends EntityController[MyEntity] {
protected def entityWriter: Writes[MyEntity] = ...
protected def entityListWriter: Writes[List[MyEntity]] = ...
}
However, it is much more practical to define these implicits outside the controller, typically in the companion object of your entity, so that they the compiler can find them automatically without import.
Then, pass the implicit values to the constructor of EntityController
:
abstract class EntityController[A<:Entity](implicit entityWriter: Writes[A], entityListWriter: Writes[List[A]] ) extends Controller {
...
}
class MyEntity extends Entity {
...
}
object MyEntity {
protected implicit def entityWriter: Writes[A]
protected implicit def entityListWriter: Writes[List[A]]
}
class MyEntityController extends EntityController[MyEntity] {
...
}
Final note, the implicit to List[MyEntity] is probably unneeded( thus only the implicit for MyEntity would need to be explicitly defined). I have not checked, but usually when using this "typeclass pattern", the framework will already define an implicit for each List[T], provided that there is an implicit for T. This is probably the case, though I have not checked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With