https://two-wrongs.com/software-design-tree-and-program-families.html ↩︎
// Too much knowledge
fun <T> getFlagValue(
name: String,
deserialize: (JSON) -> T
): T
class Flag<T>(
val name: String,
internal val deserialize: (JSON) -> T,
)
// Juuuuuust right
fun <T> getFlagValue(
flag: Flag<T>
): T
class Flag<T>(
val name: String,
internal val deserialize: (JSON) -> T,
)
val myFlag = Flag(
name = "Jamie Sanson",
deserialize = ...
)
class Flag<T>(
val name: Name,
internal val deserialize: (JSON) -> T,
) {
value class Name(val value: String)
}
class Flag<T>(
val name: Name,
internal val deserialize: (JSON) -> T,
) {
value class Name(val value: String)
}
data class Flag<T>(
val name: Name,
+ val description: String?,
internal val deserialize: (JSON) -> T,
) {
+ constructor(
+ name: Name,
+ deserialize: (JSON) -> T
+ ) : this(name, null, deserialize)
}
// Before change ✅
val (name, deserialize: (JSON) -> Int) = flag
// After change 💥
val (name, deserialize: (JSON) -> Int) = flag
^ 'component2()' function returns
'String?', but '(JSON) -> Int' is expected
@Poko class Flag<T>(
val name: Name,
val description: String?,
internal val deserialize: (JSON) -> T,
)
whensealed interface FlagType {
data object Feature: FlagType
@Poko class Experiment(
val id: String
): FlagType
}
whenwhen (val type = flag.type) {
is Feature ->
doSomething()
is Experiment ->
experiment(type.id)
}
when sealed interface FlagType {
data object Feature: FlagType
+ data object Rollout: FlagType
@Poko class Experiment(
val id: String
): FlagType
}
class User internal constructor(
internal val type: Type,
) {
constructor(id: ID) : this(
type = Type.Identified(id = id),
)
companion object {
val Guest: User get() = User(Type.Guest)
value class ID(val id: String)
internal sealed interface Type {
data object Guest : Type
value class Identified(val id: ID) : Type
}
}
https://square.github.io/okhttp/features/interceptors/ ↩︎
Returned JSON content is always prepended with a while(1){} clause to mitigate abuse.
class RemoveBodyPrefixInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
if (response.body == null) return response
val newBody =
response.body
?.string()
?.removePrefix("while (1) {}\n")
?.toResponseBody()
return response.newBuilder()
.body(newBody)
.build()
}
}
@Poko class Flag<T>(
val name: Name,
val description: Description,
internal val defaultValue: () -> T,
internal val deserialize: (JSON) -> T,
)
typealias FlagProvider = (Flag.Name) -> JSON?
fun vetoProvider(
delegate: FlagProvider
) = { name ->
// Get the value
val result = delegate(name)
// Throw it out
null
}
fun FlagProvider(
name: String,
resolve: (Flag.Name) -> JSON?
): FlagProvider
class FlagProvider internal constructor(
internal val resolve: (Flag.Name) -> Output,
)
internal data class Output(
internal val outcome: Outcome,
internal val effect: Effect,
)
fun FlagProvider(vararg providers: FlagProvider): FlagProvider =
FlagProvider composite@{ flagName ->
for (provider in providers) {
val output = provider.resolve(flagName)
if (output.outcome is Outcome.Found) {
return@composite output
}
}
FlagProvider.Output(
outcome = Outcome.NotFound(flagName),
effect = Effect.Empty,
)
}
class FlagProvider(...) {
fun <Value> valueOf(flag: Flag<Value>): Value
}
val flagProvider: StateFlow<FlagProvider>
// For use in Java 8 😢
fun FlagProvider(
name: String,
resolve: (String) -> String?
): FlagProvider
// Usage from Java 8 😕
FlagProvider flagProvider =
FlagProviderKt.FlagProvider(
"name",
flagName -> null
);
@file:JvmName("FlagProviders")
package com.marksandspencer.flagging
⋮
@JvmName("create")
fun FlagProvider(...): FlagProvider
// Usage from Java 8 🙂
FlagProvider flagProvider =
FlagProviders.create(
"name",
flagName -> null
);
@JvmSynthetic
fun FlagProvider(
name: String,
resolve: (Flag.Name) -> JSON?
): FlagProvider
@JvmName("create")
- fun FlagProvider(...): FlagProvider
+ internal fun FlagProvider(...): FlagProvider
@dev.drewhamilton.poko.Poko public final class Flag<Value> {
ctor public Flag(String name, String description, kotlin.jvm.functions.Function1<? super com.marksandspencer.flagging.JSON,? extends Value> decode, kotlin.jvm.functions.Function0<? extends Value> defaultValue);
method public String getDescription();
method public String getName();
property public final String description;
property public final String name;
field public static final com.marksandspencer.flagging.Flag.Companion Companion;
}
https://github.com/tylerbwong/metalava-gradle
https://jamie.sanson.dev/kotlin-first-libraries