How to use Qualifiers with Assisted Injection? - dagger-2

I would like to create a class where I can pass some runtime parameters as an input. To do so I can use Assisted Injection.
But I would also like to provide different implementations of this class depending on a Qualifier. Then I could have for instance 2 Assisted Factories that would provide different types.
When I try to create a #Provides method on a class that use Assisted Injection, I get this error:
Dagger does not support providing #AssistedInject types.
Is there a way to achieve what I am trying to do with dagger?
This is the general idea of what I would like to have even if this code doesn't work:
class DependencyWithQualifier(val type: Int)
class OtherDependency
class SampleClass #AssistedInject constructor(
#Assisted val name: String,
val dependencyWithQualifier: DependencyWithQualifier,
otherDependency: OtherDependency,
) {
#AssistedFactory
interface Type1Factory {
#Named("type1")
fun create(name: String): SampleClass
}
#AssistedFactory
interface Type2Factory {
#Named("type2")
fun create(name: String): SampleClass
}
}
#Module
#InstallIn(SingletonComponent::class)
class SampleClassModule {
#Provides
#Named("type1")
fun provideType1SampleClass(
name: String,
otherDependency: OtherDependency
) = SampleClass(
name = name,
dependencyWithQualifier = DependencyWithQualifier(1),
otherDependency = otherDependency
)
#Provides
#Named("type2")
fun provideType2SampleClass(
name: String,
otherDependency: OtherDependency
) = SampleClass(
name = name,
dependencyWithQualifier = DependencyWithQualifier(2),
otherDependency = otherDependency
)
}

Related

Create Guice dependency injection binding in Scala function

I want to bind a certain class, lets call it injected either to its real implementation real or to its mock.
Also side effects should happen and therefor a function would be preferable to encapsulate this work.
Doing that outside of a function is known. But that also requires the side effects to be coded multiple times
The functionality iam looking for could be outlined as this (none working approach!)
private def bindMocksOptional(configSettingKey: String, injected: Class[_], real: Class[_] , mock: Class[_]) {
configuration.getOptional[Boolean](configSettingKey) match {
case Some(true) => {
bind(injected).to(mock)
val message = s"Using a mock (${mock.getCanonicalName})for ${injected.getCanonicalName}"
Logger.warn(message)
println(Console.MAGENTA + message)
}
case _ => bind(injected).to(real)
}
}
The function should take the outlined types as parameters, look up some config settings and based on those bind either to mock or real implementation.
You can use Provider:
import com.google.inject.{AbstractModule, Guice, Inject, Provider}
class Configuration {
def getOptional[T](key: String): Option[T] = None
}
trait DatabaseClient
class DatabaseClientMock extends DatabaseClient
class DatabaseClientReal extends DatabaseClient
// ---
// 1. Define Guice Provider:
class DatabaseClientGuiceProvide #Inject()(configuration: Configuration)
extends Provider[DatabaseClient] {
override def get(): DatabaseClient = {
configuration.getOptional[Boolean]("mock") match {
case Some(true) =>
println("used mock")
new DatabaseClientMock
case _ =>
println("used real")
new DatabaseClientReal
}
}
}
class MainModule extends AbstractModule {
override def configure(): Unit = {
// 2. Bind dependencies of provider
bind(classOf[Configuration]).toInstance(new Configuration)
// 3. Bind provider
bind(classOf[DatabaseClient])
.toProvider(classOf[DatabaseClientGuiceProvide])
}
}
// 4. Test it:
object GuiceMain extends App {
val module = Guice.createInjector(new MainModule)
println(module.getInstance(classOf[DatabaseClient]))
}

Refer to super in default values of constructor parameters when implementing an interface

