Skip to content

Commit

Permalink
migrate interceptors to immutable lists and enhance id strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
sugarmanz committed Oct 27, 2021
1 parent ebfdb97 commit 458ca6e
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ internal data class HookCodeGen(
private val zeroArity: Boolean
) {
val tapMethod get() = if (!zeroArity) """
fun tap(name: String, f: ($hookSignature)): String = tap(name, randomId(), f)
fun tap(name: String, id: String, f: ($hookSignature)): String = super.tap(name, id) { _: HookContext, $paramsWithTypes -> f($paramsWithoutTypes) }
fun tap(name: String, f: ($hookSignature)): String? = tap(name, generateRandomId(), f)
fun tap(name: String, id: String, f: ($hookSignature)): String? = super.tap(name, id) { _: HookContext, $paramsWithTypes -> f($paramsWithoutTypes) }
""".trimIndent() else ""
val paramsWithTypes get() = params.joinToString(", ") { it.withType }
val paramsWithoutTypes get() = params.joinToString(", ") { it.withoutType }
Expand Down
4 changes: 2 additions & 2 deletions docs/src/test/kotlin/example/snippets.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ fun typed() {
fun call(value: Int) =
super.call { f, context -> f(context, value) }

fun tap(name: String, id: String, f: ((Int) -> Unit)) =
super.tap(name, id) { _, newValue -> f(newValue) }
fun tap(name: String, f: ((Int) -> Unit)) =
super.tap(name) { _, newValue -> f(newValue) }
}

val myHook = MyHook()
Expand Down
9 changes: 5 additions & 4 deletions hooks/api/hooks.api
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public final class com/intuit/hooks/BailResult$Continue : com/intuit/hooks/BailR

public abstract class com/intuit/hooks/BaseHook {
public fun <init> (Ljava/lang/String;)V
protected fun generateRandomId ()Ljava/lang/String;
protected fun getInterceptors ()Lcom/intuit/hooks/Interceptors;
protected final fun getTaps ()Ljava/util/List;
public final fun interceptCall (Lkotlin/Function;)V
Expand All @@ -61,12 +62,11 @@ public abstract class com/intuit/hooks/BaseHook {
public final fun untap (Ljava/lang/String;)V
}

public final class com/intuit/hooks/BaseHookKt {
public static final fun randomId ()Ljava/lang/String;
}

public class com/intuit/hooks/Interceptors {
public fun <init> ()V
public final fun addCallInterceptor (Lkotlin/Function;)V
public final fun addRegisterInterceptor (Lkotlin/jvm/functions/Function1;)V
public final fun addTapInterceptor (Lkotlin/jvm/functions/Function2;)V
public final fun getCall ()Ljava/util/List;
public final fun getRegister ()Ljava/util/List;
public final fun getTap ()Ljava/util/List;
Expand All @@ -76,6 +76,7 @@ public class com/intuit/hooks/Interceptors {

public final class com/intuit/hooks/LoopInterceptors : com/intuit/hooks/Interceptors {
public fun <init> ()V
public final fun addLoopInterceptor (Lkotlin/Function;)V
public final fun getLoop ()Ljava/util/List;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ public abstract class AsyncSeriesLoopHook<F : Function<LoopResult>, FInterceptor
}

public fun interceptLoop(f: FInterceptor) {
interceptors.loop.add(f)
interceptors.addLoopInterceptor(f)
}
}
87 changes: 56 additions & 31 deletions hooks/src/main/kotlin/com/intuit/hooks/BaseHook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,31 @@ import kotlin.collections.HashMap
public typealias HookContext = HashMap<String, Any>

public open class Interceptors<F : Function<*>> {
// TODO: I don't love that I've really only made [taps] immutable.. this screams inconsistency
public val register: MutableList<(TapInfo<F>) -> TapInfo<F>?> = mutableListOf()
public val tap: MutableList<(HookContext, TapInfo<F>) -> Unit> = mutableListOf()
public val call: MutableList<F> = mutableListOf()

public fun invokeRegisterInterceptors(info: TapInfo<F>?): TapInfo<F>? =
register.fold(info) { acc, interceptor ->
acc?.let(interceptor)
}
public var register: List<(TapInfo<F>) -> TapInfo<F>?> = emptyList(); private set
public var tap: List<(HookContext, TapInfo<F>) -> Unit> = emptyList(); private set
public var call: List<F> = emptyList(); private set

public fun addRegisterInterceptor(interceptor: (TapInfo<F>) -> TapInfo<F>?) {
register = register + interceptor
}

public fun invokeRegisterInterceptors(info: TapInfo<F>?): TapInfo<F>? = register.fold(info) { acc, interceptor ->
acc?.let(interceptor)
}

public fun invokeTapInterceptors(taps: List<TapInfo<F>>, context: HookContext): Unit =
tap.forEach { interceptor ->
taps.forEach { tap ->
interceptor.invoke(context, tap)
}
public fun addTapInterceptor(interceptor: (HookContext, TapInfo<F>) -> Unit) {
tap = tap + interceptor
}

public fun invokeTapInterceptors(taps: List<TapInfo<F>>, context: HookContext): Unit = tap.forEach { interceptor ->
taps.forEach { tap ->
interceptor.invoke(context, tap)
}
}

public fun addCallInterceptor(interceptor: F) {
call = call + interceptor
}
}

public class TapInfo<FWithContext : Function<*>> internal constructor(
Expand Down Expand Up @@ -65,37 +74,53 @@ public abstract class BaseHook<F : Function<*>>(private val type: String) {
protected var taps: List<TapInfo<F>> = emptyList(); private set
protected open val interceptors: Interceptors<F> = Interceptors()

public fun tap(name: String, f: F): String = tap(name, randomId(), f)

public fun tap(name: String, id: String, f: F): String {
val filtered = taps.filter {
it.id != id
}

taps = TapInfo(name, id, type, f).let(interceptors::invokeRegisterInterceptors)?.let {
filtered + it
} ?: filtered

return id
/**
* Tap the hook with [f].
*
* @param name human-readable identifier to make debugging easier
*
* @return an auto generated identifier token that can be used to [untap], null if tap was
* rejected by any of the register interceptors.
*/
public fun tap(name: String, f: F): String? = tap(name, generateRandomId(), f)

/**
* Tap the hook with [f].
*
* @param name human-readable identifier to make debugging easier
* @param id identifier token to register the [f] callback with. If another tap exists with
* the same [id], it will be overridden, which essentially shortcuts an [untap] call.
*
* @return identifier token that can be used to [untap], null if tap was rejected by any of the
* register interceptors.
*/
public fun tap(name: String, id: String, f: F): String? {
untap(id)

return TapInfo(name, id, type, f).let(interceptors::invokeRegisterInterceptors)?.also {
taps = taps + it
}?.id
}

/** Remove tapped callback associated with the [id] returned from [tap] */
public fun untap(id: String) {
taps = taps.filter {
it.id != id
}
}

public fun interceptTap(f: (context: HookContext, tapInfo: TapInfo<F>) -> Unit) {
interceptors.tap.add(f)
interceptors.addTapInterceptor(f)
}

public fun interceptCall(f: F) {
interceptors.call.add(f)
interceptors.addCallInterceptor(f)
}

public fun interceptRegister(f: (TapInfo<F>) -> TapInfo<F>?) {
interceptors.register.add(f)
interceptors.addRegisterInterceptor(f)
}
}

public fun randomId(): String = UUID.randomUUID().toString()
/** Method to generate a random identifier for managing `TapInfo`s */
protected open fun generateRandomId(): String = UUID.randomUUID().toString()
}
8 changes: 6 additions & 2 deletions hooks/src/main/kotlin/com/intuit/hooks/SyncLoopHook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ public enum class LoopResult {
}

public class LoopInterceptors<F : Function<*>, FInterceptor : Function<*>> : Interceptors<F>() {
public val loop: MutableList<FInterceptor> = mutableListOf()
public var loop: List<FInterceptor> = emptyList(); private set

public fun addLoopInterceptor(f: FInterceptor) {
loop = loop + f
}
}

public abstract class SyncLoopHook<F : Function<LoopResult>, FInterceptor : Function<*>> : SyncBaseHook<F>("SyncLoopHook") {
Expand All @@ -35,6 +39,6 @@ public abstract class SyncLoopHook<F : Function<LoopResult>, FInterceptor : Func
}

public fun interceptLoop(f: FInterceptor) {
interceptors.loop.add(f)
interceptors.addLoopInterceptor(f)
}
}
4 changes: 2 additions & 2 deletions hooks/src/test/kotlin/com/intuit/hooks/SyncHookTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class SyncHookTests {
}
val tap2 = hook.tap("second") {
output.add(2)
}
}!!
hook.tap("third") {
output.add(3)
}
Expand All @@ -99,7 +99,7 @@ class SyncHookTests {
}
val tap2 = hook.tap("second") {
output.add(2)
}
}!!
hook.tap("third") {
output.add(3)
}
Expand Down

0 comments on commit 458ca6e

Please sign in to comment.