is there a way to build a generic setter? I'm look...
# getting-started
b
is there a way to build a generic setter? I'm looking for a typesafe, generic way to turn turn obj.property.propert2 = "test" into an Array of "obj.property.propert2" to "test" I thought I could get away by just having some sort of dynamic proxy that intercepts every call like this
Copy code
private class SetterProxy

@Suppress("UNCHECKED_CAST")
fun <D : Document> buildUpdate(block: D.() -> Unit): Record<String, Any?> {
    val setterProxy = SetterProxy() as D
    setterProxy.block()
    return setterProxy.toUpdates()
}
Solved this using Js Proxies
2
c
I'm not sure I understand what your goal is, but if you want to store fields as data, you can do:
Copy code
class Foo {
    private val data = HashMap<String, Any?>()

    val property2: String by data
    val property3: Int by data
}
Now, if you use
property2 = "foo"
, it writes that into the map
b
for that you need to know all existing properties beforehand, correct
c
Yes, since you create a
val
for each of them. That's required to be type-safe.
b
Copy code
val isProxy = Symbol("isProxy")


private class Handler(
    private val currentPath: String = "",
    private val updates: HashMap<String, Any?>,
) {
    private fun buildPath(p: String) = if (currentPath.isEmpty()) p else "$currentPath.$p"

    fun set(target: dynamic, p: PropertyKey, value: dynamic, receiver: Any) {
        if (value[isProxy] === true) throw IllegalArgumentException("You are assigning an attribute to a proxy. Did you mean to assign a value instead?")
        updates[buildPath("$p")] = value
    }

    fun get(target: Any, p: PropertyKey, receiver: Any): Any {
        if (p === isProxy) return true
        return Handler(buildPath("$p"), updates)
            .asProxy(jso())
    }

    fun asProxy(target: Any): Proxy<Any> {
        @Suppress("UNCHECKED_CAST")
        val binding = recordOf("set" to ::set, "get" to ::get) as ProxyHandler<Any>
        return Proxy(target, binding)
    }
}

/**
 * Allows you to assign partial updates in a typesafe manner, e.g.:
 * pf2eActor.typeSafeUpdate {
 *     name = "test",
 *     system.details.level.value = 3
 * }.await()
 *
 * will produce {'name': 'test', 'system.details.level.value': 3}
 *
 * Note that you *must not* assign a property to itself, e.g.
 * * pf2eActor.typeSafeUpdate {
 *  *     name = "test",
 *  *     system.details.level.value = system.details.level.value
 *  * }.await()
 */
@Suppress("UNCHECKED_CAST")
fun <D : Document> D.typeSafeUpdate(block: D.() -> Unit): Promise<D> {
    val result = HashMap<String, Any?>()
    val proxy = Handler(updates = result)
        .asProxy(this) as D
    proxy.block()
    return update(result.toRecord()) as Promise<D>
}
btw