How to initialize a variable in Kotlin with an Interfaced type? - kotlin

I'm brand new to Kotlin. I wanted to try the create-react-kotlin-app since I'm a react developer currently and I want to explore Kotlin.
I'm having a tough time instaniating my variables. I'm trying to do this:
fun main(args: Array<String>) {
val rootDiv = document.getElementById("root")
val gm : GameUiProps
gm.numPlayers = 2
gm.cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
gm.playerArray = arrayOf("Player 1", "Player 2")
RBuilder.render(rootDiv) {
GameUi(gm)
}
}
My gm variable isn't being initialized and I can't figure how how to do it.
I need to initiazle my GameUi component with props, but I can't figure out how to do that.
So GameUiProps comes from my component
interface GameUiProps : RProps {
var numPlayers: Int
var playerArray: Array<String>
var cardArray: Array<String>
}
class GameUi(props: GameUiProps) : RComponent<GameUiProps, RState>(props) {
private fun RBuilder.createHands() {
var numPlayers = props.numPlayers
val handArray: ArrayList<Unit> = ArrayList()
while (numPlayers > 0) {
handArray.add(
handUi(
props.playerArray[numPlayers],
props.cardArray[numPlayers]
)
)
numPlayers--
}
}
override fun RBuilder.render() {
div {
h1("sweet") {
+"RKT"
}
div {
createHands()
}
}
}
}

Your GameUiProps is just an interface, so you cannot instantiate it directly.
What you have (val gm: GameUiProps) is just field declaration without assigning anything to it. You obviously cannot access nor write any properties to it.
You need to either create a simple implementation with constructor, or an anonymous object.
Class that declares all interface fields in the constructor:
class GameUiPropsImpl(
override var numPlayers: Int,
override var playerArray: Array<String>,
override var cardArray: Array<String>) : GameUiProps
then in your fun main:
val gm = GameUiPropsImpl(2, arrayOf("Player 1", "Player 2"), arrayOf("Card to be dealt", "Cards to be Dealt"))
Or using an anonymous class implementing the interface:
val gm = object: GameUiProps{
override var numPlayers = 2
override var playerArray = arrayOf("Player 1", "Player 2")
override var cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
}

It has been a while, but for anyone who might need this, you just need to assign the variable gm to an object of type GameUiProps. Below is what I would have done.
val gm = object : GameUiProps{
override var numPlayers = 2
override var cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
override var playerArray = arrayOf("Player 1", "Player 2")
}
Hope it helps!

Related

How we can mock a CoroutineDatabase in ktor?

