David W
10/11/2018, 3:36 PMactor
? Basically I have a redux-ish pipeline where each input may result in 0+ state changes. Not sure how to tell when my pipeline is "inactive".David W
10/11/2018, 3:39 PMmap
is a flatMap
implemented by different subclasses (reducer) and anything can happen inside it.Jonathan
10/11/2018, 3:44 PMJonathan
10/11/2018, 3:45 PMDavid W
10/11/2018, 3:45 PMconsumeEach
)Jonathan
10/11/2018, 3:48 PMdata class State(val count: Int)
sealed class Action {
data class Add(val increment: Int = 1) : Action()
}
fun CoroutineScope.stateStore(states: SendChannel<State>): SendChannel<Action> =
actor {
var state = State(0)
states.send(state)
for (action in channel) {
state = when (action) {
is Action.Add -> state.copy(count = state.count + action.increment)
}
states.send(state)
}
}
Jonathan
10/11/2018, 3:49 PMfun test() = runBlocking {
val result = Channel<State>()
val actor = stateStore(result)
assertEquals(0, result.receive().count)
actor.send(Action.Add(42))
assertEquals(42, result.receive().count)
actor.close()
}
Jonathan
10/11/2018, 3:50 PMJonathan
10/11/2018, 3:52 PMDavid W
10/11/2018, 3:54 PMReceiveChannel
that handles the state changes, in real life send the state changes to the StateStore, for tests, receive and examine the new states in the test?Jonathan
10/11/2018, 3:55 PMDavid W
10/11/2018, 3:55 PMDavid W
10/11/2018, 3:56 PMJonathan
10/11/2018, 3:57 PMJonathan
10/11/2018, 3:57 PMDavid W
10/11/2018, 3:57 PMscan
operator (instead of directly reading from the StateStore), but it's still intrusiveJonathan
10/11/2018, 3:58 PMdata class AddAction(val numberToAdd: Long)
data class State(val number: Long)
class ClassToTest(val reducer: (AddAction) -> State) {
private val broadcast = ConflatedBroadcastChannel<State>(State(0))
val pipeline = GlobalScope.actor<AddAction> {
consumeEach { action ->
broadcast.send(reducer(broadcast.value, action))
}
}
fun openStateSubscription(): ReceiveChannel<State> = broadcast.openSubscription()
}
fun main(args: Array<String>) = runBlocking {
val testedClass = ClassToTest(reducer)
val sub = testedClass.openStateSubscription()
assertThat(sub.receive().number).isEqualTo(0)
testedClass.pipeline.send(ClassToTest.AddAction(Long.MAX_VALUE))
assertThat(sub.receive().number).isNotEqualTo(0)
}
David W
10/11/2018, 4:00 PMJonathan
10/11/2018, 4:00 PMDavid W
10/11/2018, 4:00 PMasPublisher()
but it might be a similar ideaDavid W
10/11/2018, 6:25 PMDavid W
10/11/2018, 6:36 PMLiveData
(an Android-specific analogue to a Channel)David W
10/11/2018, 6:36 PMobject
David W
10/11/2018, 6:41 PMDavid W
10/11/2018, 6:42 PMDavid W
10/11/2018, 6:43 PMpipeline.isEmpty
or pipeline.isActive
where it can tell me if there's anything going onDavid W
10/11/2018, 6:44 PMDavid W
10/11/2018, 8:03 PMDavid W
10/11/2018, 8:03 PMclass ClassToTest {
data class AddAction(val numberToAdd: Long)
data class State(val number: Long)
object Store {
var state: State = State(0)
set(value) {
field = value
broadcaster.offer(field)
}
private val broadcaster = BroadcastChannel<State>(1)
fun openStateSubscription() = broadcaster.openSubscription()
}
val pipeline = GlobalScope.actor<AddAction>(context = Dispatchers.Unconfined) {
channel
.flatMap(context = coroutineContext) { action ->
produce {
repeat(10) {
delay(100)
send(action.numberToAdd)
}
}
}
.map {
// Please ignore the horrible threading issues that could arise from this
numberToAdd ->
Store.state.copy(number = Store.state.number + numberToAdd)
}
.consumeEach {
Store.state = it
}
}
}
fun main(args: Array<String>) {
val testedClass = ClassToTest()
testedClass.pipeline.sendBlocking(ClassToTest.AddAction(1))
runBlocking {
val subscription = ClassToTest.Store.openStateSubscription()
assertThat(subscription.receive().number).isNotEqualTo(0)
subscription.receive()
assertThat(ClassToTest.Store.state.number).isEqualTo(2)
delay(200)
assertThat(ClassToTest.Store.state.number).isEqualTo(3)
null
}
}
David W
10/11/2018, 8:05 PMLiveData
in my Store
got a value, but there's no built-in way to block until you can receive
like with CoroutinesDavid W
10/11/2018, 8:06 PMJonathan
10/12/2018, 11:48 AMJonathan
10/12/2018, 11:48 AMJonathan
10/12/2018, 11:52 AMDavid W
10/12/2018, 1:24 PMscan
, except that I just realized that's actually potentially a huge problemDavid W
10/12/2018, 1:24 PMDavid W
10/12/2018, 1:25 PMscan
, each thinking that they're using the latest version of State but never actually "refreshing" their state from the storeDavid W
10/12/2018, 1:26 PMDavid W
10/12/2018, 2:15 PM