scala: how to inject inside guice module - scala

i am new in guice here is my code
trait MyRepository
class MyRepositoryImp extends MyRepository
trait MyService
class MyServiceImp #Inject()(myRepository:MyRepository) extends MyService
class Demo #Inject(myService:MyService)
class RepositoryModule extends AbstractModule {
override protected def configure(): Unit = {
bind(classOf[MyRepository]).toInstance(new MyRepositoryImpl)
}
}
class ServiceModule extends AbstractModule {
override protected def configure(): Unit = {
bind(classOf[MyService]).toInstance(new MyService(//what should i pass in here?))
}
}
my question is in ServiceModule how can i pass the injected MyRepository param in MyService constructor

Try with #Provides annotation
import com.google.inject.{AbstractModule, Guice, Inject, Injector, Provides}
import scala.jdk.CollectionConverters._
trait MyRepository {}
class MyRepositoryImp extends MyRepository {}
trait MyService {}
class MyServiceImp #Inject() (myRepository: MyRepository) extends MyService {}
class Demo #Inject() (myService: MyService) { println("Woohoo, I am wired!") }
class Module extends AbstractModule {
#Provides def repository: MyRepository = new MyRepositoryImp
#Provides def service(repo: MyRepository): MyService = new MyServiceImp(repo)
#Provides def demo(service: MyService): Demo = new Demo(service)
}
object guiceExample extends App {
val injector: Injector = Guice.createInjector(List(new Module).asJava)
val demo = injector.getInstance(classOf[Demo])
}
which outputs Woohoo, I am wired!.

Related

The easiest way to mock class with org.scalamock.scalatest.MockFactory

I have a trait:
trait MyRepository {
def save(dto: Dto): Unit
}
This trait is used by class:
class MyService(myRepository: MyRepository) {
def save(obj: Object): Unit = {
val dto = obj.toDto()
myRepository.save(dto)
}
}
and I want to test save method of MyService if it transform obj to dto properly. To do this I want to mock MyRepository.save(dto). This method should return it's args.
How to do this with org.scalamock.scalatest.MockFactory?
val mockRepo = mock[MyRepository]
(mockRepo.save _).expects(dto).returning(dto)
?

How to mock an interface in Guice module with ScalaMock?

How can I simply mock an interface needed for injection in Guice using ScalaMock?
I've ended up using
import org.scalamock.MockFactoryBase
import org.scalatest.exceptions.TestFailedException
trait MyMockFactory extends MockFactoryBase {
type ExpectationException = TestFailedException
override protected def newExpectationException(message: String, methodName: Option[Symbol]): TestFailedException = ???
}
and in TestModule
class TestModule extends AbstractModule with MyMockFactory {
override def configure(): Unit = {
val mockObject = mock[ClassName]
bind(classOf[ClassName]).toInstance(mockObject)
}
}
Use a separate test module by binding mocked instances to class names and use that module to inject objects for your tests.
Test Module goes like this:
class TestModule extends AbstractModule {
override def configure(): Unit = {
val mockObject = mock[ClassName]
bind(classOf[ClassName]).toInstance(mockObject)
}
}
Spec goes like this:
class SomeSpec extends FlatSpec {
val injector = Guice.createInjector(new TestModule)
val mockObject = injector.getInstance(classOf[ClassName])
}

How to inject a trait with macwire

I have a Scala trait
trait UserRepository {
def findByEmail(email: String): User
}
I would like to inject this into a service with MacWire
class AccountService(){
val userRepo = wire[UserRepository]
}
And then use it in a test or class
class AccountServiceSpec {
val userRepo = new UserRepositoryImpl()
val accountSvc = new AccountService() //<--not manually injecting repo in service constructor
}
but I'm getting a compile error in the service class
Cannot find a public constructor nor a companion object for
accounts.repository.UserRepository
You may try to transform userRepo to class parameter, that allows macwire automatically provide its value for service:
import com.softwaremill.macwire._
case class User(email: String)
trait UserRepository {
def findByEmail(email: String): User
}
class AccountService(val userRepo: UserRepository)
class UserRepositoryImpl extends UserRepository{
def findByEmail(email: String): User = new User(email)
}
class AccountServiceSpec {
val userRepo = new UserRepositoryImpl()
val accountSvc = wire[AccountService] //<--not manually injecting repo in service constructor
}

How to inject services into actors using the Play 2.4?

I am able to inject services into my Application class with no issues. But somehow I am unable to inject into the actors themselves.
My actor:
class PollerCrow #Inject()(
#Named("pollService") pollService: PollService[List[ChannelSftp#LsEntry]]
, #Named("redisStatusService") redisStatusService: StatusService
, #Named("dynamoDBStatusService") dynamoDbStatusService: StatusService
) extends BaseCrow {
... impl and stuff ...
}
My actor's companion object:
object PollerCrow extends NamedActor {
override def name: String = this.getClass.getSimpleName
val filesToProcess = ConfigFactory.load().getString("poller.crow.files.to.process")
def props = Props[PollerCrow]
}
I'm getting the following when I run it:
IllegalArgumentException: no matching constructor found on class watcher.crows.PollerCrow for arguments []
How can I fix this?
Edit:
I have binded my actors:
class ActorModule extends AbstractModule with AkkaGuiceSupport {
override def configure() {
bindPollerActors()
}
private def PollActors() = {
bindActor[PollerCrow](PollerCrow.name)
}
}
Edit 2:
Additional details to the class:
abstract class BaseCrow extends Crow with Actor with ActorLogging
class PollerCrow #Inject()(
#Named(ServiceNames.PollService) pollService: PollService[List[ChannelSftp#LsEntry]]
, #Named(ServiceNames.RedisStatusService) redisStatusService: StatusService
, #Named(ServiceNames.DynamoDbStatusService) dynamoDbStatusService: StatusService
) extends BaseCrow {
override def receive: Receive = {
...
}
}
object PollerCrow extends NamedActor {
override def name: String = this.getClass.getSimpleName
def props = Props[PollerCrow]
}
trait NamedActor {
def name: String
final def uniqueGeneratedName: String = name + Random.nextInt(10000)
}
You might to make Guice aware of you actors. This is clean approach:
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class ActorModule extends AbstractModule with AkkaGuiceSupport {
override def configure(): Unit = {
bindActor[YourActor]("your-actor")
}
}
#Singleton
class YourActor #Inject()(yourService: IYourService) extends Actor {
override def receive: Receive = {
case msg => unhandled(msg)
}
}
And application.conf:
play.modules {
enabled += "ActorModule"
}
For those who don't want to hassle, just call injector directly and don't forget to import Application to scope:
Play.application.injector.instanceOf[YourService]
Play.application.injector.instanceOf(BindingKey(classOf[YourService]).qualifiedWith("your-name"));

How to setup my UserService using the cake pattern inside of play?

Currently I am using guice to wireup my UserService inside of a controller like:
#Singleton
class UserController #Inject()(userService: UserService) extends Controller {
def show(userId: Int) {
val user = userService.get(userId)
Ok("hello " + user.name)
}
}
My UserService looks like:
abstract class UserService {
def get(userId: Int): User
}
class UserServiceImpl #Inject()(val userDao: UserDao) extends UserService {
def get(userId: Int): User = {
// ....
}
}
If I wanted to drop guice as a dependency and use the cake pattern, what would the code look like and how would I integrate this into Play so I could use this service in my Controllers?
Here's a suggestion with package names to give you an idea of how to organize all of this.
package controllers.users
trait UserController extends Controller {
this: UserService =>
def show(userId: Int) = {
val user = this.get(userId)
Ok("hello " + user.name)
}
}
package services.users
trait UserService {
def get(userId: Int): User
}
package services.users.impl
trait UserServiceImpl extends UserService {
def get(userId: Int) = { /*implementation*/ }
}
package controllers
object UserController extends UserController with UserServiceImpl
You can put your services and your controller traits anywhere you like in the project. You should put your controller object directly in the controllers package to make routing most convenient.