I use the KMongo tool
How we can mock a Coroutine Database?
How can we mock our database in a koin module?
Is there a way to do this?
Thanks for guiding me
Methods I have tried and it has not worked:
The first method:
single<CoroutineDatabase> {
val client = Mockito.mock(CoroutineClient::class.java)
client.getDatabase(CoreConstants.DATABASE_NAME)
}
The second method:
single<CoroutineDatabase> {
val client = declareMock<CoroutineClient> { }
client.getDatabase(CoreConstants.DATABASE_NAME)
}
I've managed to get this working with MockK with the following approach.
TLDR
Just use a mock of MongoDatabase/MongoCollection<T> and make their coroutine extension property return a mocked CoroutineDatabase/CoroutineCollection<T>. Also need to mock the actual MongoDatabase::getCollection to return the respective MongoCollection<T>.
Suppose we have this scenario.
data class User(val id: Int, val name: String)
class Service(private val myDatabase: CoroutineDatabase) {
private val userCollection: CoroutineCollection<User> = myDatabase.getCollection("users")
suspend fun getById(id: Int): User? = userCollection.findOneById(id)
}
Since userCollection is acquired by calling the inline method CoroutineDatabase::getCollection we need to mock all the code inside that inline because inline methods cannot be mocked with MockK (at the time of writing). Looking at the method code
inline fun <reified TDocument : Any> getCollection(
collectionName: String = KMongoUtil.defaultCollectionName(TDocument::class)
): CoroutineCollection<TDocument> =
database.getCollection(collectionName, TDocument::class.java).coroutine
It just calls com.mongodb.reactivestreams.client.MongoDatabase::getCollection and then uses this extension property to map it to a CoroutineCollection. Notice it uses the field database from CoroutineDatabase which is a MongoDatabase (The CoroutineDatabase was previously obtain via a similar extension property for MongoDatabase).
val <T : Any> MongoCollection<T>.coroutine: CoroutineCollection<T> get() = CoroutineCollection(this)
val MongoDatabase.coroutine: CoroutineDatabase get() = CoroutineDatabase(this)
Having all of this we need to mock:
Both coroutine extension properties on MongoDatabase and MongoCollection<T> (see mocking extension properties with MockK)
The actual MongoDatabase::getCollection because CoroutineDatabase::getCollection is an inline function
// Arrange
val mockedMongoDd: MongoDatabase = mockk<MongoDatabase> {
mockkStatic(MongoDatabase::coroutine)
val that = this
every { coroutine } returns mockk {
every { database } returns that
}
}
val mockedMongoCol: MongoCollection<User> = mockk<MongoCollection<User>> {
mockkStatic(MongoCollection<T>::coroutine)
val that = this
every { ofType<MongoCollection<T>>().coroutine } returns mockk {
every { collection } returns that
}
}
every {
mockedMongoDb.getCollection("users", User::class.java)
} returns mockedMongoCol
val mockedCoroutineDb = mockedMongoDb.coroutine
val mockedCoroutineCol = mockedMongoCol.coroutine
val service = Service(mockedCoroutineDb)
val expectedUser = User(2, "Joe")
coEvery {
mockedCoroutineCol.findOneById(2)
} returns expectedUser
// Act
val actualUser = service.getById(2)
// Assert
assertEquals(expectedUser, actualUser)
Finally, one could make some methods like the following to hide this details from the test.
inline fun <reified T : Any> mockkCoroutineCollection(
name: String? = null,
relaxed: Boolean = false,
vararg moreInterfaces: KClass<*>,
relaxUnitFun: Boolean = false,
block: MongoCollection<T>.() -> Unit = {}
): MongoCollection<T> = mockk(name, relaxed, *moreInterfaces, relaxUnitFun = relaxUnitFun) {
mockkStatic(MongoCollection<*>::coroutine)
val that = this
every { coroutine } returns mockk(name, relaxed, *moreInterfaces, relaxUnitFun = relaxUnitFun) {
every { collection } returns that
}
block()
}
inline fun mockkCoroutineDatabase(
name: String? = null,
relaxed: Boolean = false,
vararg moreInterfaces: KClass<*>,
relaxUnitFun: Boolean = false,
block: MongoDatabase.() -> Unit = {}
): MongoDatabase = mockk(name, relaxed, *moreInterfaces, relaxUnitFun = relaxUnitFun) {
mockkStatic(MongoDatabase::coroutine)
val that = this
every { coroutine } returns mockk(name, relaxed, *moreInterfaces, relaxUnitFun = relaxUnitFun) {
every { database } returns that
}
block()
}
This would reduce the first lines to
val mockedMongoDb: MongoDatabase = mockkCoroutineDatabase()
val mockedMongoCol: MongoCollection<User> = mockkCoroutineCollection<User>()
// ...

Kotlin on Android: How to use LiveData from a database in a fragment?

I use MVVM and have a list of data elements in a database that is mapped through a DAO and repository to ViewModel functions.
Now, my problem is rather banal; I just want to use the data in fragment variables, but I get a type mismatch.
The MVVM introduces a bit of code, and for completeness of context I'll run through it, but I'll strip it to the essentials:
The data elements are of a data class, "Objects":
#Entity(tableName = "objects")
data class Objects(
#ColumnInfo(name = "object_name")
var objectName: String
) {
#PrimaryKey(autoGenerate = true)
var id: Int? = null
}
In ObjectsDao.kt:
#Dao
interface ObjectsDao {
#Query("SELECT * FROM objects")
fun getObjects(): LiveData<List<Objects>>
}
My database:
#Database(
entities = [Objects::class],
version = 1
)
abstract class ObjectsDatabase: RoomDatabase() {
abstract fun getObjectsDao(): ObjectsDao
companion object {
// create database
}
}
In ObjectsRepository.kt:
class ObjectsRepository (private val db: ObjectsDatabase) {
fun getObjects() = db.getObjectsDao().getObjects()
}
In ObjectsViewModel.kt:
class ObjectsViewModel(private val repository: ObjectsRepository): ViewModel() {
fun getObjects() = repository.getObjects()
}
In ObjectsFragment.kt:
class ObjectsFragment : Fragment(), KodeinAware {
private lateinit var viewModel: ObjectsViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this, factory).get(ObjectsViewModel::class.java)
// I use the objects in a recyclerview; rvObjectList
rvObjectList.layoutManager = GridLayoutManager(context, gridColumns)
val adapter = ObjectsAdapter(listOf(), viewModel)
// And I use an observer to keep the recyclerview updated
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})
}
}
The adapter:
class ObjectsAdapter(var objects: List<Objects>,
private val viewModel: ObjectsViewModel):
RecyclerView.Adapter<ObjectsAdapter.ObjectsViewHolder>() {
// Just a recyclerview adapter
}
Now, all the above works fine - but my problem is that I don't want to use the observer to populate the recyclerview; in the database I store some objects, but there are more objects that I don't want to store.
So, I try to do this instead (in the ObjectsFragment):
var otherObjects: List<Objects>
// ...
if (condition) {
adapter.objects = viewModel.getObjects()
} else {
adapter.objects = otherObjects
}
adapter.notifyDataSetChanged()
And, finally, my problem; I get type mismatch for the true condition assignment:
Type mismatch: inferred type is LiveData<List> but List was expected
I am unable to get my head around this. Isn't this pretty much what is happening in the observer? I know about backing properties, such as explained here, but I don't know how to do that when my data is not defined in the ViewModel.
We need something to switch data source. We pass switching data source event to viewModel.
mySwitch.setOnCheckedChangeListener { _, isChecked ->
viewModel.switchDataSource(isChecked)
}
In viewModel we handle switching data source
(To use switchMap include implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0")
class ObjectsViewModel(private val repository: ObjectsRepository) : ViewModel() {
// Best practice is to keep your data in viewModel. And it is useful for us in this case too.
private val otherObjects = listOf<Objects>()
private val _loadDataFromDataBase = MutableLiveData<Boolean>()
// In case your repository returns liveData of favorite list
// from dataBase replace MutableLiveData(otherObjects) with repository.getFavorite()
fun getObjects() = _loadDataFromDataBase.switchMap {
if (it) repository.getObjects() else MutableLiveData(otherObjects)
}
fun switchDataSource(fromDataBase: Boolean) {
_loadDataFromDataBase.value = fromDataBase
}
}
In activity/fragment observe getObjects()
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})
You can do something like this:
var displayDataFromDatabase = true // Choose whatever default fits your use-case
var databaseList = emptyList<Objects>() // List we get from database
val otherList = // The other list that you want to show
toggleSwitch.setOnCheckedChangeListener { _, isChecked ->
displayDataFromDatabase = isChecked // Or the negation of this
// Update adapter to use databaseList or otherList depending upon "isChecked"
}
viewModel.getObjects.observe(viewLifecycleOwner) { list ->
databaseList = list
if(displayDataFromDatabase)
// Update adapter to use this databaseList
}

