In https://github.com/InsertKoinIO/koin/blob/master/koin-projects/docs/reference/koin-android/scope.md#sharing-instances-between-components-with-scopes it is shown the below example
module {
// Shared user session data
scope(named("session")) {
scoped { UserSession() }
}
// Inject UserSession instance from "session" Scope
factory { (scopeId : ScopeID) -> Presenter(getScope(scopeId).get())}
}
But I don't even know how to get presenter?
I try
val nameScope = getKoin().createScope("SomeName", named("session"))
val presenter = get<Presenter>(nameScope.id)
but it's not the correct. How to get my presenter?
After tracing the code, the way to do it is to use parameter to pass over the scopeId
For the above example, it will be
val nameScope = getKoin().createScope("SomeName", named("session"))
val presenter = get<Presenter>(parameters = { parametersOf(nameScope.id) )
If there's qualifier, we just need to send through them as well
One Example as below where we need a parameter of the lambda to send through scopeId and name of the qualifier. (the argument is self definable through the parameters of any type).
module {
scope(named("AScopeName")) {
scoped(qualifier = named("scopedName")) { Dependency() }
factory(qualifier = named("factoryName")) { Dependency() }
}
factory { (scopeId: ScopeID, name: String) ->
Environment(getScope(scopeId).get(qualifier = named(name)))
}
}
Then the calling is as simple as below
val nameScope = getKoin().createScope("SomeName", named("AScopeName"))
val environment = get<Environment>(parameters = { parametersOf(nameScope.id, "scopedName") })
Or we could also
val nameScope = getKoin().createScope("SomeName", named("AScopeName"))
val environment = get<Environment>(parameters = { parametersOf("SomeName", "scopedName") })
Related
Hey I am working in kotlin. I am working on tree data structure. I added the value in list and now I want to find that value and modified their property. But I am getting the error.
VariantNode, StrengthNode, ProductVariant
StrengthNode.kt
class StrengthNode : VariantNode() {
var pricePerUnit: String? = null
var defaultValue = AtomicBoolean(false)
}
ActivityViewModel.kt
class ActivityViewModel : ViewModel() {
var baseNode: VariantNode = VariantNode()
private val defaultValueId = "12643423243324"
init {
createGraph()
}
private fun createGraph() {
val tempHashMap: MutableMap<String, VariantNode> = mutableMapOf()
val sortedList = getSortedList()
sortedList.forEach { productVariant ->
productVariant.strength?.let { strength ->
if (tempHashMap.containsKey("strength_${strength.value}")) {
baseNode.children.contains(VariantNode(strength.value)) // getting error
return#let
}
val tempNode = StrengthNode().apply {
value = strength
pricePerUnit = productVariant.pricePerUnit?.value
if (productVariant.id == defaultValueId) {
defaultValue.compareAndSet(false, true)
}
}
baseNode.children.add(tempNode)
tempHashMap["strength_${strength.value}"] = tempNode
}
productVariant.quantity?.let { quantity ->
if (tempHashMap.containsKey("strength_${productVariant.strength?.value}_quantity_${quantity.value}")) {
return#let
}
val tempNode = QuantityNode().apply {
value = quantity
}
val parent =
tempHashMap["strength_${productVariant.strength?.value}"] ?: baseNode
parent.children.add(tempNode)
tempHashMap["strength_${productVariant.strength?.value}_quantity_${quantity.value}"] =
tempNode
}
productVariant.subscription?.let { subscription ->
val tempNode = SubscriptionNode().apply {
value = subscription
}
val parent =
tempHashMap["strength_${productVariant.strength?.value}_quantity_${productVariant.quantity?.value}"]
?: baseNode
parent.children.add(tempNode)
}
}
baseNode
}
}
I am getting error on this.
I want to find that node value and modified other property.
Your class VariantNode only has a single no-arg constructor, but you're trying to call it with arguments, hence the error
Too many arguments for public constructor VariantNode() defined in com.example.optionsview.VariantNode
Either you have to provide a constructor, that matches your call, e.g.
open class VariantNode(var value: ProductValue?) {
var children: MutableList<VariantNode> = arrayListOf()
}
or you need to adjust your code to use the no-arg constructor instead.
val node = VariantNode()
node.value = strength.value
baseNode.children.contains(node)
Note however, that your call to contains most likely will not work, because you do not provide a custom implementation for equals. This is provided by default, when using a data class.
If you just want to validate whether baseNode.children has any element, where value has the expected value, you can use any instead, e.g.:
baseNode.children.any { it.value == strength.value }
This is my method which used to populate a map, and there's other data will be added in the future.
object ConfigurationService {
fun populateCache(client: RedissonClient): RMap<String, String> {
val map = client.getMap("map")
map["k1"] = "v1"
map["k2"] = "v2"
....
return map
}
}
The problem is, each time when I calling this method in my main function, it will re-create the same map content again and again, is there a way to define it as a constant map object, not in a method.
This is what I did, is that correct?
class ConfigurationService(client: RedissonClient) {
val map = client.getMap("map")
fun populateCache(): RMap<String, String> {
map["k1"] = "v1"
map["k2"] = "v2"
....
return map
}
}
One option is to extract the constant part as a property on your object:
object ConfigurationService {
private val fixedMapContent = mapOf(
"k1" to "v1",
"k2" to "v2",
)
fun populateCache(client: RedissonClient): RMap<String, String> {
val map = client.getMap("map")
map.putAll(fixedMapContent)
return map
}
}
But I don't think that's what you're looking for.
If the client to use is always the same, another option is to inject that client into ConfigurationService (which will not be an object anymore, you'll need to inject it as well):
class ConfigurationService(private val client: RedissonClient) {
val cache by lazy {
val map = client.getMap("map")
map["k1"] = "v1"
map["k2"] = "v2"
....
map
}
}
If you want to control when the cache is initially populated (instead of lazily on first access), you can also do this:
class ConfigurationService(client: RedissonClient) {
val cache = client.getMap("map")
fun populateCache() {
cache["k1"] = "v1"
cache["k2"] = "v2"
....
}
}
I would like to create something similar to Ruby's ActiveRecord Scopes using Kotlin Exposed.
For example I would like to break the following query up so that the first part acts like a scope.
This query returns what I want.
val m1 = Measurement.wrapRows(Measurements.innerJoin(Runs).select {
((exists(Tags.select { Tags.run.eq(Runs.sequelId) and Tags.name.eq("region") and Tags.value.eq("default") })) or notExists(Tags.select {
Tags.run.eq(Runs.sequelId) and Tags.name.eq("region")
})) and Measurements.name.eq("someName")
I would like to use this part as a scope:
val q1 = Measurements.innerJoin(Runs).select {
((exists(Tags.select { Tags.run.eq(Runs.sequelId) and Tags.name.eq("region") and Tags.value.eq("default") })) or notExists(Tags.select {
Tags.run.eq(Runs.sequelId) and Tags.name.eq("region")
}))
}
and then be able to refine the query using the q1 "scope"
so something like this:
val q2 = q1.having { Measurements.name.eq("someName") } // which does not work
Ultimately I would like to push this down into either the Measurements object or the Measurement class so I can do something like this
Measurement.withDefaultRegion.where( Measurements.name.eq("someName")
I was able to get what I wanted by adding a couple of functions to the model's companion object.
The first one provides the "scope"
fun defaultRegion() :Op<Boolean> {
return Op.build {(exists(Tags.select { Tags.run.eq(Runs.sequelId) and Tags.name.eq("region") and Tags.value.eq("default") })) or notExists(Tags.select {
Tags.run.eq(Runs.sequelId) and Tags.name.eq("region")
})}
}
The second function does the query using the scope and any refinements passed in and returns a "collection" of objects.
fun withDefaultRegionAnd( refinedBy: (SqlExpressionBuilder.()->Op<Boolean>)) : SizedIterable<Measurement> {
return Measurement.wrapRows(Measurements.innerJoin(Runs).select(Measurement.defaultRegion() and SqlExpressionBuilder.refinedBy() ))
}
At the client level I can simply do this:
val measurements = Measurement.withDefaultRegionAnd { Measurements.name.eq("someName") }
Here are the nearly table object and entity classes:
object Measurements : IntIdTable("measurements") {
val sequelId = integer("id").primaryKey()
val run = reference("run_id", Runs)
// more properties
}
class Measurement(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Measurement>(Measurements) {
fun defaultRegion() :Op<Boolean> {
return Op.build {(exists(Tags.select { Tags.run.eq(Runs.sequelId) and Tags.name.eq("region") and Tags.value.eq("default") })) or notExists(Tags.select {
Tags.run.eq(Runs.sequelId) and Tags.name.eq("region")
})}
}
fun withDefaultRegionAnd( refinedBy: (SqlExpressionBuilder.()->Op<Boolean>)) : SizedIterable<Measurement> {
return Measurement.wrapRows(Measurements.innerJoin(Runs).select(Measurement.defaultRegion() and SqlExpressionBuilder.refinedBy() ))
}
}
var run by Run referencedOn Measurements.run
var name by Measurements.name
// more properties
}
I'm wondering if it's possible in Kotlin to deserialize (restore property values) of a declared object, without having to manually assign the properties or resorting to reflection. The following snippet further explains:
object Foo: Serializable {
var propOne: String = ""
// ...
fun persist() {
serialize(this)
// no problem with serialization
}
fun restore(bytes: ByteArray) {
val fooObj: Foo = deserialize(bytes) as Foo
// It seems Kotlin allows us to use singleton as type!
// obvioulsly either of the following is wrong:
// this = fooObj
// Foo = fooObj
// ... is there a way to 'recover' the singleton (object) other than
// manual assignment of properties (or reflection) ???
}
}
There is no way to reassign the global reference to a singleton with a new instance. At most you can write out the properties during serialization, and then on deserialization directly read the properties and mutate the state in the original object. It will require custom code for you to assign the properties into the object either by direct assignment or reflection. It would be better if you make your own singleton mechanism that holds an instance that you can swap out to be another instance that you deserialize.
have faced with the same issue, and want to share with you my solution:
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import java.io.File
import java.lang.reflect.Modifier
typealias ObjMap = HashMap<String, Any?>
fun <T : Any> T.getInstance() : Any? {
val target = if(this is Class<*>) this else javaClass
return target.getDeclaredField("INSTANCE")?.get(null)
}
class ObjectHelper {
companion object {
val mapper = ObjectMapper().apply {
enable(SerializationFeature.INDENT_OUTPUT)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
fun objectToMap(obj: Any): ObjMap {
var res = ObjMap()
val instance = obj.getInstance()
val o = instance ?: obj
o.javaClass.declaredFields.forEach {
if(it.name != "INSTANCE") {
it.isAccessible = true
val value = if(Modifier.isStatic(it.modifiers)) it.get(null) else it.get(o)
res[it.name] = value
}
}
o.javaClass.classes.forEach {
res[it.simpleName] = objectToMap(it)
}
return res
}
fun saveObject(path: String, obj: Any) {
mapper.writeValue(File(path), objectToMap(obj))
}
fun loadObject(path: String, obj: Any) {
val json = mapper.readValue<HashMap<*,*>>(File(path), HashMap::class.java) as ObjMap
loadObject(obj, json)
}
fun loadObject(obj: Any, props: ObjMap) {
val objectParam = mapper.writeValueAsString(props)
mapper.readValue(objectParam, obj::class.java)
obj.javaClass.classes.forEach {
val instance = it.getInstance()
val map = props[it.simpleName]
if(map != null && instance != null) {
loadObject(instance, map as ObjMap)
}
}
}
}
}
Usage example:
object TestObj {
var f1: String = "f1"
var f2: String = "f2"
object TestObj_2 {
var f1: String = "f1_1"
var f2: String = "f2_2"
}
}
TestObj.f1 = "aaa"
saveObject("out.json", TestObj)
TestObj.f1 = "bbb"
loadObject("out.json", TestObj)
println(TestObj.f1)
Result will be "aaa".
I have a class that writes a user to SharedPreferences every time it is set:
class UserManager #Inject constructor(
val prefs: SharedPreferences,
val jsonAdapter: JsonAdapter<User>
) {
companion object {
val USER = "user"
}
var user: User = User()
set(value) {
field = value
prefs.edit().putString(USER, jsonAdapter.toJson(user)).apply()
}
init {
val userString = prefs.getString(USER, null)
if (userString != null) {
user = jsonAdapter.fromJson(userString)
}
}
}
Problem: If the user is set in the init block, it calls the setter and writes the user that we just got from the shared prefs... to the shared prefs.
Question 1: How can I directly set the property from the init block?
Question 2: Why do I have to initialize the User when I define a custom setter, but can omit the initialization when the default setter is used?
You need to directily initiliaze the property with the correct value. You can do this using the run function from the stdlib:
class UserManager #Inject constructor(
val prefs: SharedPreferences,
val jsonAdapter: JsonAdapter<User>
) {
companion object {
val USER = "user"
}
var user: User = run {
val userString = prefs.getString(USER, null)
if (userString != null) {
jsonAdapter.fromJson(userString)
} else {
User()
}
}
set(value) {
field = value
prefs.edit().putString(USER, jsonAdapter.toJson(user)).apply()
}
}
Shorter syntax proposed by Ilya Ryzhenkov on the Kotlin Slack:
var user: User = prefs.getString(USER, null)?.let { jsonAdapter.fromJson(it) } ?: User()
set(value) {
field = value
prefs.edit().putString(USER, jsonAdapter.toJson(user)).apply()
}
I believe the best solution is to use the 'backing property' concept described here: https://kotlinlang.org/docs/reference/properties.html#backing-properties
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null)
_table = HashMap() // Type parameters are inferred
return _table ?: throw AssertionError("Set to null by another thread")
}
Then initialize the backing property in the constructor and do <backingproperty> = value instead of field = value as well as point the getter to the backing property.
Take a look at by map delegate, seems like this is the pattern you want:
class User(val map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
https://kotlinlang.org/docs/reference/delegated-properties.html#storing-properties-in-a-map