Assume I have an interface that provides many immutable properties with default getters, as such
interface Repository {
val name : String
val pattern : String
get() = "foo/bar/baz"
var url : String
get() = "http://mycompanyrepo.com/$name"
fun add() {
TODO("do something interesting here")
}
}
Now, there are several concrete implementations that just use most of the defaults. However, I also want to provide a ConfigurableRepository that allows for more flexibility in other projects that want to configure this at runtime, based on other user parameters.
How can I create a primary constructor that has optional parameters?
I was hoping something along the lines of:
class ConfigurableRepo(var name, var pattern, var url) {
...
}
Edited for clarification
The intent here is to use primary constructor behavior, where the named parameters are optional. More specifically, name, pattern, and url are all optional to anyone calling the constructor, and it will default to the interface's getters. But, I'm obviously trying to force the Kotlin interface to fit this model and it is probably a noob Kotlin user mistake, as I ultimately have no access to super here.
From the class docs
In fact, for declaring properties and initializing them from the
primary constructor, Kotlin has a concise syntax:
class Person(val firstName: String, val lastName: String, var age: Int) {
// ...
}
I seem to be getting errors that this hides members of the supertype and requires the override modifier. If I try to override the supertype and then provide a default, it complains that supertypes aren't accessible in this context.
Can this only be done in a secondary constructor with mutable
values?
I'm also open to suggestions/feedback on better ways to provide an interface that supports configuration over convention. The most important aspect being that the interface always provide defaults for a convention. Or, more simply put:
Is there a better way?
Please have a look at
https://kotlinlang.org/docs/reference/visibility-modifiers.html
Especially the 2nd paragraph around Classes and interfaces.
From these examples, a suggestion would be, if it's possible in your case, to use a base class instead of an interface. This would look something like this :
open class DefaultRepo {
protected open val name = "foo/bar/baz"
protected open val pattern = "foo/bar/baz"
protected open val url = "http://mycompanyrepo.com/$name"
open fun add() {
TODO("do something interesting here")
}
}
class RenamedRepo(newName: String): DefaultRepo() {
override val name = newName
}
class ConfigurableRepo(n: String, p: String, u: String): DefaultRepo() {
override val name = n
override val pattern = p
override val url = u
override fun add() {
}
}
Use an abstract class like this:
abstract class Repository {
open val name = "default"
open val pattern = "default"
open val url = "default"
}
// example: only override name
class RepoWithDifferentName(
override val name: String
): Repository() {
// ...
}
Since I now figured out how to do it with the vars, here an answer for the exact interface from your question.
class ConfigurableRepo(
private var _name: String,
private var _pattern: String,
private var _url: String) : Repository
{
override val name get () = _name
override val pattern get () = _pattern
override var url
get () = _url
set (u: String) { _url = u }
}
If your intention is to have default values for the parameters of the default constructor like this (pseudocode, which does not compile):
class ConfigurableRepo(
override var name,
override var pattern = super.pattern,
override var url = super.url
) {
...
}
Then the way to go would be the "null as default pattern" which one can always use when the desired default value can't be expressed in the method declaration. In your case, it could work out like this:
class ConfigurableRepo (
override val name: String,
_url: String? = null,
_pattern: String? = null
): Repository {
override val pattern = _pattern ?: super.pattern
override var url = _url ?: super.url
// ... more code ...
}
As you can see, the trick works with val as well as var properties and fields. Property name which does not have a default value in the interface is implemented with straight-forward field in this class.

Generic getter for multiple Collection in Mongo with Kotlin