When trying to implement Branch.io deep-linking using their share sheet, no action executes

I am trying to implement deep linking with branch and having issues with the Share Sheet. It just doesn't work. No matter what I click, the relevant action doesn't happen and it just goes back to the bottom of the screen. Even when I click copy, the text doesn't copy. there are no errors so I don't know what's wrong.
This is my code (it is a single item in a recyclerView. I am using GroupieAdapter):
class SingleCommunityOption(val community: Community, val activity : MainActivity) : Item<ViewHolder>() {
private lateinit var buo: BranchUniversalObject
private lateinit var lp: LinkProperties
override fun getLayout(): Int {
return R.layout.community_option_layout
}
override fun bind(viewHolder: ViewHolder, position: Int) {
val firebaseAnalytics = FirebaseAnalytics.getInstance(viewHolder.root.context!!)
val title = viewHolder.itemView.community_option_title
val description = viewHolder.itemView.community_option_description
val memberCount = viewHolder.itemView.community_option_members_count
val share= viewHolder.itemView.community_share
title.text = community.title
description.text = community.description
memberCount.text = "${community.members}"
buo = BranchUniversalObject()
.setCanonicalIdentifier(community.id)
.setTitle(community.title)
.setContentDescription("")
.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setContentMetadata(ContentMetadata().addCustomMetadata("type", "community"))
lp = LinkProperties()
buo.listOnGoogleSearch(viewHolder.root.context)
share.setOnClickListener {
val ss = ShareSheetStyle(activity, "Republic invite", "Join me in this republic.")
.setCopyUrlStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
.setMoreOptionStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_search), "Show more")
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK_MESSENGER)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.WHATS_APP)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.TWITTER)
.setAsFullWidthStyle(true)
.setSharingTitle("Share With")
buo.showShareSheet(activity, lp, ss, object : Branch.BranchLinkShareListener {
override fun onShareLinkDialogLaunched() {}
override fun onShareLinkDialogDismissed() {}
override fun onLinkShareResponse(sharedLink: String, sharedChannel: String, error: BranchError) {}
override fun onChannelSelected(channelName: String) {
firebaseAnalytics.logEvent("community_shared_$channelName", null)
}
})
}
}
}

Use a class from a list of generic interface

