Is it possible to create an overloaded play.api.mvc.Controller trait that has dependency injected arguments?
For example, say I have a couple of customized Actions that require a dependency injected AuthorizationService. I would like to write my controllers like this:
class UserController extends CustomController {
def getUser(userID: String) = CustomAction {
...
}
}
However, I can't figure out how to create the CustomController trait such that it doesn't require me to inject my AuthorizationService in my UserController. Is there a way to do this with Guice?
You can inject a field into your CustomController trait. The field should'n be final so it has to be declared as var in Scala.
#Inject() var authService: AuthorizationService
You can also make the injected var private and declare a public val which references the injected field. In this case val has to be lazy since injection occurs after class was instantiated. See Guice docs for more details.
#Inject() private var as: AuthorizationService = _
lazy val authService: AuthorizationService = as
It isn't possible to inject a dependency into a trait because a trait isn't instantiable. A trait doesn't have a constructor to define the dependencies, you must inject your AuthService via UserController
Example.
trait CustomController extends Controller {
val authService: AuthService
...
}
class UserController #Inject()(override val authService: AuthService) extends CustomController {
...
}
Related
While building a new Playframework app I am trying to use the cake pattern.
My first understanding was to mix custom traits into the controllers and pass those provided by Play as parameter:
trait MyComponents {
def actorSystem: ActorSystemClassic
lazy val service = new MyService(actorSystem)
}
class MyController(
controllerComponents: ControllerComponents,
override val actorSystem: ActorSystemClassic) with MyComponents {
// ...
// Use `service` from MyComponents
}
class MyApp extends BuiltInComponentsFromContext(context) {
val controller = new MyController(
controllerComponents, // <- Provided by BuiltInComponentsFromContext
)
}
That worked fine until I had to test MyController and tried to mock the service.
To mock the service I should be able to work with a stub of MyComponents that will provide the mock. To provide that stub I have to pass it as constructor parameter.
class MyController(
controllerComponents: ControllerComponents,
myComponents: MyComponents,
override val actorSystem: ActorSystemClassic) {
// ...
// Use `myComponents.service`
}
Of course my controller is more complex than that and he need more than one component to work. My fear is to end with a constructor that will become hardly manageable, with a lot of parameters.
To limit the number of parameters, one idea would be to mix all of the components in one. However I am not able to mix the instance on ControllerComponents provided by the super class BuiltInComponentsFromContext with MyComponents:
class MyController(components: MyComponents with ControllerComponents)
class MyApp extends BuiltInComponentsFromContext(context) {
val controller = new MyController(
new MyComponents with /*instance of controllerComponents provided by BuiltInComponentsFromContext*/
)
}
I do not want to pass the controllerComponents to MyComponents because that class provide business services, she don not care about controller components.
Can you help me to implement a real life application with the cake pattern ?
I'm using Mockito to test a scala class with a protected var. I would like to mock this var but obviously I can not access to this var via my mock class.
This is my code:
abstract class ETL_Generic(val fileCode: String, val rwSessionWrapper: RWSessionWrapper) extends Serializable {
protected var measurementsByFinalCode: scala.collection.Map[String, Measurement] = _
}
And this is the code to mock the abstract class:
val etlGenericMock = mock(classOf[ETL_Generic], withSettings().useConstructor("", rwSessionWrapperMock).defaultAnswer(CALLS_REAL_METHODS))
How can I assign a value to this var with the mock class?
Thanks.
I did an adapter extending the class to mock, overriding protected vars and then mocking this adapter.
Thanks!
I'm using PlayFramework 2.6 with Scala and Guice and I have the following design between my components:
Some Person class objects:
#Singleton
class Person1 #Inject() (personApiService: PersonApiService, personDBApiService: PersonDBApiService) extends Person {
override def gerPersonInfo(person: Person): Future[PersonInfo] = {
...
}
}
I have few of those (Person1, Person2...).
In PersonService I need a list of the people instances so I did this:
Created a trait:
trait PeopleManager {
def application: Application
def peopleList = List(
classOf[Person1],
classOf[Person2],
...
)
lazy val peopleInstances: List[Person] = peopleList.map(personClass => application.injector.instanceOf(personClass))
}
So now I can do:
class PersonService #Inject() (val application: Application) extends PeopleManager {
// now I can use here peopleInstances.
}
The problem is, in my controller I get PersonService as dependency and I get to circular dependency. So in my controller I have to use Provider as suggested by PlayFramework documentation:
class MyController #Inject() (cc: ControllerComponents, peopleService: Provider[PersonServcie]) extends AbstractController(cc) {
...
}
This solved the circular dependency issue, but I have a problem now in my tests to create a mock for Provider[PersonService].
I feel something can be better in the design here that wont make me get circular dependency error, does someone have a nice suggestion?
I'm using codingwell/scala-guice and trying to inject DAO-classes into constructors of other components/classes.
In the first attempt, I only used one DAO-class to see if it works:
class DaoModule extends AbstractModule with ScalaModule {
override def configure() {
val dao1 = new FirstDaoImpl
bind(new TypeLiteral[FirstDaoTrait] {}).toInstance(dao1)
}
}
The binding works as expected, it can be used for constructor injection.
In the second step, I wanted to add another DAO class to the module. However, that DAO-class depends on the first DAO:
class SecondDaoImpl #Inject()(firstDao: FirstDaoTrait) extends SecondDaoTrait
I'm not sure how to add the necessary binding to the existing module. Repeating the first step would result in this:
val dao2 = new SecondDaoImpl(???)
bind(new TypeLiteral[SecondDaoTrait] {}).toInstance(dao2)
But of course this class can only be instantiated by providing the first DAO (therefore the "???"). How can I do this?
Use bind and let scala-guice resolve the dependencies for you:
class DaoModule extends AbstractModule with ScalaModule {
override def configure() {
bind[FirstDaoTrait].to[FirstDaoImpl]
bind[SecondDaoTrait].to[SecondDaoImpl]
}
}
And now using the injector:
val injector = Guice.createInjector(new DaoModule())
val secondDao = injector.instance[SecondDaoTrait]
This question may help you understand my needs.
Cake pattern: one component per implementation, or one component per trait?
I have a Scala application using multiple UserService implementations which will be provided by component(s?).
I wonder if there is a way in another component to "scan" the application so that I can retrieve a set of all components providing an object which implement the trait UserService?
So that I can iterate over all the UserService interfaces provided by my cake built application?
I guess I can have a component which build a list of UserService according to its dependency, but is it possible to have this component building the list without having any hardcoded dependency?
You can simply have a list of UserService instances right into UserServiceComponent, and have the base UserService register itself in this list.
trait UserServiceComponent {
private val _userServices = collection.mutable.Buffer[UserService]()
def userServices: Seq[UserService] = _userServices.synchronized {
_userServices.toList // defensive copy
}
private def registerUserService( service: UserService ) = _userServices.synchronized {
_userServices += service
}
trait UserService {
registerUserService( this )
def getPublicProfile(id: String): Either[Error, User]
}
val mainUserService: UserService
}
trait DefaultUserServiceComponent extends UserServiceComponent { self: UserRepositoryComponent =>
protected class DefaultUserService extends UserService {
// NOTE: no need to register the service, this is handled by the base class
def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
}
val mainUserService: UserService = new DefaultUserService
}