I use Kotlin and Mongo (with KMongo) and I have multiple models as UserEntity, MovieEntity and so on. Each of them use a specific Dao class to do (actually) the same methods. Therefore, I'm trying to avoid any duplication by using a BaseDao which should have these methods instead.
So I pass the specific entity in the generic base as:
class UserDao : BaseDao<UserEntity>() { ... }
This base class implements the generic methods as follows:
open class BaseDao<T: Any>() {
fun get(id: String): T? {
return getCollection().findOneById(id)
}
fun save(entity: T): T {
return getCollection().save(entity)
}
fun delete(id: String) {
getCollection().deleteOneById(id)
}
...
}
However, a problem occurs on getCollection() method:
private inline fun <reified T: Any> getCollection(): MongoCollection<T> {
return MongoDb.getDatabase().getCollection<T>()
}
This gets a compilation error each time I call it:
Type inference failed: Not enough information to infer parameter T in
inline fun <reified T : Any> getCollection(): MongoCollection<T#1 (type parameter of app.api.db.dao.BaseDao.getCollection)>
Please specify it explicitly.
I can't find the right way to do this. I already checked these threads but I didn't make it work: Generic class type usage in Kotlin & Kotlin abstract class with generic param and methods which use type param.
Question:
How can I achieve this generic BaseDao which should get any collection of each child Dao?
the JVM forgets the type of the generic T in BaseDao<T: Any>() at runtime, which is why type inference fails. A solution to this could be to pass the KClass of T in the constructor of BaseDao:
open class BaseDao<T: Any>(val kClass: KClass<T>) {
...
}
After this, give your reified function an argument that accepts a `KClass:
private inline fun <reified T: Any> getCollection(val kClass: KClass<T>): MongoCollection<T> {
return MongoDb.getDatabase().getCollection<T>()
}
I'm unaware of a method to do this without passing the KClass as a argument to the function, but this should work, as the generic T can be derived from the provided kClass.
`
Another way would be to make all methods in BaseDao inline function with reified generics and dropping the generic on the class.
open class BaseDao() {
inline fun <reified T: Any> get(id: String): T? {
return getCollection().findOneById(id)
}
inline fun <reified T: Any> save (entity: T): T {
return getCollection().save(entity)
}
inline fun <reified T: Any> delete(id: String) {
getCollection().deleteOneById(id)
}
...
}
This way the generic T can be derived since the method calling getCollection is also reified.
(For KMongo 4.0.+) no need to use reified generics for the each method, instead this base class can be used as as a starting point:
open class BaseDao<T: Any>(
protected val collection: CoroutineCollection<T>
) {
suspend fun get(id: Id<T>): T? {
return collection.findOneById(id)
}
suspend fun save(entity: T): UpdateResult? {
return collection.save(entity)
}
suspend fun delete(id: Id<T>) {
collection.deleteOneById(id)
}
}
And implemented in the particular DAO, say SessionDao:
class SessionDao(collection: CoroutineCollection<DbSession>)
: BaseDao<DbSession>(collection)
(note: inheritance can be replaced with delegation by using by keyword if one feel better this way
This and other dao can be created via DI or some sort of dao factory:
class DbInstance(mongodbConnectionString: String = "mongodb://localhost:27017/myproject") {
private val connectionInfo = ConnectionString(mongodbConnectionString)
val client = KMongo.createClient().coroutine
val db = client.getDatabase(
connectionInfo.database ?: throw IllegalArgumentException("mongodb connection string must include db name")
)
val sessions = SessionDao(db.getCollection())
}
Notes:
This example is for the coroutine based kmongo, it can be easly converted to blocking kmongo by replacing CoroutineCollection to MongoCollection
I assume documents id's are annotated via Id container which helps to mitigate errors, so documents should be created in this fashion:
data class DbSession(
#BsonId
val id: Id<DbSession>,
val name: String,
)
The solution is to use reflection as Zigzago mentioned by using KMongoUtil:
protected fun getCollection(): MongoCollection<T> =
getDaoEntityClass().let { k ->
MongoDb.getDatabase().getCollection(
KMongoUtil.defaultCollectionName(k), k.java)
}
#Suppress("UNCHECKED_CAST")
private fun getDaoEntityClass(): KClass<T>
= ((this::class.java.genericSuperclass
as ParameterizedType).actualTypeArguments[0] as Class<T>).kotlin

Modify one value in a Scala class constructor in a concise way

If I want to modify one single parameter in a constructor.
In the Scala case class, the apply method will be overridden twice. Unless apply applies ( no pun ) to auxiliary constructor.
Related to
Modifying case class constructor parameter before setting value
How to override apply in a case class companion
How one can modify one single input from a constructor ?
Criteria :
The class must hold immutable data. All data must be accessible.
Note it doesn't have to be case class.
Additionally , no need to use the apply method.
no extra unused parameters. In the example below, _fistName is still accessible but unused.
case class Person(
lastName: String,
_fistName: String, ... )
{ lazy val fistName = _fistName.toLowerCase }
Here are two simple approaches.
Using class:
class Person(first: String, last: String) {
val firstName = first.toLowerCase
val lastName = last.toLowerCase()
}
val person = new Person("Adam", "Smith")
println(person.firstName + " " + person.lastName) // adam smith
Using trait + object's apply():
trait Person {
val firstName: String
val lastName: String
}
object Person {
def apply(first: String, last: String) = new Person {
override val firstName: String = first.toLowerCase
override val lastName: String = last.toLowerCase
}
}
val person = Person("Adam", "Smith")
println(person.firstName + " " + person.lastName) // adam smith
Note that classes must be instantiated with new, while traits (created with apply()) don't.
Why no case classes? Well, they are designed to serve as ADTs (abstract data types). Basically, they are thought of as containers for some data without any logic. That's why they get apply() out-of-the-box. If you want to override it, then means your class doesn't have the semantics of a case class.
I can see that #prayag took the effort of trying to help you force it into a case class, but seriously, if it doesn't have to be one (and you said so in the question), then don't make it one.
The reference you had posted seems to have lot of answers as well.
Two simple ways I could think of
make it abstract case class and define companion object which would mutate the value you want
define the member of case class as var and mutate it.
eg. (using scalatest)
class CaseClassSpecs extends FunSpec {
describe("case class modify") {
it("APPROACH 1 : modifies abstract case class member") {
object Item {
def apply(itemId: String, itemName: String) :Item = new Item(itemId.toLowerCase, itemName) {}
}
abstract case class Item private (val itemId: String, itemName: String)
val item1 = Item("SKU-ONE", "Shirts")
assert(item1.itemId == "sku-one")
assert(item1.itemName == "Shirts")
}
it("APPROACH 2 : modifies case class member which is var") {
case class Item (var itemId: String, itemName: String) {
itemId = itemId.toLowerCase()
}
val item1 = Item("SKU-ONE", "Shirts")
assert(item1.itemId == "sku-one")
assert(item1.itemName == "Shirts")
}
}
}
Class parameters are not necessarily class members. You can have class parameters that do not become class members.
Method 1
class Foo(bar0: String) {
val bar = bar0.toLowerCase()
}
#main
def main(): Unit = {
println(Foo("AsDfGh").bar)
}
prints:
asdfgh
and the decompiled code is:
public class Foo {
private final String bar;
public Foo(final String bar0) {
this.bar = bar0.toLowerCase();
}
public String bar() {
return this.bar;
}
}
You see, bar0 is a "temporary" value, it does not become a field because it is not referenced.
So if you want to change a value, just do not use the original value in the methods.
Method 2
For case classes, it does not seem to work in 2022, but here is another trick:
case class Point (x: Double, y: Double)
class PolarPoint(r: Double, alpha: Double) extends Point(r*Math.cos(alpha), r*Math.sin(alpha))
Here r and alpha do not become members of PolarPoint.
If you don't need two types, you can make the 1st constructor protected:
case class Foo protected(x:Int)
class FooImpl(x0:Int) extends Foo(myFunc(x0))
You will reference objects as Foos but create FooImpls.
Method 3
Your class can have multiple parameter lists and implicits:
class Qux(x:String)(implicit val y:String = x.toLowerCase())
is converted to:
public class Qux {
private final String y;
public static String $lessinit$greater$default$2(String var0) {
return Qux$.MODULE$.$lessinit$greater$default$2(var0);
}
public Qux(final String x, final String y) {
this.y = y;
}
public String y() {
return this.y;
}
}
You see that here only y becomes a field.

Scala Dependency Injection with Cake Pattern

I've been following this article which describes how to achieve dependency injection in Scala via the Cake Pattern:
http://jonasboner.com/real-world-scala-dependency-injection-di/
I'm kind of new to Scala and I admit some of it went over my head, so far I've got the following working:
// Setup the component and interface
trait AccountRepositoryComponent {
val accountRepository: AccountRepositoryInterface
trait AccountRepositoryInterface {
def message: String
}
}
// An implementation
trait MyAccountRepositoryComponent extends AccountRepositoryComponent {
object AccountRepository extends AccountRepositoryInterface {
def message: String = "Hello"
}
}
// Object to configure which implementations to use and retrieve them
object ComponentRegistry extends MyAccountRepositoryComponent {
val accountRepository = AccountRepository
}
// Example service using the above
object AccountService {
val repo = ComponentRegistry.accountRepository
def say: String = repo.message
}
println(AccountService.say)
What I'm failing to understand is how I would now pass in a fake repository to Account Service, say to change the output to "Test" rather than "Hello"?
There are various ways this could be modified to achieve a workable result, depending on what counts as a workable result for your situation. I'll go through a simpler possibility here.
First, the ComponentRegistry needs to become a trait, so it can be mixed in to the AccountService:
// Trait to configure which component implementations to use and retrieve them
object ComponentRegistry extends MyAccountRepositoryComponent {
val accountRepository = AccountRepository
}
// Example service using the above
object AccountService extends ComponentRegistry {
def say: String = accountRepository.message
}
println(AccountService.say)
This should print "Hello" as before. To set up a test case, add the following:
// Test implementation
trait TestAccountRepositoryComponent extends AccountRepositoryComponent {
object AccountRepository extends AccountRepositoryInterface {
def message: String = "Test"
}
}
// trait to configure test component implementations
trait TestComponentRegistry extends TestAccountRepositoryComponent {
val accountRepository = AccountRepository
}
Now we can set up a service that uses the test components:
// Example service using the above
object AccountService extends TestComponentRegistry {
//val repo = ComponentRegistry.accountRepository
def say: String = accountRepository.message
}
println(AccountService.say)
This should print "Test".
Note that you would probably want your AccountService to define its functionality in terms of other mixins/traits, which would expect the appropriate components to be available (layered into the "cake"), but wouldn't know which implementation was in use. Eg:
trait CustomerApi {
self: AccountRepositoryComponent => // Expects an implementation of AccountRepositoryComponent to be mixed in
def say: String = accountRepository.message
}
Now the method say is implemented without knowing what version of AccountRepository it will interact with, but knowing one must be provided (checked at compile time). So we can write:
object AccountService extends CustomerApi with ComponentRegistry
object TestAccountService extends CustomerApi with TestComponentRegistry
Calling println(AccountService.say) will generate "Hello", while calling println(TestAccountService.say) will generate "Test".
This post provides a succinct example of that (followed by an interesting alternative).