I am trying to implement a QueryBus. Basically, I want to register a list of QueryHandlers. Each QueryHandler implements a handle method defined by an interface. Each QueryHandler is associated to a Query. I want to be able to retrieve a QueryHandler using the Query and call handle on it.
The thing is the handle has to be generic because each QueryHandler handles a Query differently. They all take a dedicated Query and may return whatever they want.
interface Query<R>
interface QueryHandler<R, Q : Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
class ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
class GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = mapOf(
with(ListBooksQueryHandler()) {this.listenTo() to this},
with(GetBookQueryHandler()) {this.listenTo() to this}
)
val command = ListBooksQuery()
val result = queryHandlers[command::class.toString()].handle(command)
// Should print the list of BookDto
print(result)
}
I don't even know if its possible, to be honest.
UPDATE 1:
I changed the usage example in the main to show what I am really trying to do. The List was for (bad?) demonstration purpose. I want to store the QueryHandlers and retrieve them from a map.
Additional resources:
Here is what I really want to do:
https://gist.github.com/ValentinTrinque/76b7a32221884a46e657090b9ee60193
UPDATE I've read your gist and tried to come up with a solution that will provide a clean interface to the user of the QueryBusMiddleware.
Note that I used objects instead of classes for the QueryHandler implementations, which felt more natural to me (since there is only one possible entry in the map for each Query implementation).
interface Query<R>
interface QueryHandler<R, Q: Query<R>> {
fun handle(query: Q): R
fun listenTo(): String
}
// DTOs
data class BookDto(val name: String)
// List books query
data class ListBooksQuery(val page: Int = 1): Query<List<BookDto>>
object ListBooksQueryHandler: QueryHandler<List<BookDto>, ListBooksQuery> {
override fun handle(query: ListBooksQuery): List<BookDto> {
return listOf(BookDto("Dune"), BookDto("Dune II"))
}
override fun listenTo(): String = ListBooksQuery::class.toString()
}
// Get book query
data class GetBookQuery(val name: String): Query<BookDto?>
object GetBookQueryHandler: QueryHandler<BookDto?, GetBookQuery> {
override fun handle(query: GetBookQuery): BookDto {
return BookDto("Dune")
}
override fun listenTo(): String = GetBookQuery::class.toString()
}
// Run it!
fun main(args: Array<String>) {
// Initializing query bus
val queryHandlers = listOf(
ListBooksQueryHandler,
GetBookQueryHandler
)
val dispatcher: QueryBusMiddleware = QueryDispatcherMiddleware(queryHandlers)
// Calling query bus
val query = ListBooksQuery()
// Result should be List<BookDto>
val result = dispatcher.dispatch(query)
print(result)
}
interface QueryBusMiddleware {
fun <R, Q : Query<R>> dispatch(query: Q): R
}
class QueryDispatcherMiddleware constructor(handlers: List<QueryHandler<*, *>>) : QueryBusMiddleware {
private val handlers = HashMap<String, QueryHandler<*, *>>()
init {
handlers.forEach { handler -> this.handlers[handler.listenTo()] = handler }
}
override fun <R, Q : Query<R>> dispatch(query: Q): R {
val queryClass = query::class.toString()
val handler = handlers[queryClass] ?: throw Exception("No handler listen to the query: $queryClass")
return handler::class.members.find { it.name == "handle" }!!.call(handler, query) as R
}
}

Delegate property to another property

Can you delegate a property to another property in Kotlin? I have the following code:
class SettingsPage {
lateinit var tagCharacters: JTextField
lateinit var tagForegroundColorChooser: ColorPanel
lateinit var tagBackgroundColorChooser: ColorPanel
var allowedChars: String
get() = tagCharacters.text
set(value) = tagCharacters.setText(value)
var tagForegroundColor by tagForegroundColorChooser
var tagBackgroundColor by tagBackgroundColorChooser
}
In order to get property delegation, I declare the following two extension functions:
operator fun ColorPanel.getValue(a: SettingsPage, p: KProperty<*>) = selectedColor
operator fun ColorPanel.setValue(a: SettingsPage, p: KProperty<*>, c: Color?) { selectedColor = c }
However, what I would like to write is something like the following:
class SettingsPage {
lateinit var tagCharacters: JTextField
lateinit var tagForegroundColorChooser: ColorPanel
lateinit var tagBackgroundColorChooser: ColorPanel
var allowedChars: String by Alias(tagCharacters.text)
var tagForegroundColor by Alias(tagForegroundColorChooser.selectedColor)
var tagBackgroundColor by Alias(tagBackgroundColorChooser.selectedColor)
}
Is this possible to do Kotlin? How do I write the class Alias?
UPD: Since Kotlin 1.4, the standard library includes the necessary extensions that allow this out of the box:
class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) {
var delegatedToMember: Int by this::memberInt
var delegatedToTopLevel: Int by ::topLevelInt
val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
}
var MyClass.extDelegated: Int by ::topLevelInt
Yes, it's possible: you can use a bound callable reference for a property that you store in the alias, and then the Alias implementation will look like this:
class Alias<T>(val delegate: KMutableProperty0<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T =
delegate.get()
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
delegate.set(value)
}
}
And the usage:
class Container(var x: Int)
class Foo {
var container = Container(1)
var x by Alias(container::x)
}
To reference a property of the same instance, use this::someProperty.
Follow-up, if you would like to see support for property references in Kotlin, please vote and track this issue for updates: https://youtrack.jetbrains.com/issue/KT-8658