Skip to content

Commit 458ca6e

Browse files
committed
migrate interceptors to immutable lists and enhance id strategy
1 parent ebfdb97 commit 458ca6e

File tree

7 files changed

+74
-44
lines changed

7 files changed

+74
-44
lines changed

compiler-plugin/src/main/kotlin/com/intuit/hooks/plugin/CodeGeneration.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ internal data class HookCodeGen(
1313
private val zeroArity: Boolean
1414
) {
1515
val tapMethod get() = if (!zeroArity) """
16-
fun tap(name: String, f: ($hookSignature)): String = tap(name, randomId(), f)
17-
fun tap(name: String, id: String, f: ($hookSignature)): String = super.tap(name, id) { _: HookContext, $paramsWithTypes -> f($paramsWithoutTypes) }
16+
fun tap(name: String, f: ($hookSignature)): String? = tap(name, generateRandomId(), f)
17+
fun tap(name: String, id: String, f: ($hookSignature)): String? = super.tap(name, id) { _: HookContext, $paramsWithTypes -> f($paramsWithoutTypes) }
1818
""".trimIndent() else ""
1919
val paramsWithTypes get() = params.joinToString(", ") { it.withType }
2020
val paramsWithoutTypes get() = params.joinToString(", ") { it.withoutType }

docs/src/test/kotlin/example/snippets.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ fun typed() {
1919
fun call(value: Int) =
2020
super.call { f, context -> f(context, value) }
2121

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

2626
val myHook = MyHook()

hooks/api/hooks.api

+5-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public final class com/intuit/hooks/BailResult$Continue : com/intuit/hooks/BailR
5151

5252
public abstract class com/intuit/hooks/BaseHook {
5353
public fun <init> (Ljava/lang/String;)V
54+
protected fun generateRandomId ()Ljava/lang/String;
5455
protected fun getInterceptors ()Lcom/intuit/hooks/Interceptors;
5556
protected final fun getTaps ()Ljava/util/List;
5657
public final fun interceptCall (Lkotlin/Function;)V
@@ -61,12 +62,11 @@ public abstract class com/intuit/hooks/BaseHook {
6162
public final fun untap (Ljava/lang/String;)V
6263
}
6364

64-
public final class com/intuit/hooks/BaseHookKt {
65-
public static final fun randomId ()Ljava/lang/String;
66-
}
67-
6865
public class com/intuit/hooks/Interceptors {
6966
public fun <init> ()V
67+
public final fun addCallInterceptor (Lkotlin/Function;)V
68+
public final fun addRegisterInterceptor (Lkotlin/jvm/functions/Function1;)V
69+
public final fun addTapInterceptor (Lkotlin/jvm/functions/Function2;)V
7070
public final fun getCall ()Ljava/util/List;
7171
public final fun getRegister ()Ljava/util/List;
7272
public final fun getTap ()Ljava/util/List;
@@ -76,6 +76,7 @@ public class com/intuit/hooks/Interceptors {
7676

7777
public final class com/intuit/hooks/LoopInterceptors : com/intuit/hooks/Interceptors {
7878
public fun <init> ()V
79+
public final fun addLoopInterceptor (Lkotlin/Function;)V
7980
public final fun getLoop ()Ljava/util/List;
8081
}
8182

hooks/src/main/kotlin/com/intuit/hooks/AsyncSeriesLoopHook.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ public abstract class AsyncSeriesLoopHook<F : Function<LoopResult>, FInterceptor
2323
}
2424

2525
public fun interceptLoop(f: FInterceptor) {
26-
interceptors.loop.add(f)
26+
interceptors.addLoopInterceptor(f)
2727
}
2828
}

hooks/src/main/kotlin/com/intuit/hooks/BaseHook.kt

+56-31
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,31 @@ import kotlin.collections.HashMap
66
public typealias HookContext = HashMap<String, Any>
77

88
public open class Interceptors<F : Function<*>> {
9-
// TODO: I don't love that I've really only made [taps] immutable.. this screams inconsistency
10-
public val register: MutableList<(TapInfo<F>) -> TapInfo<F>?> = mutableListOf()
11-
public val tap: MutableList<(HookContext, TapInfo<F>) -> Unit> = mutableListOf()
12-
public val call: MutableList<F> = mutableListOf()
13-
14-
public fun invokeRegisterInterceptors(info: TapInfo<F>?): TapInfo<F>? =
15-
register.fold(info) { acc, interceptor ->
16-
acc?.let(interceptor)
17-
}
9+
public var register: List<(TapInfo<F>) -> TapInfo<F>?> = emptyList(); private set
10+
public var tap: List<(HookContext, TapInfo<F>) -> Unit> = emptyList(); private set
11+
public var call: List<F> = emptyList(); private set
12+
13+
public fun addRegisterInterceptor(interceptor: (TapInfo<F>) -> TapInfo<F>?) {
14+
register = register + interceptor
15+
}
16+
17+
public fun invokeRegisterInterceptors(info: TapInfo<F>?): TapInfo<F>? = register.fold(info) { acc, interceptor ->
18+
acc?.let(interceptor)
19+
}
1820

19-
public fun invokeTapInterceptors(taps: List<TapInfo<F>>, context: HookContext): Unit =
20-
tap.forEach { interceptor ->
21-
taps.forEach { tap ->
22-
interceptor.invoke(context, tap)
23-
}
21+
public fun addTapInterceptor(interceptor: (HookContext, TapInfo<F>) -> Unit) {
22+
tap = tap + interceptor
23+
}
24+
25+
public fun invokeTapInterceptors(taps: List<TapInfo<F>>, context: HookContext): Unit = tap.forEach { interceptor ->
26+
taps.forEach { tap ->
27+
interceptor.invoke(context, tap)
2428
}
29+
}
30+
31+
public fun addCallInterceptor(interceptor: F) {
32+
call = call + interceptor
33+
}
2534
}
2635

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

68-
public fun tap(name: String, f: F): String = tap(name, randomId(), f)
69-
70-
public fun tap(name: String, id: String, f: F): String {
71-
val filtered = taps.filter {
72-
it.id != id
73-
}
74-
75-
taps = TapInfo(name, id, type, f).let(interceptors::invokeRegisterInterceptors)?.let {
76-
filtered + it
77-
} ?: filtered
78-
79-
return id
77+
/**
78+
* Tap the hook with [f].
79+
*
80+
* @param name human-readable identifier to make debugging easier
81+
*
82+
* @return an auto generated identifier token that can be used to [untap], null if tap was
83+
* rejected by any of the register interceptors.
84+
*/
85+
public fun tap(name: String, f: F): String? = tap(name, generateRandomId(), f)
86+
87+
/**
88+
* Tap the hook with [f].
89+
*
90+
* @param name human-readable identifier to make debugging easier
91+
* @param id identifier token to register the [f] callback with. If another tap exists with
92+
* the same [id], it will be overridden, which essentially shortcuts an [untap] call.
93+
*
94+
* @return identifier token that can be used to [untap], null if tap was rejected by any of the
95+
* register interceptors.
96+
*/
97+
public fun tap(name: String, id: String, f: F): String? {
98+
untap(id)
99+
100+
return TapInfo(name, id, type, f).let(interceptors::invokeRegisterInterceptors)?.also {
101+
taps = taps + it
102+
}?.id
80103
}
81104

105+
/** Remove tapped callback associated with the [id] returned from [tap] */
82106
public fun untap(id: String) {
83107
taps = taps.filter {
84108
it.id != id
85109
}
86110
}
87111

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

92116
public fun interceptCall(f: F) {
93-
interceptors.call.add(f)
117+
interceptors.addCallInterceptor(f)
94118
}
95119

96120
public fun interceptRegister(f: (TapInfo<F>) -> TapInfo<F>?) {
97-
interceptors.register.add(f)
121+
interceptors.addRegisterInterceptor(f)
98122
}
99-
}
100123

101-
public fun randomId(): String = UUID.randomUUID().toString()
124+
/** Method to generate a random identifier for managing `TapInfo`s */
125+
protected open fun generateRandomId(): String = UUID.randomUUID().toString()
126+
}

hooks/src/main/kotlin/com/intuit/hooks/SyncLoopHook.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ public enum class LoopResult {
99
}
1010

1111
public class LoopInterceptors<F : Function<*>, FInterceptor : Function<*>> : Interceptors<F>() {
12-
public val loop: MutableList<FInterceptor> = mutableListOf()
12+
public var loop: List<FInterceptor> = emptyList(); private set
13+
14+
public fun addLoopInterceptor(f: FInterceptor) {
15+
loop = loop + f
16+
}
1317
}
1418

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

3741
public fun interceptLoop(f: FInterceptor) {
38-
interceptors.loop.add(f)
42+
interceptors.addLoopInterceptor(f)
3943
}
4044
}

hooks/src/test/kotlin/com/intuit/hooks/SyncHookTests.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class SyncHookTests {
7777
}
7878
val tap2 = hook.tap("second") {
7979
output.add(2)
80-
}
80+
}!!
8181
hook.tap("third") {
8282
output.add(3)
8383
}
@@ -99,7 +99,7 @@ class SyncHookTests {
9999
}
100100
val tap2 = hook.tap("second") {
101101
output.add(2)
102-
}
102+
}!!
103103
hook.tap("third") {
104104
output.add(3)
105105
}

0 commit comments

Comments
 (0)